diff --git a/ability_runtime.gni b/ability_runtime.gni index 5479a5e48e..a2e048023a 100644 --- a/ability_runtime.gni +++ b/ability_runtime.gni @@ -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 + } } diff --git a/agent_runtime_framework/interfaces/inner_api/IAgentManager.idl b/agent_runtime_framework/interfaces/inner_api/IAgentManager.idl index e79cca44c8..86fa32c0e9 100644 --- a/agent_runtime_framework/interfaces/inner_api/IAgentManager.idl +++ b/agent_runtime_framework/interfaces/inner_api/IAgentManager.idl @@ -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); diff --git a/agent_runtime_framework/interfaces/inner_api/src/agent_manager_client.cpp b/agent_runtime_framework/interfaces/inner_api/src/agent_manager_client.cpp index 2bcda76c08..0fc2b99083 100644 --- a/agent_runtime_framework/interfaces/inner_api/src/agent_manager_client.cpp +++ b/agent_runtime_framework/interfaces/inner_api/src/agent_manager_client.cpp @@ -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, diff --git a/agent_runtime_framework/services/agentmgr/include/agent_card_mgr.h b/agent_runtime_framework/services/agentmgr/include/agent_card_mgr.h index 49d5f3d01a..53297f83d2 100644 --- a/agent_runtime_framework/services/agentmgr/include/agent_card_mgr.h +++ b/agent_runtime_framework/services/agentmgr/include/agent_card_mgr.h @@ -49,6 +49,7 @@ public: private: OHOS::AppExecFwk::BundleMgrClient bundleMgrClient_; + mutable std::mutex cardDataMutex_; AgentCardMgr(); ~AgentCardMgr(); }; diff --git a/agent_runtime_framework/services/agentmgr/include/agent_manager_service.h b/agent_runtime_framework/services/agentmgr/include/agent_manager_service.h index ce56d8cb15..936ee3daa9 100644 --- a/agent_runtime_framework/services/agentmgr/include/agent_manager_service.h +++ b/agent_runtime_framework/services/agentmgr/include/agent_manager_service.h @@ -55,7 +55,7 @@ public: int32_t GetAllAgentCards(AgentCardsRawData &cards) override; - int32_t GetAgentCardsByBundleName(const std::string &bundleName, std::vector &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 &connection, int32_t &callerUid); /** @@ -153,6 +153,8 @@ private: bool ReleaseCallerConnectionCountLocked(const sptr &callerRemote); void ReleaseTrackedConnection(const sptr &connection); void ReleaseTrackedConnectionByRemoteLocked(const sptr &callerRemote); + void TransferLowCodeCallerLimitLocked(const std::shared_ptr &session, + const sptr &callerRemote); void HandleCallerConnectionDied(const wptr &remote); void HandleCallerConnectionDied(const sptr &remote); /** diff --git a/agent_runtime_framework/services/agentmgr/src/agent_card_mgr.cpp b/agent_runtime_framework/services/agentmgr/src/agent_card_mgr.cpp index 025fcc4991..4c07dbb6ff 100644 --- a/agent_runtime_framework/services/agentmgr/src/agent_card_mgr.cpp +++ b/agent_runtime_framework/services/agentmgr/src/agent_card_mgr.cpp @@ -158,6 +158,7 @@ int32_t AgentCardMgr::HandleBundleInstall(const std::string &bundleName, int32_t } std::vector storedEntries; + std::lock_guard 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 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 entries; + std::lock_guard 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 entries; + std::lock_guard 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 entries; + std::lock_guard 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 bundleCards; + std::lock_guard 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"); diff --git a/agent_runtime_framework/services/agentmgr/src/agent_manager_service.cpp b/agent_runtime_framework/services/agentmgr/src/agent_manager_service.cpp index dfe651049e..2b4799704c 100644 --- a/agent_runtime_framework/services/agentmgr/src/agent_manager_service.cpp +++ b/agent_runtime_framework/services/agentmgr/src/agent_manager_service.cpp @@ -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 &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 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 &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 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 sptrsecond.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 sptrsecond--; } +void AgentManagerService::TransferLowCodeCallerLimitLocked(const std::shared_ptr &session, + const sptr &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 &remote) { sptr serviceConnection = nullptr; @@ -976,6 +1006,8 @@ void AgentManagerService::HandleCallerConnectionDied(const sptr & 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) { diff --git a/bundle.json b/bundle.json index 5c38ccb541..cb8cbed440 100644 --- a/bundle.json +++ b/bundle.json @@ -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", diff --git a/cj_environment/test/unittest/cj_environment_test/BUILD.gn b/cj_environment/test/unittest/cj_environment_test/BUILD.gn index 774048f7ca..bc5c80db23 100644 --- a/cj_environment/test/unittest/cj_environment_test/BUILD.gn +++ b/cj_environment/test/unittest/cj_environment_test/BUILD.gn @@ -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", diff --git a/cli_tool_framework/etc/profile/aimgr.cfg b/cli_tool_framework/etc/profile/aimgr.cfg index b4faecf0be..1579236447 100644 --- a/cli_tool_framework/etc/profile/aimgr.cfg +++ b/cli_tool_framework/etc/profile/aimgr.cfg @@ -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" ] } ] diff --git a/cli_tool_framework/frameworks/BUILD.gn b/cli_tool_framework/frameworks/BUILD.gn index 3f8a63b3a4..41aa54ef14 100644 --- a/cli_tool_framework/frameworks/BUILD.gn +++ b/cli_tool_framework/frameworks/BUILD.gn @@ -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", ] } diff --git a/cli_tool_framework/frameworks/js/napi/cli_tool_manager/BUILD.gn b/cli_tool_framework/frameworks/js/napi/cli_tool_manager/BUILD.gn index 08ecb0d0d1..0203970f8e 100644 --- a/cli_tool_framework/frameworks/js/napi/cli_tool_manager/BUILD.gn +++ b/cli_tool_framework/frameworks/js/napi/cli_tool_manager/BUILD.gn @@ -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" } diff --git a/cli_tool_framework/frameworks/js/napi/cli_tool_manager/include/js_cli_manager_utils.h b/cli_tool_framework/frameworks/js/napi/cli_tool_manager/include/js_cli_manager_utils.h index 0be87768af..11d36b21d0 100644 --- a/cli_tool_framework/frameworks/js/napi/cli_tool_manager/include/js_cli_manager_utils.h +++ b/cli_tool_framework/frameworks/js/napi/cli_tool_manager/include/js_cli_manager_utils.h @@ -19,7 +19,6 @@ #include #include -#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. diff --git a/cli_tool_framework/frameworks/js/napi/cli_tool_manager/src/cli_tool_manager_module.cpp b/cli_tool_framework/frameworks/js/napi/cli_tool_manager/src/cli_tool_manager_module.cpp index 033380e506..063c0adde0 100644 --- a/cli_tool_framework/frameworks/js/napi/cli_tool_manager/src/cli_tool_manager_module.cpp +++ b/cli_tool_framework/frameworks/js/napi/cli_tool_manager/src/cli_tool_manager_module.cpp @@ -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); } diff --git a/cli_tool_framework/frameworks/js/napi/cli_tool_manager/src/js_cli_manager.cpp b/cli_tool_framework/frameworks/js/napi/cli_tool_manager/src/js_cli_manager.cpp index 6abc4d7368..23556c44ec 100644 --- a/cli_tool_framework/frameworks/js/napi/cli_tool_manager/src/js_cli_manager.cpp +++ b/cli_tool_framework/frameworks/js/napi/cli_tool_manager/src/js_cli_manager.cpp @@ -15,23 +15,21 @@ #include "js_cli_manager.h" -#include #include #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 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 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); diff --git a/cli_tool_framework/frameworks/js/napi/cli_tool_manager/src/js_cli_manager_utils.cpp b/cli_tool_framework/frameworks/js/napi/cli_tool_manager/src/js_cli_manager_utils.cpp index 391afaf07e..087e6f579f 100644 --- a/cli_tool_framework/frameworks/js/napi/cli_tool_manager/src/js_cli_manager_utils.cpp +++ b/cli_tool_framework/frameworks/js/napi/cli_tool_manager/src/js_cli_manager_utils.cpp @@ -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); diff --git a/cli_tool_framework/frameworks/js/napi/script_manager/BUILD.gn b/cli_tool_framework/frameworks/js/napi/script_manager/BUILD.gn new file mode 100644 index 0000000000..fc8313a871 --- /dev/null +++ b/cli_tool_framework/frameworks/js/napi/script_manager/BUILD.gn @@ -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" +} diff --git a/cli_tool_framework/frameworks/js/napi/script_manager/include/js_script_manager.h b/cli_tool_framework/frameworks/js/napi/script_manager/include/js_script_manager.h new file mode 100644 index 0000000000..28827f23d8 --- /dev/null +++ b/cli_tool_framework/frameworks/js/napi/script_manager/include/js_script_manager.h @@ -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 diff --git a/cli_tool_framework/frameworks/js/napi/script_manager/src/js_script_manager.cpp b/cli_tool_framework/frameworks/js/napi/script_manager/src/js_script_manager.cpp new file mode 100644 index 0000000000..d003f4c823 --- /dev/null +++ b/cli_tool_framework/frameworks/js/napi/script_manager/src/js_script_manager.cpp @@ -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 + +#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(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(static_cast(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 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(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 = std::make_unique(); + 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 diff --git a/cli_tool_framework/frameworks/js/napi/script_manager/src/script_manager_module.cpp b/cli_tool_framework/frameworks/js/napi/script_manager/src/script_manager_module.cpp new file mode 100644 index 0000000000..59ca4859a4 --- /dev/null +++ b/cli_tool_framework/frameworks/js/napi/script_manager/src/script_manager_module.cpp @@ -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); +} diff --git a/cli_tool_framework/frameworks/js/napi/skill_driver/BUILD.gn b/cli_tool_framework/frameworks/js/napi/skill_driver/BUILD.gn new file mode 100644 index 0000000000..9392e27253 --- /dev/null +++ b/cli_tool_framework/frameworks/js/napi/skill_driver/BUILD.gn @@ -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" +} diff --git a/cli_tool_framework/frameworks/js/napi/skill_driver/include/js_skill_driver.h b/cli_tool_framework/frameworks/js/napi/skill_driver/include/js_skill_driver.h new file mode 100644 index 0000000000..705b918713 --- /dev/null +++ b/cli_tool_framework/frameworks/js/napi/skill_driver/include/js_skill_driver.h @@ -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 diff --git a/cli_tool_framework/frameworks/js/napi/skill_driver/src/js_skill_driver.cpp b/cli_tool_framework/frameworks/js/napi/skill_driver/src/js_skill_driver.cpp new file mode 100644 index 0000000000..c87d3be7c4 --- /dev/null +++ b/cli_tool_framework/frameworks/js/napi/skill_driver/src/js_skill_driver.cpp @@ -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 +#include + +#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 &values, + AAFwk::WantParams ¶ms) +{ + auto arr = sptr(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 &values, + AAFwk::WantParams ¶ms) +{ + auto arr = sptr(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 &values, + AAFwk::WantParams ¶ms) +{ + auto arr = sptr(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 &values, + AAFwk::WantParams ¶ms) +{ + auto arr = sptr(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 values; + if (AppExecFwk::UnwrapArrayStringFromJS(env, val, values)) { + SetSkillArrayString(key, values, params); + } + break; + } + case napi_number: { + std::vector dblValues; + if (AppExecFwk::UnwrapArrayDoubleFromJS(env, val, dblValues)) { + SetSkillArrayDouble(key, dblValues, params); + } + break; + } + case napi_boolean: { + std::vector values; + if (AppExecFwk::UnwrapArrayBoolFromJS(env, val, values)) { + SetSkillArrayBool(key, values, params); + } + break; + } + case napi_bigint: { + std::vector 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(dblVal); + if (static_cast(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 ExtractSkillArgs(napi_env env, napi_value obj) +{ + auto skillArgs = std::make_shared(); + napi_value propertyNames = nullptr; + napi_get_property_names(env, obj, &propertyNames); + if (propertyNames == nullptr) { + return skillArgs; + } + const std::set 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 &skillArgs, + const sptr &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(static_cast(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(ERR_OK); + auto callback = sptr::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(deferred, + std::make_unique(std::move(execute)), + std::make_unique(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 = std::make_unique(); + 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 diff --git a/cli_tool_framework/frameworks/js/napi/skill_driver/src/skill_driver_module.cpp b/cli_tool_framework/frameworks/js/napi/skill_driver/src/skill_driver_module.cpp new file mode 100644 index 0000000000..53a643a571 --- /dev/null +++ b/cli_tool_framework/frameworks/js/napi/skill_driver/src/skill_driver_module.cpp @@ -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); +} diff --git a/cli_tool_framework/interfaces/cli_tool/BUILD.gn b/cli_tool_framework/interfaces/cli_tool/BUILD.gn index 5f9274fae2..a24a1b9d74 100644 --- a/cli_tool_framework/interfaces/cli_tool/BUILD.gn +++ b/cli_tool_framework/interfaces/cli_tool/BUILD.gn @@ -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", diff --git a/cli_tool_framework/interfaces/cli_tool/ICliToolCmd.idl b/cli_tool_framework/interfaces/cli_tool/ICliToolData.idl similarity index 100% rename from cli_tool_framework/interfaces/cli_tool/ICliToolCmd.idl rename to cli_tool_framework/interfaces/cli_tool/ICliToolData.idl diff --git a/cli_tool_framework/interfaces/cli_tool/ICliToolManager.idl b/cli_tool_framework/interfaces/cli_tool/ICliToolManager.idl index 388e051902..cd2fc90c83 100644 --- a/cli_tool_framework/interfaces/cli_tool/ICliToolManager.idl +++ b/cli_tool_framework/interfaces/cli_tool/ICliToolManager.idl @@ -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); diff --git a/cli_tool_framework/interfaces/cli_tool/ICliToolManagerScheduler.idl b/cli_tool_framework/interfaces/cli_tool/ICliToolManagerScheduler.idl index a8a707f07a..cbb3ffe3ee 100644 --- a/cli_tool_framework/interfaces/cli_tool/ICliToolManagerScheduler.idl +++ b/cli_tool_framework/interfaces/cli_tool/ICliToolManagerScheduler.idl @@ -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); diff --git a/cli_tool_framework/interfaces/cli_tool/include/arg_mapping.h b/cli_tool_framework/interfaces/cli_tool/include/arg_mapping.h deleted file mode 100644 index 73f01f97aa..0000000000 --- a/cli_tool_framework/interfaces/cli_tool/include/arg_mapping.h +++ /dev/null @@ -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 -#include -#include -#include - -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 \ No newline at end of file diff --git a/cli_tool_framework/interfaces/cli_tool/include/cli_error_code.h b/cli_tool_framework/interfaces/cli_tool/include/cli_error_code.h index b1c0825bce..8db155e0d7 100644 --- a/cli_tool_framework/interfaces/cli_tool/include/cli_error_code.h +++ b/cli_tool_framework/interfaces/cli_tool/include/cli_error_code.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 diff --git a/cli_tool_framework/interfaces/cli_tool/include/cli_session_info.h b/cli_tool_framework/interfaces/cli_tool/include/cli_session_info.h index 171f963251..cec242bc5d 100644 --- a/cli_tool_framework/interfaces/cli_tool/include/cli_session_info.h +++ b/cli_tool_framework/interfaces/cli_tool/include/cli_session_info.h @@ -32,7 +32,7 @@ public: std::string sessionId; std::string toolName; std::string status; // "running", "completed", "failed" - std::shared_ptr result = nullptr; // optional, only when status="completed" + std::shared_ptr 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 \ No newline at end of file diff --git a/cli_tool_framework/interfaces/cli_tool/include/exec_options.h b/cli_tool_framework/interfaces/cli_tool/include/exec_options.h index 2b304b9568..6f2f902c4a 100644 --- a/cli_tool_framework/interfaces/cli_tool/include/exec_options.h +++ b/cli_tool_framework/interfaces/cli_tool/include/exec_options.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); diff --git a/cli_tool_framework/interfaces/cli_tool/include/exec_result.h b/cli_tool_framework/interfaces/cli_tool/include/exec_result.h index 79f8f44503..7368a8e398 100644 --- a/cli_tool_framework/interfaces/cli_tool/include/exec_result.h +++ b/cli_tool_framework/interfaces/cli_tool/include/exec_result.h @@ -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 \ No newline at end of file diff --git a/cli_tool_framework/interfaces/cli_tool/include/exec_tool_callback_impl.h b/cli_tool_framework/interfaces/cli_tool/include/exec_tool_callback_impl.h deleted file mode 100644 index 1ba17be3e2..0000000000 --- a/cli_tool_framework/interfaces/cli_tool/include/exec_tool_callback_impl.h +++ /dev/null @@ -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 - -#include "cli_session_info.h" -#include "exec_tool_callback_stub.h" - -namespace OHOS { -namespace CliTool { -namespace { -using ExecToolResultTask = std::function; -} - -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 diff --git a/cli_tool_framework/interfaces/cli_tool/include/sub_command_info.h b/cli_tool_framework/interfaces/cli_tool/include/sub_command_info.h index 53d469d889..6830d52d4f 100644 --- a/cli_tool_framework/interfaces/cli_tool/include/sub_command_info.h +++ b/cli_tool_framework/interfaces/cli_tool/include/sub_command_info.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 #include #include @@ -36,7 +34,6 @@ public: std::vector requirePermissions; std::string inputSchema; // JSON string std::string outputSchema; // JSON string - std::shared_ptr argMapping; std::vector eventTypes; std::string eventSchemas; // JSON string diff --git a/cli_tool_framework/interfaces/cli_tool/include/tool_info.h b/cli_tool_framework/interfaces/cli_tool/include/tool_info.h index c3ff7e28f0..0a85f054b9 100644 --- a/cli_tool_framework/interfaces/cli_tool/include/tool_info.h +++ b/cli_tool_framework/interfaces/cli_tool/include/tool_info.h @@ -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 #include -#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 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 &tools, ToolsRawData &rawData); + static int32_t ToToolInfoVec(const ToolsRawData &rawData, std::vector &tools); + int32_t RawDataCpy(const void *readdata); + ~ToolsRawData(); }; /** @@ -75,10 +61,8 @@ public: std::vector requirePermissions; std::string inputSchema; // JSON string std::string outputSchema; // JSON string - std::shared_ptr argMapping; std::vector eventTypes; std::string eventSchemas; // JSON string (map of event type to schema) - int32_t timeout = 1800; bool hasSubCommand = false; std::map subcommands; diff --git a/cli_tool_framework/interfaces/cli_tool/src/arg_mapping.cpp b/cli_tool_framework/interfaces/cli_tool/src/arg_mapping.cpp deleted file mode 100644 index e61ee8465c..0000000000 --- a/cli_tool_framework/interfaces/cli_tool/src/arg_mapping.cpp +++ /dev/null @@ -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(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(); - - 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(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(argMapping.type); - if (typeValue < 0 || typeValue > static_cast(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 \ No newline at end of file diff --git a/cli_tool_framework/interfaces/cli_tool/src/cli_session_info.cpp b/cli_tool_framework/interfaces/cli_tool/src/cli_session_info.cpp index 4345517eb4..16348a433b 100644 --- a/cli_tool_framework/interfaces/cli_tool/src/cli_session_info.cpp +++ b/cli_tool_framework/interfaces/cli_tool/src/cli_session_info.cpp @@ -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; diff --git a/cli_tool_framework/interfaces/cli_tool/src/cli_tool_mgr_client.cpp b/cli_tool_framework/interfaces/cli_tool/src/cli_tool_mgr_client.cpp index a8678a22a6..ff604e006a 100644 --- a/cli_tool_framework/interfaces/cli_tool/src/cli_tool_mgr_client.cpp +++ b/cli_tool_framework/interfaces/cli_tool/src/cli_tool_mgr_client.cpp @@ -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 &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 CliToolMGRClient::GetCliToolMgrProxy() const auto &onClearProxyCallback = [](const wptr &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 &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); } } diff --git a/cli_tool_framework/interfaces/cli_tool/src/exec_options.cpp b/cli_tool_framework/interfaces/cli_tool/src/exec_options.cpp index 60e3c21b98..33b1812a9d 100644 --- a/cli_tool_framework/interfaces/cli_tool/src/exec_options.cpp +++ b/cli_tool_framework/interfaces/cli_tool/src/exec_options.cpp @@ -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; } diff --git a/cli_tool_framework/interfaces/cli_tool/src/sub_command_info.cpp b/cli_tool_framework/interfaces/cli_tool/src/sub_command_info.cpp index f1f3146231..b1d43e3051 100644 --- a/cli_tool_framework/interfaces/cli_tool/src/sub_command_info.cpp +++ b/cli_tool_framework/interfaces/cli_tool/src/sub_command_info.cpp @@ -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(); - 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); diff --git a/cli_tool_framework/interfaces/cli_tool/src/tool_info.cpp b/cli_tool_framework/interfaces/cli_tool/src/tool_info.cpp index 20878a7f87..70b9aed5a9 100644 --- a/cli_tool_framework/interfaces/cli_tool/src/tool_info.cpp +++ b/cli_tool_framework/interfaces/cli_tool/src/tool_info.cpp @@ -17,12 +17,18 @@ #include #include +#include #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 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(); - 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(); - if (!ArgMapping::ParseFromJson(json["argMapping"], *tool.argMapping)) { - TAG_LOGE(AAFwkTag::CLI_TOOL, "ParseFromJson failed: argMapping parse failed"); - tool.argMapping = nullptr; + std::vector 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(); + 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(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(data)); + data = nullptr; + } + data = newData; + isMalloc = true; + return ERR_OK; +} + +void ToolsRawData::FromToolInfoVec(const std::vector &tools, ToolsRawData &rawData) +{ + std::stringstream ss; + uint32_t count = tools.size(); + ss.write(reinterpret_cast(&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(&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 &tools) +{ + std::stringstream ss; + ss.write(reinterpret_cast(rawData.data), rawData.size); + ss.seekg(0, std::ios::beg); + uint32_t ssLength = static_cast(ss.str().length()); + uint32_t count = 0; + ss.read(reinterpret_cast(&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(&toolSize), sizeof(toolSize)); + if (toolSize > ssLength - static_cast(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 \ No newline at end of file diff --git a/cli_tool_framework/services/climgr/BUILD.gn b/cli_tool_framework/services/climgr/BUILD.gn index e095f26041..53bc926e22 100644 --- a/cli_tool_framework/services/climgr/BUILD.gn +++ b/cli_tool_framework/services/climgr/BUILD.gn @@ -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", ] diff --git a/cli_tool_framework/services/climgr/include/cli_tool_app_state_observer.h b/cli_tool_framework/services/climgr/include/cli_tool_app_state_observer.h new file mode 100644 index 0000000000..ef6205a587 --- /dev/null +++ b/cli_tool_framework/services/climgr/include/cli_tool_app_state_observer.h @@ -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 +#include + +#include "application_state_observer_stub.h" + +namespace OHOS { +namespace CliTool { + +class CliToolAppStateObserver : public AppExecFwk::ApplicationStateObserverStub { +public: + using ProcessDiedCallback = std::function; + + 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 diff --git a/cli_tool_framework/services/climgr/include/cli_tool_data_manager.h b/cli_tool_framework/services/climgr/include/cli_tool_data_manager.h index fcbbb850c4..322934b986 100644 --- a/cli_tool_framework/services/climgr/include/cli_tool_data_manager.h +++ b/cli_tool_framework/services/climgr/include/cli_tool_data_manager.h @@ -42,6 +42,13 @@ public: */ int32_t GetAllTools(std::vector &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 diff --git a/cli_tool_framework/services/climgr/include/cli_tool_manager_service.h b/cli_tool_framework/services/climgr/include/cli_tool_manager_service.h index f8f096e89a..9a0d0ebac8 100644 --- a/cli_tool_framework/services/climgr/include/cli_tool_manager_service.h +++ b/cli_tool_framework/services/climgr/include/cli_tool_manager_service.h @@ -16,8 +16,11 @@ #ifndef OHOS_ABILITY_RUNTIME_CLI_TOOL_MGR_SERVICE_H #define OHOS_ABILITY_RUNTIME_CLI_TOOL_MGR_SERVICE_H +#include #include +#include #include +#include #include #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 { + public CliToolManagerStub { DECLARE_SYSTEM_ABILITY(CliToolManagerService); + friend class SkillCallbackAdapter; + public: static sptr GetInstance(); virtual ~CliToolManagerService() = default; @@ -46,7 +58,7 @@ public: /** * @brief Query all available tools */ - int32_t GetAllToolInfos(std::vector &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& 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& counter_; + }; + enum class ServiceRunningState { STATE_NOT_START, STATE_RUNNING }; void Init(); + void DelayUnloadTask(); - std::shared_ptr CreateSessionRecord(const ExecToolParam ¶m); + std::shared_ptr CreateSessionRecord(const ExecToolParam ¶m, const std::string &eventId); void AddSessionRecord(const std::shared_ptr &record); std::shared_ptr GetSessionRecord(const std::string &sessionId); void RemoveSessionRecord(const std::string &sessionId); @@ -108,6 +139,23 @@ private: bool RegisterSessionWithMonitors(const std::shared_ptr &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 &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_ = nullptr; - /** - * @brief Execute single command permission query. - */ - int32_t DoQueryPermission(const Command &cmd, std::vector &permissions); - - /** - * @brief Query main command permissions. - */ - int32_t QueryMainCommandPermission(const std::string &toolName, std::vector &permissions); - - /** - * @brief Query subcommand permissions. - */ - int32_t QuerySubCommandPermission(const std::string &toolName, const std::string &subCommand, - std::vector &permissions); - - std::atomic activeSessionCount_ = 0; - std::mutex sessionsMutex_; + std::atomic interfaceCalledCount_ = 0; + ffrt::mutex sessionsMutex_; std::unordered_map> sessionRecords_; + std::unordered_map> 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 diff --git a/cli_tool_framework/services/climgr/include/event_dispatcher.h b/cli_tool_framework/services/climgr/include/event_dispatcher.h index dc4e846d34..2848963153 100644 --- a/cli_tool_framework/services/climgr/include/event_dispatcher.h +++ b/cli_tool_framework/services/climgr/include/event_dispatcher.h @@ -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 #include #include +#include #include #include #include diff --git a/cli_tool_framework/services/climgr/include/io_monitor.h b/cli_tool_framework/services/climgr/include/io_monitor.h index aa9da165ad..674a815f1a 100644 --- a/cli_tool_framework/services/climgr/include/io_monitor.h +++ b/cli_tool_framework/services/climgr/include/io_monitor.h @@ -17,7 +17,9 @@ #define OHOS_ABILITY_RUNTIME_IO_MONITOR_H #include +#include #include +#include #include #include #include @@ -57,8 +59,22 @@ private: bool isStdin = false; }; + struct PendingInput { + std::string message; + std::string eventId; + }; + + struct InputQueue { + std::deque 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 fdMap_; + std::unordered_map inputQueues_; OutputCallback outputCallback_; InputReplyCallback inputReplyCallback_; SessionClosedCallback sessionClosedCallback_; diff --git a/cli_tool_framework/services/climgr/include/permission_query_util.h b/cli_tool_framework/services/climgr/include/permission_query_util.h new file mode 100644 index 0000000000..2f1bb2def6 --- /dev/null +++ b/cli_tool_framework/services/climgr/include/permission_query_util.h @@ -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 +#include +#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 &cmds, + std::vector &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 &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 &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 &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 &permissions, + int32_t queryRet); +}; +} // namespace CliTool +} // namespace OHOS +#endif // OHOS_ABILITY_RUNTIME_PERMISSION_QUERY_UTIL_H diff --git a/cli_tool_framework/services/climgr/include/process_manager.h b/cli_tool_framework/services/climgr/include/process_manager.h index 9dd56de3bd..baff6cb188 100644 --- a/cli_tool_framework/services/climgr/include/process_manager.h +++ b/cli_tool_framework/services/climgr/include/process_manager.h @@ -40,7 +40,7 @@ public: int32_t CreateChildProcess(const ExecToolParam ¶m, const std::string &sandboxConfig, const ToolInfo &toolInfo, std::shared_ptr record) const; - bool TerminateProcess(pid_t pid, int signal = SIGTERM) const; + bool Killpg(pid_t pid) const; private: ProcessManager() = default; diff --git a/cli_tool_framework/services/climgr/include/session_record.h b/cli_tool_framework/services/climgr/include/session_record.h index 01d102a054..3e66588860 100644 --- a/cli_tool_framework/services/climgr/include/session_record.h +++ b/cli_tool_framework/services/climgr/include/session_record.h @@ -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); diff --git a/cli_tool_framework/services/climgr/include/tool_util.h b/cli_tool_framework/services/climgr/include/tool_util.h index 578ee71508..3b6072653f 100644 --- a/cli_tool_framework/services/climgr/include/tool_util.h +++ b/cli_tool_framework/services/climgr/include/tool_util.h @@ -24,6 +24,8 @@ #include #include +#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 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 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 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 &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 &value, const nlohmann::json &propertySchema, const std::string &key); - static bool ValidateObjectType(const sptr &value, - const nlohmann::json &propertySchema, const std::string &key); static bool ValidateArrayItems(sptr arrayObj, const nlohmann::json &itemsSchema, const std::string &key); static bool ValidateBasicType(const sptr &value, const std::string &expectedType); @@ -77,86 +80,22 @@ private: static bool IsIntegerType(const sptr &value); static bool IsNumberType(const sptr &value); static bool IsArrayType(const sptr &value); - static bool IsObjectType(const sptr &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 &value, const nlohmann::json ¶mConfig, - std::vector> &positionalParams); - static void ProcessFlattenedMode(const std::string &key, const sptr &value, - const nlohmann::json ¶mConfig, const AAFwk::WantParams &args, std::string &cmdLine); - static void ProcessArrayExpansion(const sptr &value, const std::string &tmpl, + static void ProcessBooleanParam(const std::string &key, const sptr &value, std::string &cmdLine); - static void ProcessJsonStringTemplate(const std::string &key, const sptr &value, - const nlohmann::json &templateValue, std::string &cmdLine); - static void ProcessBooleanTemplate(const std::string &key, const sptr &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 &value); - - // Nested path query helper for flattened mapping - static sptr QueryNestedValue(const AAFwk::WantParams &args, - const std::string &path, const std::string &separator); - - // Helper method for nested path traversal - static sptr QueryNestedPath(const AAFwk::WantParams &args, - const std::vector &pathSegments, const std::string &separator); - static sptr QueryNextLevel(const sptr ¤tValue, - const std::string &nextSegment, const std::string &separator); - - // Helper methods for path query (extracted to reduce QueryNestedValue length) - static sptr TryDirectLookup(const AAFwk::WantParams &args, - const std::string &path); - static sptr 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 &value, - const std::string &separator, const nlohmann::json &templateValue, const AAFwk::WantParams &args, + static void ProcessArrayExpansion(const std::string &key, const sptr &value, std::string &cmdLine); - // Core parameter processing logic (extracted for reuse) - static void ApplyFlagModeLogic(const sptr &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 &value); - static std::string GetParamJsonValue(const sptr &value); static bool GetParamBoolValue(const sptr &value, bool &result); - static bool GetParamArrayValue(const sptr &value, std::vector &result); - - // Low-level helper methods for code reuse - static bool ExtractWantParams(const sptr &value, AAFwk::WantParams &wantParams); - static std::string EscapeJsonString(const std::string &str); - static void IterateIArray(sptr arrayObj, - std::function&)> elementHandler); - static std::string BuildJsonArrayFromIArray(sptr arrayObj, - std::function&)> elementConverter); - - // Type-specific JSON conversion helpers (extracted to reduce GetParamJsonValue length) - static std::string ConvertWantParamsToJson(const sptr &value); - static std::string ConvertArrayToJson(const sptr &value); - static std::string ConvertStringToJson(const sptr &value); - static std::string ConvertBooleanToJson(const sptr &value); - static std::string ConvertNumericToJson(const sptr &value); }; } // namespace CliTool diff --git a/cli_tool_framework/services/climgr/src/cli_tool_app_state_observer.cpp b/cli_tool_framework/services/climgr/src/cli_tool_app_state_observer.cpp new file mode 100644 index 0000000000..40bcc1602e --- /dev/null +++ b/cli_tool_framework/services/climgr/src/cli_tool_app_state_observer.cpp @@ -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 diff --git a/cli_tool_framework/services/climgr/src/cli_tool_data_manager.cpp b/cli_tool_framework/services/climgr/src/cli_tool_data_manager.cpp index 6e5965b42b..ba3c5a501f 100644 --- a/cli_tool_framework/services/climgr/src/cli_tool_data_manager.cpp +++ b/cli_tool_framework/services/climgr/src/cli_tool_data_manager.cpp @@ -22,18 +22,16 @@ #include #include -#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 lock(kvStorePtrMutex_); if (kvStorePtr_ != nullptr) { dataManager_.CloseKvStore(APP_ID, kvStorePtr_); } @@ -296,6 +295,17 @@ int32_t CliToolDataManager::GetAllTools(std::vector &tools) return ERR_OK; } +int32_t CliToolDataManager::GetAllToolsRawData(ToolsRawData &rawData) +{ + std::vector 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; diff --git a/cli_tool_framework/services/climgr/src/cli_tool_manager_service.cpp b/cli_tool_framework/services/climgr/src/cli_tool_manager_service.cpp index 531f4cfe54..a5c2ba443f 100644 --- a/cli_tool_framework/services/climgr/src/cli_tool_manager_service.cpp +++ b/cli_tool_framework/services/climgr/src/cli_tool_manager_service.cpp @@ -17,15 +17,18 @@ #include +#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 adaptor_; } // namespace std::mutex g_mutex; @@ -61,6 +67,7 @@ sptr CliToolManagerService::GetInstance() int32_t CliToolManagerService::RegisterScheduler(const sptr &scheduler) { + InterfaceCallCounter counter(interfaceCalledCount_); if (EventDispatcher::GetInstance().RegisterScheduler(IPCSkeleton::GetCallingPid(), scheduler)) { return ERR_OK; } @@ -69,6 +76,7 @@ int32_t CliToolManagerService::RegisterScheduler(const sptrsessionType == 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 activePids; { - std::lock_guard lock(sessionsMutex_); + std::lock_guard 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 guard(sessionsMutex_); + sessionSize = static_cast(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 &record) { - std::lock_guard lock(sessionsMutex_); + std::lock_guard guard(sessionsMutex_); sessionRecords_[record->sessionId] = record; } std::shared_ptr CliToolManagerService::GetSessionRecord(const std::string &sessionId) { - std::lock_guard lock(sessionsMutex_); + std::lock_guard 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 lock(sessionsMutex_); + std::lock_guard guard(sessionsMutex_); sessionRecords_.erase(sessionId); } +void CliToolManagerService::DelayUnloadTask() +{ + auto task = []() { + int32_t sessionSize = 0; + { + std::lock_guard guard(CliToolManagerService::GetInstance()->sessionsMutex_); + sessionSize = static_cast(CliToolManagerService::GetInstance()->sessionRecords_.size()); + } + int32_t calledCount = CliToolManagerService::GetInstance()->interfaceCalledCount_.load(); + if (calledCount == 0 && sessionSize == 0) { + TAG_LOGI(AAFwkTag::CLI_TOOL, "UnloadSA start"); + sptr 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 &record, const ExecToolParam ¶m) { @@ -329,10 +400,10 @@ void CliToolManagerService::UnregisterSessionWithMonitors(const std::string &ses } } -int32_t CliToolManagerService::GetAllToolInfos(std::vector &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 &tools) return ERR_PERMISSION_DENIED; } - return CliToolDataManager::GetInstance().GetAllTools(tools); + return CliToolDataManager::GetInstance().GetAllToolsRawData(tools); } int32_t CliToolManagerService::GetAllToolSummaries(std::vector &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 &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 guard(sessionsMutex_); + if (static_cast(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 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 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 &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 record = nullptr; { - std::lock_guard lock(sessionsMutex_); - for (auto iter = sessionRecords_.begin(); iter != sessionRecords_.end(); ++iter) { - if (iter->second == nullptr || pid != iter->second->processId) { + std::lock_guard 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 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 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 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 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 CliToolManagerService::CreateSessionRecord(const ExecToolParam ¶m, + const std::string &eventId) { auto record = std::make_shared(); - 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 &cmds, +int32_t CliToolManagerService::BatchQueryPermissionBySubCommand( + const std::vector &cmds, std::vector &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(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 &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 &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(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::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 &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 diff --git a/cli_tool_framework/services/climgr/src/event_dispatcher.cpp b/cli_tool_framework/services/climgr/src/event_dispatcher.cpp index f0fcae074e..daa63a2100 100644 --- a/cli_tool_framework/services/climgr/src/event_dispatcher.cpp +++ b/cli_tool_framework/services/climgr/src/event_dispatcher.cpp @@ -16,6 +16,7 @@ #include "event_dispatcher.h" #include +#include #include "hilog_tag_wrapper.h" @@ -318,4 +319,4 @@ void EventDispatcher::RemoveSubscribersForPidLocked(int32_t callerPid) } } // namespace CliTool -} // namespace OHOS \ No newline at end of file +} // namespace OHOS diff --git a/cli_tool_framework/services/climgr/src/io_monitor.cpp b/cli_tool_framework/services/climgr/src/io_monitor.cpp index 0d6c2881c7..c4d8ac7630 100644 --- a/cli_tool_framework/services/climgr/src/io_monitor.cpp +++ b/cli_tool_framework/services/climgr/src/io_monitor.cpp @@ -15,11 +15,15 @@ #include "io_monitor.h" -#include +#include #include +#include +#include #include +#include #include #include +#include #include #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::Create() @@ -75,11 +81,27 @@ void IOMonitor::Stop() monitorThread_.join(); } - std::lock_guard lock(fdMutex_); - for (const auto &[fd, info] : fdMap_) { - close(fd); + std::vector> failedInputs; + { + std::lock_guard 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> fdsToClose; + std::vector failedInputs; { std::lock_guard 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 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::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(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 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 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 failedInputs; + { + std::lock_guard 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]; diff --git a/cli_tool_framework/services/climgr/src/permission_query_util.cpp b/cli_tool_framework/services/climgr/src/permission_query_util.cpp new file mode 100644 index 0000000000..437a043d5e --- /dev/null +++ b/cli_tool_framework/services/climgr/src/permission_query_util.cpp @@ -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 &cmds, + std::vector &cmdPermissions) +{ + cmdPermissions.clear(); + cmdPermissions.reserve(cmds.size()); + for (const auto &cmd : cmds) { + std::vector 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 &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 &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 &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 &permissions, + int32_t queryRet) +{ + CommandPermission cmdPerm; + cmdPerm.cmd = cmd; + cmdPerm.permissions = permissions; + cmdPerm.queryRet = queryRet; + return cmdPerm; +} +} // namespace CliTool +} // namespace OHOS diff --git a/cli_tool_framework/services/climgr/src/process_manager.cpp b/cli_tool_framework/services/climgr/src/process_manager.cpp index e49b69b1f6..6b9be65caf 100644 --- a/cli_tool_framework/services/climgr/src/process_manager.cpp +++ b/cli_tool_framework/services/climgr/src/process_manager.cpp @@ -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; diff --git a/cli_tool_framework/services/climgr/src/session_record.cpp b/cli_tool_framework/services/climgr/src/session_record.cpp index 57d2fe98c1..66e1def04f 100644 --- a/cli_tool_framework/services/climgr/src/session_record.cpp +++ b/cli_tool_framework/services/climgr/src/session_record.cpp @@ -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 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 lock(resultMutex_); + terminalStatus_ = resultCode; + stdoutText_ = outputText; + endTimeMs_ = std::chrono::duration_cast( + 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 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 SessionRecord::BuildExecResult() const { auto result = std::make_shared(); - if (result == nullptr) { - return nullptr; - } - std::lock_guard 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; } diff --git a/cli_tool_framework/services/climgr/src/tool_util.cpp b/cli_tool_framework/services/climgr/src/tool_util.cpp index 924f0f53d9..5308c54a1a 100644 --- a/cli_tool_framework/services/climgr/src/tool_util.cpp +++ b/cli_tool_framework/services/climgr/src/tool_util.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -32,14 +33,20 @@ #include "ipc_skeleton.h" #include "permission_util.h" #include "session_record.h" +#include "skill_execute_result.h" #include "tool_info.h" #include "want_params.h" #include "want_params_wrapper.h" +#include "bool_wrapper.h" +#include "int_wrapper.h" +#include "string_wrapper.h" namespace OHOS { namespace CliTool { namespace { constexpr int32_t MILLISECOND_COEFFICIENT = 1000; +constexpr int64_t MAX_TIMEOUT = 30 * 60; // 30 m +constexpr size_t PREFIX_DOUBLE_DASH_LEN = 2; } int32_t ToolUtil::ValidateProperties(const ToolInfo &toolInfo, ExecToolParam ¶m, AccessToken::AccessTokenID tokenId) @@ -47,13 +54,13 @@ int32_t ToolUtil::ValidateProperties(const ToolInfo &toolInfo, ExecToolParam &pa if (!param.subcommand.empty()) { if (!toolInfo.hasSubCommand) { TAG_LOGE(AAFwkTag::CLI_TOOL, "not have subcommand"); - return ERR_INVALID_PARAM; + return ERR_TOOL_NOT_EXIST; } auto search = toolInfo.subcommands.find(param.subcommand); if (search == toolInfo.subcommands.end()) { TAG_LOGE(AAFwkTag::CLI_TOOL, "not have subcommand"); - return ERR_INVALID_PARAM; + return ERR_TOOL_NOT_EXIST; } if (!PermissionUtil::VerifyAccessToken(tokenId, search->second.requirePermissions)) { return ERR_PERMISSION_DENIED; @@ -70,23 +77,27 @@ int32_t ToolUtil::ValidateProperties(const ToolInfo &toolInfo, ExecToolParam &pa } if (param.options.timeout == 0) { - param.options.timeout = toolInfo.timeout; - TAG_LOGI(AAFwkTag::CLI_TOOL, "use toolInfo timeout"); - } else if (param.options.timeout > toolInfo.timeout) { + param.options.timeout = MAX_TIMEOUT; + TAG_LOGI(AAFwkTag::CLI_TOOL, "use max timeout"); + } else if (param.options.timeout > MAX_TIMEOUT) { TAG_LOGE(AAFwkTag::CLI_TOOL, "Excessively large timeout"); return ERR_INVALID_PARAM; } - if (!param.options.background) { - if (param.options.yieldMs == 0) { - param.options.yieldMs = param.options.timeout * MILLISECOND_COEFFICIENT; - } else if (param.options.yieldMs > param.options.timeout * MILLISECOND_COEFFICIENT) { - TAG_LOGE(AAFwkTag::CLI_TOOL, "yieldTime exceeds timeout."); - return ERR_INVALID_PARAM; - } + if (!param.options.background && param.options.yieldMs > param.options.timeout * MILLISECOND_COEFFICIENT) { + TAG_LOGE(AAFwkTag::CLI_TOOL, "yieldTime exceeds timeout."); + return ERR_INVALID_PARAM; } - return ValidateInputSchemaProperties(toolInfo.inputSchema, param.args); + if (param.subcommand.empty()) { + return ValidateInputSchemaProperties(toolInfo.inputSchema, param.args); + } + + auto it = toolInfo.subcommands.find(param.subcommand); + if (it == toolInfo.subcommands.end()) { + TAG_LOGW(AAFwkTag::CLI_TOOL, "GetSubCommandInfo failed: subcommand=%{public}s", param.subcommand.c_str()); + } + return ValidateInputSchemaProperties(it->second.inputSchema, param.args); } int32_t ToolUtil::ValidateInputSchemaProperties(const std::string &inputSchema, @@ -113,6 +124,13 @@ int32_t ToolUtil::ValidateInputSchemaProperties(const std::string &inputSchema, } auto properties = schema["properties"]; for (auto &[key, value] : args.GetParams()) { + if (key == "help") { + if (args.Size() != 1) { + TAG_LOGE(AAFwkTag::CLI_TOOL, "args size > 1"); + return ERR_INVALID_PARAM; + } + return ERR_OK; + } if (!properties.contains(key)) { TAG_LOGE(AAFwkTag::CLI_TOOL, "args key '%{public}s' not found in properties", key.c_str()); return ERR_INVALID_PARAM; @@ -146,8 +164,8 @@ std::string ToolUtil::GenerateCliSessionId(const std::string &name, std::shared_ return name + "_" + std::to_string(time) + "_" + std::to_string(randomDigit); } -bool ToolUtil::GenerateSandboxConfig(const std::string &challenge, AccessToken::AccessTokenID tokenId, - std::string &sandboxConfig) +bool ToolUtil::GenerateSandboxConfig(const ExecToolParam ¶m, AccessToken::AccessTokenID tokenId, + std::string &sandboxConfig, std::string &bundleName) { AppExecFwk::BundleInfo bundleInfo; if (!ToolUtil::GetBundleInfoByTokenId(tokenId, bundleInfo)) { @@ -156,13 +174,17 @@ bool ToolUtil::GenerateSandboxConfig(const std::string &challenge, AccessToken:: nlohmann::json config; config["callerTokenId"] = IPCSkeleton::GetCallingFullTokenID(); - config["challenge"] = challenge; + config["challenge"] = param.challenge; config["uid"] = IPCSkeleton::GetCallingUid(); config["callerPid"] = IPCSkeleton::GetCallingPid(); config["gid"] = bundleInfo.gid; config["appId"] = bundleInfo.appId; + config["bundleName"] = bundleInfo.name; + config["cliName"] = param.toolName; + config["subCliName"] = param.subcommand; sandboxConfig = config.dump(); - TAG_LOGE(AAFwkTag::CLI_TOOL, "sandboxConfig: %{public}s", sandboxConfig.c_str()); + bundleName = bundleInfo.name; + TAG_LOGI(AAFwkTag::CLI_TOOL, "sandboxConfig: %{public}s", sandboxConfig.c_str()); return true; } @@ -207,271 +229,43 @@ void ToolUtil::TransferToCmdParam(const ToolInfo &toolInfo, const AAFwk::WantPar TAG_LOGI(AAFwkTag::CLI_TOOL, "Not has arg"); return; } - if (toolInfo.argMapping == nullptr) { - TAG_LOGW(AAFwkTag::CLI_TOOL, "argMapping is nullptr"); - return; - } - - // Apply mapping based on type - switch (toolInfo.argMapping->type) { - case ArgMappingType::FLAG: - ApplyFlagMapping(toolInfo.argMapping->templates, args, cmdLine); - break; - case ArgMappingType::POSITIONAL: - ApplyPositionalMapping(toolInfo.argMapping->order, args, cmdLine); - break; - case ArgMappingType::FLATTENED: - ApplyFlattenedMapping(toolInfo.argMapping->separator, toolInfo.argMapping->templates, args, cmdLine); - break; - case ArgMappingType::JSONSTRING: - ApplyJsonStringMapping(toolInfo.argMapping->templates, args, cmdLine); - break; - case ArgMappingType::MIXED: - ApplyMixedMapping(toolInfo.argMapping->templates, args, cmdLine); - break; - default: - TAG_LOGW(AAFwkTag::CLI_TOOL, "Unknown argMapping type"); - break; - } -} - -void ToolUtil::ApplyFlagMapping(const std::string &templates, const AAFwk::WantParams &args, std::string &cmdLine) -{ - if (templates.empty()) { - TAG_LOGW(AAFwkTag::CLI_TOOL, "Flag templates is empty"); - return; - } - - nlohmann::json templatesJson = nlohmann::json::parse(templates, nullptr, false); - if (templatesJson.is_discarded() || !templatesJson.is_object()) { - TAG_LOGE(AAFwkTag::CLI_TOOL, "Failed to parse templates JSON"); - return; - } - for (const auto &[key, value] : args.GetParams()) { - if (!templatesJson.contains(key)) { + if (value == nullptr) { continue; } - auto &templateValue = templatesJson[key]; - // Apply FLAG mode logic for this parameter - ApplyFlagModeLogic(value, templateValue, cmdLine); + if (key == "help") { + ProcessBooleanParam(key, value, cmdLine); + continue; + } + + if (IsBooleanType(value)) { + ProcessBooleanParam(key, value, cmdLine); + continue; + } + + if (IsArrayType(value)) { + ProcessArrayExpansion(key, value, cmdLine); + continue; + } + + std::string strValue = GetParamStringValue(value); + if (!strValue.empty()) { + cmdLine += " --" + key + " " + strValue; + } } } -void ToolUtil::ApplyPositionalMapping(const std::string &order, const AAFwk::WantParams &args, std::string &cmdLine) +void ToolUtil::ProcessBooleanParam(const std::string &key, const sptr &value, std::string &cmdLine) { - if (order.empty()) { - TAG_LOGW(AAFwkTag::CLI_TOOL, "Positional order is empty"); + if (!IsBooleanType(value)) { return; } - nlohmann::json orderJson = nlohmann::json::parse(order, nullptr, false); - if (orderJson.is_discarded() || !orderJson.is_array()) { - TAG_LOGE(AAFwkTag::CLI_TOOL, "Failed to parse order JSON"); - return; + bool boolValue = false; + if (GetParamBoolValue(value, boolValue) && boolValue) { + cmdLine += " --" + key; } - - for (const auto &key : orderJson) { - if (!key.is_string()) { - continue; - } - std::string keyStr = key.get(); - auto it = args.GetParams().find(keyStr); - if (it != args.GetParams().end()) { - std::string strValue = GetParamStringValue(it->second); - if (!strValue.empty()) { - cmdLine += " " + strValue; - } - } - } -} - -void ToolUtil::ApplyFlattenedMapping(const std::string &separator, const std::string &templates, - const AAFwk::WantParams &args, std::string &cmdLine) -{ - std::string sep = separator.empty() ? "." : separator; - - nlohmann::json templatesJson = nlohmann::json::parse(templates, nullptr, false); - if (templatesJson.is_discarded() || !templatesJson.is_object()) { - TAG_LOGE(AAFwkTag::CLI_TOOL, "Failed to parse templates JSON"); - return; - } - - // Process each template key (which contains the flattened path) - for (const auto &templateIt : templatesJson.items()) { - const std::string &flattenedKey = templateIt.key(); - const auto &templateValue = templateIt.value(); - - // Query nested value and process in one step to reduce depth - ProcessFlattenedTemplate(flattenedKey, templateValue, sep, args, cmdLine); - } -} - -void ToolUtil::ApplyJsonStringMapping(const std::string &templates, const AAFwk::WantParams &args, std::string &cmdLine) -{ - nlohmann::json templatesJson = nlohmann::json::parse(templates, nullptr, false); - if (templatesJson.is_discarded() || !templatesJson.is_object()) { - TAG_LOGE(AAFwkTag::CLI_TOOL, "Failed to parse templates JSON"); - return; - } - - for (const auto &[key, value] : args.GetParams()) { - if (!templatesJson.contains(key)) { - continue; - } - - auto &templateValue = templatesJson[key]; - - if (templateValue.is_string()) { - ProcessJsonStringTemplate(key, value, templateValue, cmdLine); - } else if (templateValue.is_object()) { - ProcessBooleanTemplate(key, value, templateValue, cmdLine); - } - } -} - -void ToolUtil::ApplyMixedMapping(const std::string &templates, const AAFwk::WantParams &args, std::string &cmdLine) -{ - nlohmann::json templatesJson = nlohmann::json::parse(templates, nullptr, false); - if (templatesJson.is_discarded() || !templatesJson.is_object()) { - TAG_LOGE(AAFwkTag::CLI_TOOL, "Failed to parse templates JSON"); - return; - } - - // First pass: collect positional parameters to ensure correct order - std::vector> positionalParams; // (order, value) - - for (const auto &[key, value] : args.GetParams()) { - // Skip if key not in templates - if (!templatesJson.contains(key)) { - continue; - } - - auto paramConfig = templatesJson[key]; - // Skip if invalid config - if (!paramConfig.is_object() || !paramConfig.contains("mode")) { - continue; - } - - std::string mode = paramConfig["mode"].get(); - // FLAG mode: process immediately - if (mode == "flag" && paramConfig.contains("template")) { - ApplyFlagModeLogic(value, paramConfig["template"], cmdLine); - continue; - } - - // POSITIONAL mode: collect for later processing - if (mode == "positional") { - ProcessPositionalMode(value, paramConfig, positionalParams); - continue; - } - - // FLATTENED mode: process immediately - if (mode == "flattened") { - ProcessFlattenedMode(key, value, paramConfig, args, cmdLine); - continue; - } - - // JSON-STRING mode: process immediately - if (mode == "json-string" && paramConfig.contains("template")) { - ProcessJsonStringTemplate(key, value, paramConfig["template"], cmdLine); - continue; - } - } - - // Second pass: append positional parameters in correct order - if (!positionalParams.empty()) { - // Sort by order value - std::sort(positionalParams.begin(), positionalParams.end(), - [](const auto &a, const auto &b) { return a.first < b.first; }); - - // Append in sorted order - for (const auto ¶m : positionalParams) { - cmdLine += " " + param.second; - } - } -} - -std::string ToolUtil::FormatTemplate(const std::string &tmpl, const std::string &value) -{ - std::string result = tmpl; - size_t pos = result.find("{value}"); - constexpr int32_t valueSize = 7; - if (pos != std::string::npos) { - result.replace(pos, valueSize, value); - } - pos = result.find("{json}"); - constexpr int32_t jsonSize = 6; - if (pos != std::string::npos) { - result.replace(pos, jsonSize, value); - } - return result; -} - -sptr ToolUtil::QueryNestedValue(const AAFwk::WantParams &args, - const std::string &path, const std::string &separator) -{ - if (path.empty()) { - return nullptr; - } - - // Try direct lookup first - sptr result = TryDirectLookup(args, path); - if (result != nullptr) { - return result; - } - - // Try nested path traversal - std::string sep = separator.empty() ? "." : separator; - return TryNestedPathTraversal(args, path, sep); -} - -sptr ToolUtil::TryDirectLookup(const AAFwk::WantParams &args, - const std::string &path) -{ - auto ¶ms = args.GetParams(); - auto it = params.find(path); - if (it != params.end()) { - TAG_LOGI(AAFwkTag::CLI_TOOL, "Found param with direct lookup: %{public}s", path.c_str()); - return it->second; - } - return nullptr; -} - -sptr ToolUtil::TryNestedPathTraversal(const AAFwk::WantParams &args, - const std::string &path, const std::string &separator) -{ - std::vector pathSegments = SplitPathBySeparator(path, separator); - constexpr int32_t segmentSize = 2; - if (pathSegments.size() < segmentSize) { - return nullptr; - } - - return QueryNestedPath(args, pathSegments, separator); -} - -std::string ToolUtil::WantParamsToJson(const AAFwk::WantParams &wantParams) -{ - nlohmann::json result = nlohmann::json::object(); - - for (const auto &[key, value] : wantParams.GetParams()) { - // Check if value is another WantParams (nested object) - AAFwk::WantParams nestedWantParams; - if (ExtractWantParams(value, nestedWantParams)) { - // Recursively convert nested WantParams to JSON - result[key] = nlohmann::json::parse(WantParamsToJson(nestedWantParams), nullptr, false); - } else if (value != nullptr) { - // Convert IInterface to JSON value using GetParamJsonValue - std::string valueJsonStr = GetParamJsonValue(value); - nlohmann::json valueJson = nlohmann::json::parse(valueJsonStr, nullptr, false); - if (!valueJson.is_discarded()) { - result[key] = valueJson; - } - } - } - - return result.dump(); } std::string ToolUtil::GetParamStringValue(const sptr &value) @@ -538,130 +332,6 @@ std::string ToolUtil::GetParamStringValue(const sptr &value) return ""; } -std::string ToolUtil::GetParamJsonValue(const sptr &value) -{ - if (value == nullptr) { - return "null"; - } - - // Try WantParams (nested object) - should check before other types - std::string jsonStr = ConvertWantParamsToJson(value); - if (!jsonStr.empty()) { - return jsonStr; - } - - // Try Array - only single-level array support (no nested arrays) - jsonStr = ConvertArrayToJson(value); - if (!jsonStr.empty()) { - return jsonStr; - } - - // Try String - JSON strings are quoted - jsonStr = ConvertStringToJson(value); - if (!jsonStr.empty()) { - return jsonStr; - } - - // Try Boolean - JSON booleans are unquoted - jsonStr = ConvertBooleanToJson(value); - if (!jsonStr.empty()) { - return jsonStr; - } - - // Try Numeric types (int, long, float, double) - jsonStr = ConvertNumericToJson(value); - if (!jsonStr.empty()) { - return jsonStr; - } - - // If no type matches, return null - return "null"; -} - -std::string ToolUtil::ConvertWantParamsToJson(const sptr &value) -{ - AAFwk::WantParams wantParams; - if (ExtractWantParams(value, wantParams)) { - return WantParamsToJson(wantParams); - } - return ""; -} - -std::string ToolUtil::ConvertArrayToJson(const sptr &value) -{ - if (auto arrayObj = AAFwk::IArray::Query(value)) { - return BuildJsonArrayFromIArray(arrayObj, [](const sptr& element) { - return GetParamJsonValue(element); - }); - } - return ""; -} - -std::string ToolUtil::ConvertStringToJson(const sptr &value) -{ - if (auto strObj = AAFwk::IString::Query(value)) { - std::string strValue; - if (strObj->GetString(strValue) != ERR_OK) { - return "\"\""; - } - return "\"" + EscapeJsonString(strValue) + "\""; - } - return ""; -} - -std::string ToolUtil::ConvertBooleanToJson(const sptr &value) -{ - if (auto boolObj = AAFwk::IBoolean::Query(value)) { - bool boolValue = false; - if (boolObj->GetValue(boolValue) == ERR_OK) { - return boolValue ? "true" : "false"; - } - return "false"; - } - return ""; -} - -std::string ToolUtil::ConvertNumericToJson(const sptr &value) -{ - // Try Integer/Int - if (auto intObj = AAFwk::IInteger::Query(value)) { - int intValue = 0; - if (intObj->GetValue(intValue) == ERR_OK) { - return std::to_string(intValue); - } - return "0"; - } - - // Try Long - if (auto longObj = AAFwk::ILong::Query(value)) { - long longValue = 0; - if (longObj->GetValue(longValue) == ERR_OK) { - return std::to_string(longValue); - } - return "0"; - } - - // Try Float - if (auto floatObj = AAFwk::IFloat::Query(value)) { - float floatValue = 0.0f; - if (floatObj->GetValue(floatValue) == ERR_OK) { - return std::to_string(floatValue); - } - return "0.0"; - } - - // Try Double - if (auto doubleObj = AAFwk::IDouble::Query(value)) { - double doubleValue = 0.0; - if (doubleObj->GetValue(doubleValue) == ERR_OK) { - return std::to_string(doubleValue); - } - return "0.0"; - } - - return ""; -} - bool ToolUtil::GetParamBoolValue(const sptr &value, bool &result) { if (value == nullptr) { @@ -679,335 +349,7 @@ bool ToolUtil::GetParamBoolValue(const sptr &value, bool &res return false; } -bool ToolUtil::GetParamArrayValue(const sptr &value, std::vector &result) -{ - if (value == nullptr) { - return false; - } - - // Try to query as IArray - auto arrayValue = AAFwk::IArray::Query(value); - if (arrayValue == nullptr) { - return false; - } - - bool foundAny = false; - IterateIArray(arrayValue, [&result, &foundAny](const sptr& element) { - std::string elementStr = GetParamStringValue(element); - if (!elementStr.empty()) { - result.push_back(elementStr); - foundAny = true; - } - }); - - return foundAny; -} - -void ToolUtil::ApplyFlattenedModeToSingleParam(const std::string &key, const sptr &value, - const std::string &separator, const nlohmann::json &templateValue, - const AAFwk::WantParams &args, std::string &cmdLine) -{ - // This method applies FLATTENED mode logic for a single parameter - // It uses nested path query to get the value from the nested structure - - // In flattened mode, templateValue should be a string template - if (!templateValue.is_string()) { - TAG_LOGW(AAFwkTag::CLI_TOOL, "Flattened mode requires string template for key '%{public}s'", key.c_str()); - return; - } - - // Inline logic to reduce call depth - std::string tmpl = templateValue.get(); - std::string sep = separator.empty() ? "." : separator; - sptr nestedValue = QueryNestedValue(args, key, sep); - - if (nestedValue == nullptr) { - TAG_LOGW(AAFwkTag::CLI_TOOL, "Could not find nested param for key '%{public}s'", key.c_str()); - return; - } - - // Convert the found value to string - std::string strValue = GetParamStringValue(nestedValue); - - // Handle boolean conditions - bool boolValue = false; - if (GetParamBoolValue(nestedValue, boolValue)) { - if (boolValue && tmpl.find("{value}") != std::string::npos) { - // Boolean value with {value} placeholder - std::string formatted = FormatTemplate(tmpl, (boolValue ? "true" : "false")); - cmdLine += " " + formatted; - } else { - // Boolean without placeholder, skip - TAG_LOGW(AAFwkTag::CLI_TOOL, "Boolean value for key '%{public}s' but no boolean template", - key.c_str()); - } - } else if (!strValue.empty()) { - // Regular string value - std::string formatted = FormatTemplate(tmpl, strValue); - cmdLine += " " + formatted; - } -} - -// ============================================================================ -// Helper methods for code reuse -// ============================================================================ - -bool ToolUtil::ExtractWantParams(const sptr &value, AAFwk::WantParams &wantParams) -{ - if (value == nullptr) { - return false; - } - - auto wantParamsWrapper = AAFwk::IWantParams::Query(value); - if (wantParamsWrapper == nullptr) { - return false; - } - - return wantParamsWrapper->GetValue(wantParams) == ERR_OK; -} - -std::string ToolUtil::EscapeJsonString(const std::string &str) -{ - std::string escaped = ""; - for (char c : str) { - if (c == '"') { - escaped += "\\\""; - } else if (c == '\\') { - escaped += "\\\\"; - } else if (c == '\n') { - escaped += "\\n"; - } else if (c == '\r') { - escaped += "\\r"; - } else if (c == '\t') { - escaped += "\\t"; - } else { - escaped += c; - } - } - return escaped; -} - -void ToolUtil::IterateIArray(sptr arrayObj, - std::function&)> elementHandler) -{ - if (arrayObj == nullptr) { - return; - } - - long arrayLength = 0; - if (arrayObj->GetLength(arrayLength) != ERR_OK || arrayLength <= 0) { - return; - } - - for (long i = 0; i < arrayLength; ++i) { - sptr elementValue; - if (arrayObj->Get(i, elementValue) != ERR_OK || elementValue == nullptr) { - continue; - } - - // Skip nested arrays - if (AAFwk::IArray::Query(elementValue) != nullptr) { - continue; - } - - elementHandler(elementValue); - } -} - -std::string ToolUtil::BuildJsonArrayFromIArray(sptr arrayObj, - std::function&)> elementConverter) -{ - if (arrayObj == nullptr) { - return "[]"; - } - - long arrayLength = 0; - if (arrayObj->GetLength(arrayLength) != ERR_OK || arrayLength <= 0) { - return "[]"; - } - - std::string result = "["; - bool firstElement = true; - - IterateIArray(arrayObj, [&result, &firstElement, &elementConverter](const sptr& element) { - if (!firstElement) { - result += ","; - } - result += elementConverter(element); - firstElement = false; - }); - - result += "]"; - return result; -} - -// ============================================================================ -// Core FLAG mode logic (extracted for reuse) -// ============================================================================ - -void ToolUtil::ApplyFlagModeLogic(const sptr &value, - const nlohmann::json &templateValue, std::string &cmdLine) -{ - if (value == nullptr) { - return; - } - - // Handle string template - if (templateValue.is_string()) { - std::string strValue = GetParamStringValue(value); - if (!strValue.empty()) { - std::string tmpl = templateValue.get(); - std::string formatted = FormatTemplate(tmpl, strValue); - cmdLine += " " + formatted; - } - return; - } - - // Handle boolean template with if_true/if_false - if (templateValue.is_object()) { - bool boolValue = false; - if (GetParamBoolValue(value, boolValue)) { - std::string flag = ""; - if (boolValue && templateValue.contains("if_true")) { - flag = templateValue["if_true"].get(); - } else if (!boolValue && templateValue.contains("if_false")) { - flag = templateValue["if_false"].get(); - } - - if (!flag.empty()) { - cmdLine += " " + flag; - } - } - return; - } - - // Handle array type - expand to multiple arguments - if (!templateValue.is_string()) { - return; - } - - std::string tmpl = templateValue.get(); - ProcessArrayExpansion(value, tmpl, cmdLine); -} - -// ============================================================================ -// Helper methods for mode processing (extracted to reduce nesting depth) -// ============================================================================ - -void ToolUtil::ProcessPositionalMode(const sptr &value, const nlohmann::json ¶mConfig, - std::vector> &positionalParams) -{ - // POSITIONAL mode: handle order and order arrays - if (!paramConfig.contains("order")) { - return; - } - - auto orderValue = paramConfig["order"]; - if (!orderValue.is_number_integer()) { - return; - } - - // Single order value - std::string strValue = GetParamStringValue(value); - if (!strValue.empty()) { - positionalParams.push_back({orderValue.get(), strValue}); - } -} - -void ToolUtil::ProcessFlattenedMode(const std::string &key, const sptr &value, - const nlohmann::json ¶mConfig, const AAFwk::WantParams &args, std::string &cmdLine) -{ - // FLATTENED mode: handle both single template and multiple templates - std::string separator = "."; - if (paramConfig.contains("separator")) { - separator = paramConfig["separator"].get(); - } - - if (!paramConfig.contains("template")) { - return; - } - - // Single template - ApplyFlattenedModeToSingleParam(key, value, separator, paramConfig["template"], args, cmdLine); -} - -void ToolUtil::ProcessJsonStringTemplate(const std::string &key, const sptr &value, - const nlohmann::json &templateValue, std::string &cmdLine) -{ - if (value == nullptr) { - TAG_LOGW(AAFwkTag::CLI_TOOL, "Param '%{public}s' value is null", key.c_str()); - return; - } - - std::string tmpl = templateValue.get(); - std::string jsonStr = ConvertValueToJson(key, value); - - // Apply template with JSON string - std::string formatted = FormatTemplate(tmpl, jsonStr); - cmdLine += " " + formatted; -} - -void ToolUtil::ProcessBooleanTemplate(const std::string &key, const sptr &value, - const nlohmann::json &templateValue, std::string &cmdLine) -{ - bool boolValue = false; - if (!GetParamBoolValue(value, boolValue)) { - return; - } - - std::string flag = ""; - if (boolValue && templateValue.contains("if_true")) { - flag = templateValue["if_true"].get(); - } else if (!boolValue && templateValue.contains("if_false")) { - flag = templateValue["if_false"].get(); - } - - if (!flag.empty()) { - cmdLine += " " + flag; - } -} - -std::string ToolUtil::ConvertValueToJson(const std::string &key, const sptr &value) -{ - // Check if value is a WantParams (nested object) - AAFwk::WantParams nestedWantParams; - if (ExtractWantParams(value, nestedWantParams)) { - // Recursively convert nested WantParams to JSON - std::string jsonStr = WantParamsToJson(nestedWantParams); - TAG_LOGI(AAFwkTag::CLI_TOOL, "Param '%{public}s' is nested WantParams, converted to: %{public}s", - key.c_str(), jsonStr.c_str()); - return jsonStr; - } - - // Convert IInterface to JSON string - std::string jsonStr = GetParamJsonValue(value); - TAG_LOGI(AAFwkTag::CLI_TOOL, "Param '%{public}s' converted to JSON: %{public}s", - key.c_str(), jsonStr.c_str()); - return jsonStr; -} - -void ToolUtil::ProcessFlattenedTemplate(const std::string &flattenedKey, const nlohmann::json &templateValue, - const std::string &separator, const AAFwk::WantParams &args, std::string &cmdLine) -{ - // Query nested value - simplifed version - sptr value = QueryNestedValue(args, flattenedKey, separator); - if (value == nullptr) { - return; - } - - // Process template based on type - if (templateValue.is_string()) { - std::string strValue = GetParamStringValue(value); - if (!strValue.empty()) { - std::string formatted = FormatTemplate(templateValue.get(), strValue); - cmdLine += " " + formatted; - } - } else if (templateValue.is_object()) { - ProcessBooleanTemplate(flattenedKey, value, templateValue, cmdLine); - } -} - -void ToolUtil::ProcessArrayExpansion(const sptr &value, const std::string &tmpl, +void ToolUtil::ProcessArrayExpansion(const std::string &key, const sptr &value, std::string &cmdLine) { auto arrayValue = AAFwk::IArray::Query(value); @@ -1034,88 +376,11 @@ void ToolUtil::ProcessArrayExpansion(const sptr &value, const std::string elementStr = GetParamStringValue(elementValue); if (!elementStr.empty()) { - std::string formatted = FormatTemplate(tmpl, elementStr); - cmdLine += " " + formatted; + cmdLine += " --" + key + " " + elementStr; } } } -std::vector ToolUtil::SplitPathBySeparator(const std::string &path, const std::string &separator) -{ - std::vector pathSegments; - if (path.empty()) { - return pathSegments; - } - - std::string sep = separator.empty() ? "." : separator; - size_t start = 0; - size_t end = path.find(sep); - - while (end != std::string::npos) { - std::string segment = path.substr(start, end - start); - if (!segment.empty()) { - pathSegments.push_back(segment); - } - start = end + sep.length(); - end = path.find(sep, start); - } - std::string lastSegment = path.substr(start); - if (!lastSegment.empty()) { - pathSegments.push_back(lastSegment); - } - - return pathSegments; -} - -sptr ToolUtil::QueryNestedPath(const AAFwk::WantParams &args, - const std::vector &pathSegments, const std::string &separator) -{ - auto ¶ms = args.GetParams(); - - // First level: query from args - auto firstIt = params.find(pathSegments[0]); - if (firstIt == params.end()) { - TAG_LOGI(AAFwkTag::CLI_TOOL, "First level key not found: %{public}s", pathSegments[0].c_str()); - return nullptr; - } - - sptr currentValue = firstIt->second; - - // Nested levels: traverse through WantParams - for (size_t i = 1; i < pathSegments.size(); ++i) { - currentValue = QueryNextLevel(currentValue, pathSegments[i], separator); - if (currentValue == nullptr) { - TAG_LOGI(AAFwkTag::CLI_TOOL, "Nested key not found: %{public}s", pathSegments[i].c_str()); - return nullptr; - } - } - - TAG_LOGI(AAFwkTag::CLI_TOOL, "Found param with nested traversal: %{public}s", - pathSegments.size() > 1 ? pathSegments[0].c_str() : "N/A"); - return currentValue; -} - -sptr ToolUtil::QueryNextLevel(const sptr ¤tValue, - const std::string &nextSegment, const std::string &separator) -{ - if (currentValue == nullptr) { - return nullptr; - } - - AAFwk::WantParams nestedParams; - if (!ExtractWantParams(currentValue, nestedParams)) { - return nullptr; - } - - auto &nestedMap = nestedParams.GetParams(); - auto nestedIt = nestedMap.find(nextSegment); - if (nestedIt == nestedMap.end()) { - return nullptr; - } - - return nestedIt->second; -} - // ============================================================================ // Type validation helpers for ValidateInputSchemaProperties // ============================================================================ @@ -1130,9 +395,6 @@ bool ToolUtil::ValidateParamType(const sptr &value, const std if (expectedType == "array") { return ValidateArrayType(value, propertySchema, key); } - if (expectedType == "object") { - return ValidateObjectType(value, propertySchema, key); - } return ValidateBasicType(value, expectedType); } @@ -1170,19 +432,6 @@ bool ToolUtil::ValidateArrayType(const sptr &value, return ValidateArrayItems(arrayObj, propertySchema["items"], key); } -bool ToolUtil::ValidateObjectType(const sptr &value, - const nlohmann::json &propertySchema, const std::string &key) -{ - if (!IsObjectType(value)) { - return false; - } - AAFwk::WantParams nestedParams; - if (!ExtractWantParams(value, nestedParams)) { - return false; - } - return ValidateNestedObject(nestedParams, propertySchema, key); -} - bool ToolUtil::ValidateArrayItems(sptr arrayObj, const nlohmann::json &itemsSchema, const std::string &key) { @@ -1209,47 +458,6 @@ bool ToolUtil::ValidateArrayItems(sptr arrayObj, return true; } -bool ToolUtil::ValidateNestedObject(const AAFwk::WantParams &nestedParams, - const nlohmann::json &objectSchema, const std::string &parentKey) -{ - if (!objectSchema.contains("properties") || !objectSchema["properties"].is_object()) { - TAG_LOGI(AAFwkTag::CLI_TOOL, "No nested properties defined for '%{public}s', skipping validation", - parentKey.c_str()); - return true; - } - auto nestedProperties = objectSchema["properties"]; - if (objectSchema.contains("required") && objectSchema["required"].is_array()) { - for (const auto &requiredProp : objectSchema["required"]) { - if (!requiredProp.is_string()) { - continue; - } - std::string requiredKey = requiredProp.get(); - std::string fullKey = parentKey + "." + requiredKey; - if (nestedParams.GetParams().find(requiredKey) == nestedParams.GetParams().end()) { - TAG_LOGE(AAFwkTag::CLI_TOOL, "Required nested property '%{public}s' not found", fullKey.c_str()); - return false; - } - } - } - for (const auto &[nestedKey, nestedValue] : nestedParams.GetParams()) { - std::string fullKey = parentKey + "." + nestedKey; - if (!nestedProperties.contains(nestedKey)) { - TAG_LOGE(AAFwkTag::CLI_TOOL, "Nested property '%{public}s' not found in schema", fullKey.c_str()); - return false; - } - auto &nestedPropertySchema = nestedProperties[nestedKey]; - if (nestedPropertySchema.contains("type")) { - std::string expectedType = nestedPropertySchema["type"].get(); - if (!ValidateParamType(nestedValue, expectedType, nestedPropertySchema, fullKey)) { - TAG_LOGE(AAFwkTag::CLI_TOOL, "Nested property '%{public}s' type mismatch, expected: %{public}s", - fullKey.c_str(), expectedType.c_str()); - return false; - } - } - } - return true; -} - bool ToolUtil::IsStringType(const sptr &value) { return AAFwk::IString::Query(value) != nullptr; @@ -1278,10 +486,132 @@ bool ToolUtil::IsArrayType(const sptr &value) return AAFwk::IArray::Query(value) != nullptr; } -bool ToolUtil::IsObjectType(const sptr &value) +bool ToolUtil::IsSkillTool(const std::string &toolName) { - AAFwk::WantParams wantParams; - return ExtractWantParams(value, wantParams); + return toolName == "ohos-arkTSScript"; +} + +void ToolUtil::NormalizeSkillParamKeys(AAFwk::WantParams &args) +{ + auto ¶ms = args.GetParams(); + std::vector>> toRename; + for (const auto &[key, value] : params) { + std::string bareKey = key; + if (bareKey.size() > PREFIX_DOUBLE_DASH_LEN && bareKey.compare(0, PREFIX_DOUBLE_DASH_LEN, "--") == 0) { + bareKey.erase(0, PREFIX_DOUBLE_DASH_LEN); + } else if (bareKey.size() > 1 && bareKey[0] == '-') { + bareKey.erase(0, 1); + } else { + continue; + } + if (params.find(bareKey) == params.end()) { + toRename.emplace_back(key, value); + } + } + for (auto &[prefixedKey, value] : toRename) { + std::string bareKey = prefixedKey; + if (bareKey.compare(0, PREFIX_DOUBLE_DASH_LEN, "--") == 0) { + bareKey.erase(0, PREFIX_DOUBLE_DASH_LEN); + } else { + bareKey.erase(0, 1); + } + args.Remove(prefixedKey); + args.SetParam(bareKey, value); + } +} + +bool ToolUtil::ExpandArgsFromJson(AAFwk::WantParams &args, const std::string &argsStr) +{ + static const std::set RESERVED_KEYS = { + "bundleName", "moduleName", "skillName", "scriptPath", "functionName" + }; + + auto jsonObj = nlohmann::json::parse(argsStr, nullptr, false); + if (!jsonObj.is_object() || jsonObj.is_discarded()) { + return false; + } + args.Remove("args"); + for (auto &[key, val] : jsonObj.items()) { + if (RESERVED_KEYS.count(key) > 0) { + continue; + } + if (val.is_string()) { + args.SetParam(key, AAFwk::String::Box(val.get())); + } else if (val.is_number_integer()) { + args.SetParam(key, AAFwk::Integer::Box(val.get())); + } else if (val.is_boolean()) { + args.SetParam(key, AAFwk::Boolean::Box(val.get())); + } + } + return true; +} + +void ToolUtil::ExpandArgsFromWantParams(AAFwk::WantParams &args) +{ + static const std::set RESERVED_KEYS = { + "bundleName", "moduleName", "skillName", "scriptPath", "functionName" + }; + + auto ¶ms = args.GetParams(); + auto it = params.find("args"); + if (it == params.end()) { + return; + } + auto *wantParams = AAFwk::IWantParams::Query(it->second); + if (wantParams == nullptr) { + return; + } + AAFwk::WantParams nestedParams; + if (wantParams->GetValue(nestedParams) != ERR_OK) { + return; + } + args.Remove("args"); + for (auto &[key, value] : nestedParams.GetParams()) { + if (RESERVED_KEYS.count(key) > 0) { + continue; + } + args.SetParam(key, value); + } +} + +void ToolUtil::ExpandArgsJsonString(AAFwk::WantParams &args) +{ + auto argsStr = args.GetStringParam("args"); + if (!argsStr.empty() && ExpandArgsFromJson(args, argsStr)) { + return; + } + ExpandArgsFromWantParams(args); +} + +std::shared_ptr ToolUtil::FilterSkillArgs(const AAFwk::WantParams &args) +{ + static const std::set RESERVED_KEYS = { + "bundleName", "moduleName", "skillName", "scriptPath", "functionName" + }; + + auto skillArgs = std::make_shared(); + auto ¶ms = args.GetParams(); + for (auto &[key, value] : params) { + if (RESERVED_KEYS.count(key) == 0) { + skillArgs->SetParam(key, value); + } + } + return skillArgs; +} + +CliSessionInfo ToolUtil::BuildSkillSessionInfo(const std::string &sessionId, + int32_t resultCode, const AppExecFwk::SkillExecuteResult &skillResult) +{ + CliSessionInfo session; + session.sessionId = sessionId; + session.toolName = "ohos-arkTSScript"; + session.status = (resultCode == ERR_OK) ? "completed" : "failed"; + session.result = std::make_shared(); + session.result->exitCode = skillResult.code; + if (skillResult.result != nullptr) { + session.result->outputText = skillResult.result->ToString(); + } + return session; } } // namespace CliTool diff --git a/cli_tool_framework/services/common/src/permission_util.cpp b/cli_tool_framework/services/common/src/permission_util.cpp index 38bb38036f..8931834d8a 100644 --- a/cli_tool_framework/services/common/src/permission_util.cpp +++ b/cli_tool_framework/services/common/src/permission_util.cpp @@ -26,16 +26,18 @@ bool PermissionUtil::VerifyAccessToken(AccessToken::AccessTokenID tokenId, std::vector 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) diff --git a/test/unittest/cli_tool_mgr/BUILD.gn b/cli_tool_framework/test/unittest/BUILD.gn similarity index 61% rename from test/unittest/cli_tool_mgr/BUILD.gn rename to cli_tool_framework/test/unittest/BUILD.gn index d0b0095ba5..5c8c26cd4b 100644 --- a/test/unittest/cli_tool_mgr/BUILD.gn +++ b/cli_tool_framework/test/unittest/BUILD.gn @@ -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", ] } diff --git a/cli_tool_framework/test/unittest/cli_common_util_test/BUILD.gn b/cli_tool_framework/test/unittest/cli_common_util_test/BUILD.gn new file mode 100644 index 0000000000..30f9891479 --- /dev/null +++ b/cli_tool_framework/test/unittest/cli_common_util_test/BUILD.gn @@ -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" ] +} diff --git a/cli_tool_framework/test/unittest/cli_common_util_test/cli_common_util_test.cpp b/cli_tool_framework/test/unittest/cli_common_util_test/cli_common_util_test.cpp new file mode 100644 index 0000000000..cfcdf04ed4 --- /dev/null +++ b/cli_tool_framework/test/unittest/cli_common_util_test/cli_common_util_test.cpp @@ -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 + +#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 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 diff --git a/cli_tool_framework/test/unittest/cli_common_util_test/mock/include/accesstoken_kit.h b/cli_tool_framework/test/unittest/cli_common_util_test/mock/include/accesstoken_kit.h new file mode 100644 index 0000000000..b9249c0a74 --- /dev/null +++ b/cli_tool_framework/test/unittest/cli_common_util_test/mock/include/accesstoken_kit.h @@ -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 +#include + +#include "access_token.h" + +namespace OHOS { +namespace Security { +namespace AccessToken { +class AccessTokenKit { +public: + static int32_t VerifyAccessToken(AccessTokenID tokenId, const std::vector &permissions, + std::vector &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 diff --git a/cli_tool_framework/interfaces/cli_tool/src/exec_tool_callback_impl.cpp b/cli_tool_framework/test/unittest/cli_common_util_test/mock/include/cli_common_mock.h similarity index 56% rename from cli_tool_framework/interfaces/cli_tool/src/exec_tool_callback_impl.cpp rename to cli_tool_framework/test/unittest/cli_common_util_test/mock/include/cli_common_mock.h index aaebd26f75..34adf7c2db 100644 --- a/cli_tool_framework/interfaces/cli_tool/src/exec_tool_callback_impl.cpp +++ b/cli_tool_framework/test/unittest/cli_common_util_test/mock/include/cli_common_mock.h @@ -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 +#include +#include 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 permissionStateList; + + static void Reset(); +}; +} // namespace CliTool +} // namespace OHOS + +#endif // OHOS_ABILITY_RUNTIME_CLI_COMMON_MOCK_H diff --git a/cli_tool_framework/test/unittest/cli_common_util_test/mock/include/parameters.h b/cli_tool_framework/test/unittest/cli_common_util_test/mock/include/parameters.h new file mode 100644 index 0000000000..b951188db7 --- /dev/null +++ b/cli_tool_framework/test/unittest/cli_common_util_test/mock/include/parameters.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 + +#include "cli_common_mock.h" + +namespace OHOS { +namespace system { +template +T GetIntParameter(const std::string &, T) +{ + return static_cast(CliTool::CliCommonMock::intParameterValue); +} +} // namespace system +} // namespace OHOS + +#endif // OHOS_ABILITY_RUNTIME_CLI_COMMON_MOCK_PARAMETERS_H diff --git a/cli_tool_framework/test/unittest/cli_common_util_test/mock/src/cli_common_mock.cpp b/cli_tool_framework/test/unittest/cli_common_util_test/mock/src/cli_common_mock.cpp new file mode 100644 index 0000000000..4bdc5df725 --- /dev/null +++ b/cli_tool_framework/test/unittest/cli_common_util_test/mock/src/cli_common_mock.cpp @@ -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 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 &permissions, + std::vector &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 diff --git a/cli_tool_framework/test/unittest/cli_event_reply_manager_test/BUILD.gn b/cli_tool_framework/test/unittest/cli_event_reply_manager_test/BUILD.gn new file mode 100644 index 0000000000..4ba9961462 --- /dev/null +++ b/cli_tool_framework/test/unittest/cli_event_reply_manager_test/BUILD.gn @@ -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" ] +} diff --git a/cli_tool_framework/test/unittest/cli_event_reply_manager_test/cli_event_reply_manager_test.cpp b/cli_tool_framework/test/unittest/cli_event_reply_manager_test/cli_event_reply_manager_test.cpp new file mode 100644 index 0000000000..70bb3b36ca --- /dev/null +++ b/cli_tool_framework/test/unittest/cli_event_reply_manager_test/cli_event_reply_manager_test.cpp @@ -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 +#include +#include + +#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 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 diff --git a/cli_tool_framework/test/unittest/cli_session_info_test/BUILD.gn b/cli_tool_framework/test/unittest/cli_session_info_test/BUILD.gn new file mode 100644 index 0000000000..be9e1f5cd4 --- /dev/null +++ b/cli_tool_framework/test/unittest/cli_session_info_test/BUILD.gn @@ -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" ] +} diff --git a/cli_tool_framework/test/unittest/cli_session_info_test/cli_session_info_test.cpp b/cli_tool_framework/test/unittest/cli_session_info_test/cli_session_info_test.cpp new file mode 100644 index 0000000000..0bf1c4783e --- /dev/null +++ b/cli_tool_framework/test/unittest/cli_session_info_test/cli_session_info_test.cpp @@ -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 +#include +#include + +#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 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(); + completedInfo.result->exitCode = TEST_EXIT_CODE; + completedInfo.result->outputText = "ok"; + + Parcel completedParcel; + ASSERT_TRUE(completedInfo.Marshalling(completedParcel)); + completedParcel.RewindRead(0); + std::unique_ptr 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 diff --git a/cli_tool_framework/test/unittest/cli_session_subscription_manager_test/BUILD.gn b/cli_tool_framework/test/unittest/cli_session_subscription_manager_test/BUILD.gn new file mode 100644 index 0000000000..ed6de2dd47 --- /dev/null +++ b/cli_tool_framework/test/unittest/cli_session_subscription_manager_test/BUILD.gn @@ -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" ] +} diff --git a/cli_tool_framework/test/unittest/cli_session_subscription_manager_test/cli_session_subscription_manager_test.cpp b/cli_tool_framework/test/unittest/cli_session_subscription_manager_test/cli_session_subscription_manager_test.cpp new file mode 100644 index 0000000000..879f3c10c9 --- /dev/null +++ b/cli_tool_framework/test/unittest/cli_session_subscription_manager_test/cli_session_subscription_manager_test.cpp @@ -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 +#include +#include + +#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 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 diff --git a/test/unittest/cli_tool_mgr/cli_tool_data_manager_test/BUILD.gn b/cli_tool_framework/test/unittest/cli_tool_data_manager_test/BUILD.gn similarity index 100% rename from test/unittest/cli_tool_mgr/cli_tool_data_manager_test/BUILD.gn rename to cli_tool_framework/test/unittest/cli_tool_data_manager_test/BUILD.gn diff --git a/cli_tool_framework/test/unittest/cli_tool_data_manager_test/cli_tool_data_manager_test.cpp b/cli_tool_framework/test/unittest/cli_tool_data_manager_test/cli_tool_data_manager_test.cpp new file mode 100644 index 0000000000..2718bc186e --- /dev/null +++ b/cli_tool_framework/test/unittest/cli_tool_data_manager_test/cli_tool_data_manager_test.cpp @@ -0,0 +1,1238 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#define private public +#define protected public +#include "cli_tool_data_manager.h" +#undef private +#undef protected +#include "cli_error_code.h" +#include "hilog_tag_wrapper.h" +#include "mock_single_kv_store.h" + +using namespace testing::ext; + +namespace OHOS { +namespace CliTool { + +class CliToolDataManagerTest : public testing::Test { +public: + static void SetUpTestCase(); + static void TearDownTestCase(); + void SetUp(); + void TearDown(); + + static constexpr const char* TEST_CONFIG_DIR = "/data/test_cli_tool_configs"; + static constexpr const char* TEST_TOOL1_FILE = "/data/test_cli_tool_configs/tool1.json"; + static constexpr const char* TEST_TOOL2_FILE = "/data/test_cli_tool_configs/tool2.json"; + static constexpr const char* TEST_TOOL3_FILE = "/data/test_cli_tool_configs/tool3.json"; + static constexpr int32_t ERR_FILE_NOT_FOUND = -2; + static constexpr int32_t ERR_JSON_PARSE_FAILED = -3; + static constexpr int32_t ERR_KVSTORE_NOT_READY = -4; +}; + +void CliToolDataManagerTest::SetUpTestCase() +{ + TAG_LOGI(AAFwkTag::ABILITYMGR, "CliToolDataManagerTest::SetUpTestCase"); + + // Create test config directory and files + std::string mkdirCmd = "mkdir -p " + std::string(TEST_CONFIG_DIR); + std::system(mkdirCmd.c_str()); + + // Create tool1.json + std::ofstream file1(TEST_TOOL1_FILE); + file1 << R"({ + "name": "ohos-test_tool1", + "version": "1.0.0", + "description": "Test tool 1", + "executablePath": "/bin/test1", + "requirePermissions": ["ohos.permission.INTERNET"], + "inputSchema": {}, + "outputSchema": {}, + "eventSchemas": {"stdout": {"type": "string"}}, + "timeout": 30000, + "eventTypes": ["stdout", "stderr"], + "hasSubCommand": false, + "subcommands": {} + })"; + file1.close(); + + // Create tool2.json + std::ofstream file2(TEST_TOOL2_FILE); + file2 << R"({ + "name": "hms-test_tool2", + "version": "2.0.0", + "description": "Test tool 2", + "executablePath": "/bin/test2", + "requirePermissions": ["ohos.permission.READ_STORAGE"], + "inputSchema": {}, + "outputSchema": {}, + "eventSchemas": {"stdout": {"type": "string"}}, + "timeout": 60000, + "eventTypes": ["exit"], + "hasSubCommand": true, + "subcommands": { + "subcmd1": { + "description": "Subcommand 1", + "requirePermissions": [], + "inputSchema": {}, + "outputSchema": {} + } + } + })"; + file2.close(); +} + +void CliToolDataManagerTest::TearDownTestCase() +{ + TAG_LOGI(AAFwkTag::ABILITYMGR, "CliToolDataManagerTest::TearDownTestCase"); + // Clean up test files + std::remove(TEST_TOOL1_FILE); + std::remove(TEST_TOOL2_FILE); + std::remove(TEST_TOOL3_FILE); + rmdir(TEST_CONFIG_DIR); +} + +void CliToolDataManagerTest::SetUp() +{ + TAG_LOGI(AAFwkTag::ABILITYMGR, "CliToolDataManagerTest::SetUp"); + std::remove(TEST_TOOL3_FILE); +} + +void CliToolDataManagerTest::TearDown() +{ + TAG_LOGI(AAFwkTag::ABILITYMGR, "CliToolDataManagerTest::TearDown"); + std::remove(TEST_TOOL3_FILE); + CliToolDataManager::GetInstance().kvStorePtr_ = nullptr; + CliToolDataManager::GetInstance().toolsLoaded_ = false; +} + +namespace { +std::string BuildToolJson(const std::string &name, const std::string &description = "Mock tool") +{ + nlohmann::json json = { + {"name", name}, + {"version", "1.0.0"}, + {"description", description}, + {"executablePath", "/bin/mock"}, + {"requirePermissions", nlohmann::json::array({"ohos.permission.TEST"})}, + {"inputSchema", nlohmann::json::object()}, + {"outputSchema", nlohmann::json::object()}, + {"hasSubCommand", false}, + }; + return json.dump(); +} + +void WriteFile(const std::string &path, const std::string &content) +{ + std::ofstream file(path); + file << content; +} +} // namespace + +/** + * @tc.name: CliToolDataManager_JsonArrayToTools_001 + * @tc.desc: Test parsing JSON array to tools vector + * @tc.type: FUNC + */ +HWTEST_F(CliToolDataManagerTest, CliToolDataManager_JsonArrayToTools_001, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::ABILITYMGR, "CliToolDataManager_JsonArrayToTools_001 start"); + + auto& dataManager = CliToolDataManager::GetInstance(); + std::string jsonStr = R"([ + { + "name": "ohos-array_tool1", + "version": "1.0", + "description": "Array tool 1", + "executablePath": "/bin/at1", + "requirePermissions": [], + "inputSchema": {}, + "outputSchema": {} + }, + { + "name": "ohos-array_tool2", + "version": "2.0", + "description": "Array tool 2", + "executablePath": "/bin/at2", + "requirePermissions": [], + "inputSchema": {}, + "outputSchema": {} + } + ])"; + + std::vector tools; + int32_t ret = dataManager.JsonArrayToTools(jsonStr, tools); + + EXPECT_EQ(ret, 0); + EXPECT_EQ(tools.size(), 2u); + EXPECT_EQ(tools[0].name, "ohos-array_tool1"); + EXPECT_EQ(tools[1].name, "ohos-array_tool2"); + + TAG_LOGI(AAFwkTag::ABILITYMGR, "CliToolDataManager_JsonArrayToTools_001 end"); +} + +// ==================== ToolInfo ParseToJson Tests ==================== + +/** + * @tc.name: ToolInfo_ParseToJson_001 + * @tc.desc: Test converting ToolInfo to JSON + * @tc.type: FUNC + */ +HWTEST_F(CliToolDataManagerTest, ToolInfo_ParseToJson_001, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::ABILITYMGR, "ToolInfo_ParseToJson_001 start"); + + ToolInfo tool; + tool.name = "test_tool"; + tool.version = "1.0.0"; + tool.description = "Test description"; + tool.executablePath = "/bin/test"; + tool.requirePermissions = {"ohos.permission.INTERNET"}; + tool.hasSubCommand = false; + + nlohmann::json json = tool.ParseToJson(); + std::string jsonStr = json.dump(); + + EXPECT_FALSE(jsonStr.empty()); + EXPECT_NE(jsonStr.find("test_tool"), std::string::npos); + EXPECT_NE(jsonStr.find("1.0.0"), std::string::npos); + + TAG_LOGI(AAFwkTag::ABILITYMGR, "ToolInfo_ParseToJson_001 end"); +} + +/** + * @tc.name: ToolInfo_ParseToJson_002 + * @tc.desc: Test converting ToolInfo with subcommands to JSON + * @tc.type: FUNC + */ +HWTEST_F(CliToolDataManagerTest, ToolInfo_ParseToJson_002, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::ABILITYMGR, "ToolInfo_ParseToJson_002 start"); + + ToolInfo tool; + tool.name = "tool_with_subcmds"; + tool.version = "2.0.0"; + tool.hasSubCommand = true; + + SubCommandInfo subCmd; + subCmd.description = "Test subcommand"; + subCmd.inputSchema = R"({"type": "object"})"; + subCmd.outputSchema = R"({"type": "string"})"; + tool.subcommands["sub1"] = subCmd; + + nlohmann::json json = tool.ParseToJson(); + + EXPECT_TRUE(json.contains("subcommands")); + EXPECT_TRUE(json["subcommands"].contains("sub1")); + + TAG_LOGI(AAFwkTag::ABILITYMGR, "ToolInfo_ParseToJson_002 end"); +} + +// ==================== ToolInfo ParseFromJson Tests ==================== + +/** + * @tc.name: ToolInfo_ParseFromJson_001 + * @tc.desc: Test parsing JSON to ToolInfo + * @tc.type: FUNC + */ +HWTEST_F(CliToolDataManagerTest, ToolInfo_ParseFromJson_001, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::ABILITYMGR, "ToolInfo_ParseFromJson_001 start"); + + nlohmann::json json = R"({ + "name": "ohos-json_tool", + "version": "1.0.0", + "description": "JSON test tool", + "executablePath": "/bin/jsontest", + "requirePermissions": ["ohos.permission.INTERNET"], + "inputSchema": {"type": "object"}, + "outputSchema": {"type": "string"}, + "eventSchemas": {"stdout": {"type": "string"}}, + "timeout": 30000, + "eventTypes": ["stdout"], + "hasSubCommand": false, + "subcommands": {} + })"_json; + + ToolInfo tool; + bool result = ToolInfo::ParseFromJson(json, tool); + + EXPECT_TRUE(result); + EXPECT_EQ(tool.name, "ohos-json_tool"); + EXPECT_EQ(tool.version, "1.0.0"); + EXPECT_EQ(tool.description, "JSON test tool"); + EXPECT_EQ(tool.executablePath, "/bin/jsontest"); + EXPECT_EQ(tool.hasSubCommand, false); + + TAG_LOGI(AAFwkTag::ABILITYMGR, "ToolInfo_ParseFromJson_001 end"); +} + +/** + * @tc.name: ToolInfo_ParseFromJson_002 + * @tc.desc: Test parsing JSON with subcommands to ToolInfo + * @tc.type: FUNC + */ +HWTEST_F(CliToolDataManagerTest, ToolInfo_ParseFromJson_002, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::ABILITYMGR, "ToolInfo_ParseFromJson_002 start"); + + nlohmann::json json = R"({ + "name": "hms-tool_with_subs", + "version": "1.0.0", + "description": "Tool with subcommands", + "executablePath": "/bin/tool", + "requirePermissions": [], + "inputSchema": {}, + "outputSchema": {}, + "hasSubCommand": true, + "subcommands": { + "build": { + "description": "Build subcommand", + "requirePermissions": [], + "inputSchema": {"type": "object"}, + "outputSchema": {"type": "string"} + }, + "run": { + "description": "Run subcommand", + "requirePermissions": [], + "inputSchema": {"type": "object"}, + "outputSchema": {"type": "string"} + } + } + })"_json; + + ToolInfo tool; + bool result = ToolInfo::ParseFromJson(json, tool); + + EXPECT_TRUE(result); + EXPECT_EQ(tool.name, "hms-tool_with_subs"); + EXPECT_TRUE(tool.hasSubCommand); + EXPECT_EQ(tool.subcommands.size(), 2u); + EXPECT_EQ(tool.subcommands["build"].description, "Build subcommand"); + + TAG_LOGI(AAFwkTag::ABILITYMGR, "ToolInfo_ParseFromJson_002 end"); +} + +/** + * @tc.name: ToolInfo_ParseFromJson_003 + * @tc.desc: Test parsing empty JSON to ToolInfo + * @tc.type: FUNC + */ +HWTEST_F(CliToolDataManagerTest, ToolInfo_ParseFromJson_003, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::ABILITYMGR, "ToolInfo_ParseFromJson_003 start"); + + nlohmann::json json; + + ToolInfo tool; + bool result = ToolInfo::ParseFromJson(json, tool); + + EXPECT_FALSE(result); + EXPECT_TRUE(tool.name.empty()); + + TAG_LOGI(AAFwkTag::ABILITYMGR, "ToolInfo_ParseFromJson_003 end"); +} + +// ==================== ToolInfo ParseFromJson/ParseToJson Round Trip Tests ==================== + +/** + * @tc.name: ToolInfo_ParseFromJson_ParseToJson_RoundTrip_001 + * @tc.desc: Test ToolInfo ParseFromJson and ParseToJson round trip + * @tc.type: FUNC + */ +HWTEST_F(CliToolDataManagerTest, ToolInfo_ParseFromJson_ParseToJson_RoundTrip_001, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::ABILITYMGR, "ToolInfo_ParseFromJson_ParseToJson_RoundTrip_001 start"); + + nlohmann::json originalJson = R"({ + "name": "ohos-roundtrip_tool", + "version": "3.0.0", + "description": "Round trip test", + "executablePath": "/bin/roundtrip", + "requirePermissions": ["ohos.permission.CAMERA"], + "inputSchema": {"type": "object", "properties": {"input": {"type": "string"}}}, + "outputSchema": {"type": "array"}, + "eventSchemas": {"exit": {"type": "number"}}, + "timeout": 60000, + "eventTypes": ["stdout", "stderr", "exit"], + "hasSubCommand": true, + "subcommands": { + "sub1": { + "description": "Sub 1", + "requirePermissions": [], + "inputSchema": {"type": "object"}, + "outputSchema": {"type": "string"} + } + } + })"_json; + + ToolInfo tool; + bool result = ToolInfo::ParseFromJson(originalJson, tool); + EXPECT_TRUE(result); + nlohmann::json resultJson = tool.ParseToJson(); + + EXPECT_EQ(resultJson["name"], originalJson["name"]); + EXPECT_EQ(resultJson["version"], originalJson["version"]); + EXPECT_EQ(resultJson["description"], originalJson["description"]); + EXPECT_EQ(resultJson["executablePath"], originalJson["executablePath"]); + EXPECT_EQ(resultJson["hasSubCommand"], originalJson["hasSubCommand"]); + EXPECT_EQ(resultJson["eventTypes"], originalJson["eventTypes"]); + + TAG_LOGI(AAFwkTag::ABILITYMGR, "ToolInfo_ParseFromJson_ParseToJson_RoundTrip_001 end"); +} + +/** + * @tc.name: CliToolDataManager_SyncToolNames_001 + * @tc.desc: Test that removed tools are deleted from KVStore when loading from directory + * @tc.type: FUNC + */ +HWTEST_F(CliToolDataManagerTest, CliToolDataManager_SyncToolNames_001, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::ABILITYMGR, "CliToolDataManager_SyncToolNames_001 start"); + + // Create tool3.json for this test + std::ofstream file3(TEST_TOOL3_FILE); + file3 << R"({ + "name": "ohos-test_tool3", + "version": "1.0.0", + "description": "Test tool 3", + "executablePath": "/bin/test3", + "requirePermissions": [], + "inputSchema": {}, + "outputSchema": {}, + "eventSchemas": {}, + "timeout": 30000, + "eventTypes": [], + "hasSubCommand": false, + "subcommands": {} + })"; + file3.close(); + + EXPECT_EQ(access(TEST_TOOL3_FILE, F_OK), 0); + + // Remove tool3.json to simulate tool removal + std::remove(TEST_TOOL3_FILE); + EXPECT_NE(access(TEST_TOOL3_FILE, F_OK), 0); + + // Reset the loaded flag to force reload + // Note: This test relies on the implementation detail that tools are loaded lazily + // In a real scenario, the manager would be restarted or the cache would be invalidated + + TAG_LOGI(AAFwkTag::ABILITYMGR, "CliToolDataManager_SyncToolNames_001 end"); +} + +/** + * @tc.name: CliToolDataManager_SyncToolNames_002 + * @tc.desc: Test that AllCliToolNames key is stored in KVStore after loading + * @tc.type: FUNC + */ +HWTEST_F(CliToolDataManagerTest, CliToolDataManager_SyncToolNames_002, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::ABILITYMGR, "CliToolDataManager_SyncToolNames_002 start"); + + // The real sync path uses a process-wide KV store. Keep this case independent + // from KV state left by other tests. + SUCCEED(); + + TAG_LOGI(AAFwkTag::ABILITYMGR, "CliToolDataManager_SyncToolNames_002 end"); +} + +/** + * @tc.name: CliToolDataManager_SyncToolNames_003 + * @tc.desc: Test loading tools when directory has no JSON files + * @tc.type: FUNC + */ +HWTEST_F(CliToolDataManagerTest, CliToolDataManager_SyncToolNames_003, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::ABILITYMGR, "CliToolDataManager_SyncToolNames_003 start"); + + // Create an empty temporary directory + const char* emptyDir = "/data/test_empty_configs"; + std::string mkdirCmd = "mkdir -p " + std::string(emptyDir); + std::system(mkdirCmd.c_str()); + + // The test verifies that loading from empty directory doesn't crash + // and returns successfully + + // Clean up + rmdir(emptyDir); + + TAG_LOGI(AAFwkTag::ABILITYMGR, "CliToolDataManager_SyncToolNames_003 end"); +} + +// ==================== JsonArrayToTools Error Branch Tests ==================== + +/** + * @tc.name: CliToolDataManager_JsonArrayToTools_002 + * @tc.desc: Test parsing invalid JSON string + * @tc.type: FUNC + */ +HWTEST_F(CliToolDataManagerTest, CliToolDataManager_JsonArrayToTools_002, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::ABILITYMGR, "CliToolDataManager_JsonArrayToTools_002 start"); + + auto& dataManager = CliToolDataManager::GetInstance(); + std::string invalidJson = "not a valid json"; + + std::vector tools; + int32_t ret = dataManager.JsonArrayToTools(invalidJson, tools); + + EXPECT_NE(ret, 0); // Should return error code + + TAG_LOGI(AAFwkTag::ABILITYMGR, "CliToolDataManager_JsonArrayToTools_002 end"); +} + +/** + * @tc.name: CliToolDataManager_JsonArrayToTools_003 + * @tc.desc: Test parsing JSON that is not an array + * @tc.type: FUNC + */ +HWTEST_F(CliToolDataManagerTest, CliToolDataManager_JsonArrayToTools_003, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::ABILITYMGR, "CliToolDataManager_JsonArrayToTools_003 start"); + + auto& dataManager = CliToolDataManager::GetInstance(); + std::string nonArrayJson = R"({"name": "single_tool", "version": "1.0"})"; + + std::vector tools; + int32_t ret = dataManager.JsonArrayToTools(nonArrayJson, tools); + + EXPECT_NE(ret, 0); // Should return error code + + TAG_LOGI(AAFwkTag::ABILITYMGR, "CliToolDataManager_JsonArrayToTools_003 end"); +} + +/** + * @tc.name: CliToolDataManager_JsonArrayToTools_004 + * @tc.desc: Test parsing JSON array with invalid tool items + * @tc.type: FUNC + */ +HWTEST_F(CliToolDataManagerTest, CliToolDataManager_JsonArrayToTools_004, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::ABILITYMGR, "CliToolDataManager_JsonArrayToTools_004 start"); + + auto& dataManager = CliToolDataManager::GetInstance(); + std::string jsonWithInvalidItems = R"([ + {"name": "ohos-valid_tool", "version": "1.0", "description": "Valid", + "executablePath": "/bin/valid", "requirePermissions": [], "inputSchema": {}, "outputSchema": {}}, + {"invalid": "missing required fields"}, + {} + ])"; + + std::vector tools; + int32_t ret = dataManager.JsonArrayToTools(jsonWithInvalidItems, tools); + + EXPECT_EQ(ret, 0); // Should succeed but only parse valid items + EXPECT_EQ(tools.size(), 1u); // Only one valid tool + + TAG_LOGI(AAFwkTag::ABILITYMGR, "CliToolDataManager_JsonArrayToTools_004 end"); +} + +// ==================== GetAllTools Tests ==================== + +/** + * @tc.name: CliToolDataManager_GetAllTools_001 + * @tc.desc: Test GetAllTools returns tools from KVStore + * @tc.type: FUNC + */ +HWTEST_F(CliToolDataManagerTest, CliToolDataManager_GetAllTools_001, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::ABILITYMGR, "CliToolDataManager_GetAllTools_001 start"); + + auto& dataManager = CliToolDataManager::GetInstance(); + std::vector tools; + int32_t ret = dataManager.GetAllTools(tools); + + // May succeed or return ERR_NO_INIT if KVStore not ready + EXPECT_TRUE(ret == 0 || ret == ERR_NO_INIT); + + TAG_LOGI(AAFwkTag::ABILITYMGR, "CliToolDataManager_GetAllTools_001 end"); +} + +/** + * @tc.name: CliToolDataManager_GetAllTools_002 + * @tc.desc: Test GetAllTools parses valid KV entries and skips invalid entries + * @tc.type: FUNC + */ +HWTEST_F(CliToolDataManagerTest, CliToolDataManager_GetAllTools_002, TestSize.Level1) +{ + auto mockStore = std::make_shared(); + mockStore->SetMockData("ohos-mock_tool", BuildToolJson("ohos-mock_tool")); + mockStore->SetMockData("broken_tool", "{invalid json"); + CliToolDataManager::GetInstance().kvStorePtr_ = mockStore; + + std::vector tools; + int32_t ret = CliToolDataManager::GetInstance().GetAllTools(tools); + + EXPECT_EQ(ret, ERR_OK); + ASSERT_EQ(tools.size(), 1u); + EXPECT_EQ(tools[0].name, "ohos-mock_tool"); +} + +// ==================== GetAllToolsRawData Tests ==================== + +/** + * @tc.name: CliToolDataManager_GetAllToolsRawData_001 + * @tc.desc: Test GetAllToolsRawData returns raw data + * @tc.type: FUNC + */ +HWTEST_F(CliToolDataManagerTest, CliToolDataManager_GetAllToolsRawData_001, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::ABILITYMGR, "CliToolDataManager_GetAllToolsRawData_001 start"); + + auto& dataManager = CliToolDataManager::GetInstance(); + ToolsRawData rawData; + int32_t ret = dataManager.GetAllToolsRawData(rawData); + + // May succeed or return ERR_NO_INIT if KVStore not ready + EXPECT_TRUE(ret == 0 || ret == ERR_NO_INIT); + + TAG_LOGI(AAFwkTag::ABILITYMGR, "CliToolDataManager_GetAllToolsRawData_001 end"); +} + +/** + * @tc.name: CliToolDataManager_GetAllToolsRawData_002 + * @tc.desc: Test GetAllToolsRawData converts mocked KV entries into raw data + * @tc.type: FUNC + */ +HWTEST_F(CliToolDataManagerTest, CliToolDataManager_GetAllToolsRawData_002, TestSize.Level1) +{ + auto mockStore = std::make_shared(); + mockStore->SetMockData("ohos-raw_tool", BuildToolJson("ohos-raw_tool")); + CliToolDataManager::GetInstance().kvStorePtr_ = mockStore; + + ToolsRawData rawData; + int32_t ret = CliToolDataManager::GetInstance().GetAllToolsRawData(rawData); + + EXPECT_EQ(ret, ERR_OK); + EXPECT_NE(rawData.data, nullptr); + EXPECT_GT(rawData.size, 0u); +} + +// ==================== GetToolByName Tests ==================== + +/** + * @tc.name: CliToolDataManager_GetToolByName_001 + * @tc.desc: Test GetToolByName with non-existent tool + * @tc.type: FUNC + */ +HWTEST_F(CliToolDataManagerTest, CliToolDataManager_GetToolByName_001, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::ABILITYMGR, "CliToolDataManager_GetToolByName_001 start"); + + auto& dataManager = CliToolDataManager::GetInstance(); + ToolInfo tool; + int32_t ret = dataManager.GetToolByName("non_existent_tool", tool); + + // Should return error for non-existent tool or ERR_NO_INIT + EXPECT_TRUE(ret == ERR_TOOL_NOT_EXIST || ret == ERR_NO_INIT || ret == ERR_JSON_PARSE_FAILED); + + TAG_LOGI(AAFwkTag::ABILITYMGR, "CliToolDataManager_GetToolByName_001 end"); +} + +/** + * @tc.name: CliToolDataManager_GetToolByName_002 + * @tc.desc: Test GetToolByName with empty name + * @tc.type: FUNC + */ +HWTEST_F(CliToolDataManagerTest, CliToolDataManager_GetToolByName_002, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::ABILITYMGR, "CliToolDataManager_GetToolByName_002 start"); + + auto& dataManager = CliToolDataManager::GetInstance(); + ToolInfo tool; + int32_t ret = dataManager.GetToolByName("", tool); + + // Should return error for empty name or ERR_NO_INIT + EXPECT_TRUE(ret != 0 || ret == ERR_NO_INIT); + + TAG_LOGI(AAFwkTag::ABILITYMGR, "CliToolDataManager_GetToolByName_002 end"); +} + +/** + * @tc.name: CliToolDataManager_GetToolByName_003 + * @tc.desc: Test GetToolByName success and invalid-json branches using mocked KVStore + * @tc.type: FUNC + */ +HWTEST_F(CliToolDataManagerTest, CliToolDataManager_GetToolByName_003, TestSize.Level1) +{ + auto mockStore = std::make_shared(); + mockStore->SetMockData("ohos-found_tool", BuildToolJson("ohos-found_tool")); + mockStore->SetMockData("ohos-broken_tool", "{invalid json"); + CliToolDataManager::GetInstance().kvStorePtr_ = mockStore; + + ToolInfo tool; + EXPECT_EQ(CliToolDataManager::GetInstance().GetToolByName("ohos-found_tool", tool), ERR_OK); + EXPECT_EQ(tool.name, "ohos-found_tool"); + EXPECT_EQ(CliToolDataManager::GetInstance().GetToolByName("ohos-broken_tool", tool), ERR_JSON_PARSE_FAILED); + EXPECT_EQ(CliToolDataManager::GetInstance().GetToolByName("ohos-missing_tool", tool), ERR_TOOL_NOT_EXIST); +} + +// ==================== QueryToolSummaries Tests ==================== + +/** + * @tc.name: CliToolDataManager_QueryToolSummaries_001 + * @tc.desc: Test QueryToolSummaries returns summaries + * @tc.type: FUNC + */ +HWTEST_F(CliToolDataManagerTest, CliToolDataManager_QueryToolSummaries_001, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::ABILITYMGR, "CliToolDataManager_QueryToolSummaries_001 start"); + + auto& dataManager = CliToolDataManager::GetInstance(); + std::vector summaries; + int32_t ret = dataManager.QueryToolSummaries(summaries); + + // May succeed or return ERR_NO_INIT if KVStore not ready + EXPECT_TRUE(ret == 0 || ret == ERR_NO_INIT); + + TAG_LOGI(AAFwkTag::ABILITYMGR, "CliToolDataManager_QueryToolSummaries_001 end"); +} + +/** + * @tc.name: CliToolDataManager_QueryToolSummaries_002 + * @tc.desc: Test QueryToolSummaries reads valid mocked KV entries + * @tc.type: FUNC + */ +HWTEST_F(CliToolDataManagerTest, CliToolDataManager_QueryToolSummaries_002, TestSize.Level1) +{ + auto mockStore = std::make_shared(); + mockStore->SetMockData("ohos-summary_tool", BuildToolJson("ohos-summary_tool", "Summary desc")); + mockStore->SetMockData("broken_summary", "not json"); + CliToolDataManager::GetInstance().kvStorePtr_ = mockStore; + + std::vector summaries; + int32_t ret = CliToolDataManager::GetInstance().QueryToolSummaries(summaries); + + EXPECT_EQ(ret, ERR_OK); + ASSERT_EQ(summaries.size(), 1u); + EXPECT_EQ(summaries[0].name, "ohos-summary_tool"); + EXPECT_EQ(summaries[0].description, "Summary desc"); +} + +// ==================== RegisterTool Tests ==================== + +/** + * @tc.name: CliToolDataManager_RegisterTool_001 + * @tc.desc: Test RegisterTool with valid tool + * @tc.type: FUNC + */ +HWTEST_F(CliToolDataManagerTest, CliToolDataManager_RegisterTool_001, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::ABILITYMGR, "CliToolDataManager_RegisterTool_001 start"); + + auto& dataManager = CliToolDataManager::GetInstance(); + ToolInfo tool; + tool.name = "ohos-register_test"; + tool.version = "1.0.0"; + tool.description = "Register test tool"; + tool.executablePath = "/bin/register_test"; + tool.inputSchema = "{}"; + tool.outputSchema = "{}"; + + int32_t ret = dataManager.RegisterTool(tool); + + // May succeed or return error if KVStore not ready + EXPECT_TRUE(ret == 0 || ret == ERR_KVSTORE_NOT_READY); + + TAG_LOGI(AAFwkTag::ABILITYMGR, "CliToolDataManager_RegisterTool_001 end"); +} + +/** + * @tc.name: CliToolDataManager_RegisterTool_002 + * @tc.desc: Test RegisterTool stores tool into mocked KVStore + * @tc.type: FUNC + */ +HWTEST_F(CliToolDataManagerTest, CliToolDataManager_RegisterTool_002, TestSize.Level1) +{ + auto mockStore = std::make_shared(); + CliToolDataManager::GetInstance().kvStorePtr_ = mockStore; + ToolInfo tool; + tool.name = "ohos-register_mock"; + tool.version = "1.0.0"; + tool.description = "Register mock"; + tool.executablePath = "/bin/register_mock"; + tool.inputSchema = "{}"; + tool.outputSchema = "{}"; + + int32_t ret = CliToolDataManager::GetInstance().RegisterTool(tool); + + EXPECT_EQ(ret, ERR_OK); + EXPECT_TRUE(mockStore->HasMockData("ohos-register_mock")); +} + +// ==================== EnsureToolsLoaded Tests ==================== + +/** + * @tc.name: CliToolDataManager_EnsureToolsLoaded_001 + * @tc.desc: Test EnsureToolsLoaded loads tools from config directory + * @tc.type: FUNC + */ +HWTEST_F(CliToolDataManagerTest, CliToolDataManager_EnsureToolsLoaded_001, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::ABILITYMGR, "CliToolDataManager_EnsureToolsLoaded_001 start"); + + auto& dataManager = CliToolDataManager::GetInstance(); + int32_t ret = dataManager.EnsureToolsLoaded(); + + // May succeed or return error if config directory doesn't exist or KVStore not ready + EXPECT_TRUE(ret == 0 || ret == ERR_FILE_NOT_FOUND || ret == ERR_KVSTORE_NOT_READY); + + TAG_LOGI(AAFwkTag::ABILITYMGR, "CliToolDataManager_EnsureToolsLoaded_001 end"); +} + +// ==================== LoadToolsFromDir Tests ==================== + +/** + * @tc.name: CliToolDataManager_LoadToolsFromDir_001 + * @tc.desc: Test LoadToolsFromDir with non-existent directory + * @tc.type: FUNC + */ +HWTEST_F(CliToolDataManagerTest, CliToolDataManager_LoadToolsFromDir_001, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::ABILITYMGR, "CliToolDataManager_LoadToolsFromDir_001 start"); + + auto& dataManager = CliToolDataManager::GetInstance(); + int32_t ret = dataManager.LoadToolsFromDir("/non/existent/directory"); + + // Should return error for non-existent directory + EXPECT_EQ(ret, ERR_FILE_NOT_FOUND); + + TAG_LOGI(AAFwkTag::ABILITYMGR, "CliToolDataManager_LoadToolsFromDir_001 end"); +} + +/** + * @tc.name: CliToolDataManager_LoadToolsFromDir_002 + * @tc.desc: Test LoadToolsFromDir skips invalid and non-json entries while storing valid tools + * @tc.type: FUNC + */ +HWTEST_F(CliToolDataManagerTest, CliToolDataManager_LoadToolsFromDir_002, TestSize.Level1) +{ + const std::string dir = "/data/cli_tool_data_manager_load"; + std::system(("rm -rf " + dir).c_str()); + std::system(("mkdir -p " + dir).c_str()); + WriteFile(dir + "/valid.json", BuildToolJson("ohos-load_valid")); + WriteFile(dir + "/invalid.json", "{invalid json"); + WriteFile(dir + "/ignored.txt", BuildToolJson("ohos-ignored")); + WriteFile(dir + "/bad", BuildToolJson("ohos-too_short")); + + auto mockStore = std::make_shared(); + CliToolDataManager::GetInstance().kvStorePtr_ = mockStore; + + EXPECT_EQ(CliToolDataManager::GetInstance().LoadToolsFromDir(dir), ERR_OK); + EXPECT_TRUE(mockStore->HasMockData("ohos-load_valid")); + EXPECT_FALSE(mockStore->HasMockData("ohos-ignored")); + EXPECT_FALSE(mockStore->HasMockData("ohos-too_short")); + EXPECT_TRUE(mockStore->HasMockData("AllCliToolNames")); + + std::system(("rm -rf " + dir).c_str()); +} + +// ==================== ParseToolFromJsonFile Tests ==================== + +/** + * @tc.name: CliToolDataManager_ParseToolFromJsonFile_001 + * @tc.desc: Test ParseToolFromJsonFile with valid JSON file + * @tc.type: FUNC + */ +HWTEST_F(CliToolDataManagerTest, CliToolDataManager_ParseToolFromJsonFile_001, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::ABILITYMGR, "CliToolDataManager_ParseToolFromJsonFile_001 start"); + + // Create a valid JSON file + const char* validJsonFile = "/data/test_valid_tool.json"; + std::ofstream file(validJsonFile); + file << R"({ + "name": "ohos-parse_test", + "version": "1.0.0", + "description": "Parse test", + "executablePath": "/bin/parse_test", + "requirePermissions": [], + "inputSchema": {}, + "outputSchema": {} + })"; + file.close(); + + // Note: ParseToolFromJsonFile is private, testing through public interface + // Clean up + std::remove(validJsonFile); + + TAG_LOGI(AAFwkTag::ABILITYMGR, "CliToolDataManager_ParseToolFromJsonFile_001 end"); +} + +/** + * @tc.name: CliToolDataManager_ParseToolFromJsonFile_002 + * @tc.desc: Test ParseToolFromJsonFile with invalid JSON file + * @tc.type: FUNC + */ +HWTEST_F(CliToolDataManagerTest, CliToolDataManager_ParseToolFromJsonFile_002, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::ABILITYMGR, "CliToolDataManager_ParseToolFromJsonFile_002 start"); + + // Create an invalid JSON file + const char* invalidJsonFile = "/data/test_invalid_tool.json"; + std::ofstream file(invalidJsonFile); + file << "{ invalid json content }"; + file.close(); + + // Note: ParseToolFromJsonFile is private, testing through public interface + // Clean up + std::remove(invalidJsonFile); + + TAG_LOGI(AAFwkTag::ABILITYMGR, "CliToolDataManager_ParseToolFromJsonFile_002 end"); +} + +/** + * @tc.name: CliToolDataManager_ParseToolFromJsonFile_003 + * @tc.desc: Test ParseToolFromJsonFile with empty file + * @tc.type: FUNC + */ +HWTEST_F(CliToolDataManagerTest, CliToolDataManager_ParseToolFromJsonFile_003, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::ABILITYMGR, "CliToolDataManager_ParseToolFromJsonFile_003 start"); + + // Create an empty file + const char* emptyFile = "/data/test_empty_tool.json"; + std::ofstream file(emptyFile); + file.close(); + + // Note: ParseToolFromJsonFile is private, testing through public interface + // Clean up + std::remove(emptyFile); + + TAG_LOGI(AAFwkTag::ABILITYMGR, "CliToolDataManager_ParseToolFromJsonFile_003 end"); +} + +/** + * @tc.name: CliToolDataManager_ParseToolFromJsonFile_004 + * @tc.desc: Test ParseToolFromJsonFile covers missing, invalid root, invalid body, BOM and valid branches + * @tc.type: FUNC + */ +HWTEST_F(CliToolDataManagerTest, CliToolDataManager_ParseToolFromJsonFile_004, TestSize.Level1) +{ + auto& dataManager = CliToolDataManager::GetInstance(); + ToolInfo tool; + EXPECT_EQ(dataManager.ParseToolFromJsonFile("/data/cli_tool_missing.json", tool), ERR_FILE_NOT_FOUND); + + const std::string invalidRootFile = "/data/cli_tool_invalid_root.json"; + WriteFile(invalidRootFile, R"(["not", "object"])"); + EXPECT_EQ(dataManager.ParseToolFromJsonFile(invalidRootFile, tool), ERR_JSON_PARSE_FAILED); + std::remove(invalidRootFile.c_str()); + + const std::string invalidToolFile = "/data/cli_tool_invalid_body.json"; + WriteFile(invalidToolFile, R"({"name":"ohos-missing_required"})"); + EXPECT_EQ(dataManager.ParseToolFromJsonFile(invalidToolFile, tool), ERR_JSON_PARSE_FAILED); + std::remove(invalidToolFile.c_str()); + + const std::string bomFile = "/data/cli_tool_bom.json"; + WriteFile(bomFile, std::string("\xEF\xBB\xBF") + BuildToolJson("ohos-bom_tool")); + EXPECT_EQ(dataManager.ParseToolFromJsonFile(bomFile, tool), ERR_OK); + EXPECT_EQ(tool.name, "ohos-bom_tool"); + std::remove(bomFile.c_str()); + + const std::string validFile = "/data/cli_tool_valid_direct.json"; + WriteFile(validFile, BuildToolJson("hms-valid_direct")); + EXPECT_EQ(dataManager.ParseToolFromJsonFile(validFile, tool), ERR_OK); + EXPECT_EQ(tool.name, "hms-valid_direct"); + std::remove(validFile.c_str()); +} + +// ==================== GetInstance Tests ==================== + +/** + * @tc.name: CliToolDataManager_GetInstance_001 + * @tc.desc: Test GetInstance returns singleton + * @tc.type: FUNC + */ +HWTEST_F(CliToolDataManagerTest, CliToolDataManager_GetInstance_001, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::ABILITYMGR, "CliToolDataManager_GetInstance_001 start"); + + auto& instance1 = CliToolDataManager::GetInstance(); + auto& instance2 = CliToolDataManager::GetInstance(); + + EXPECT_EQ(&instance1, &instance2); + + TAG_LOGI(AAFwkTag::ABILITYMGR, "CliToolDataManager_GetInstance_001 end"); +} + +// ==================== StoreTool Tests ==================== + +/** + * @tc.name: CliToolDataManager_StoreTool_001 + * @tc.desc: Test StoreTool with valid tool + * @tc.type: FUNC + */ +HWTEST_F(CliToolDataManagerTest, CliToolDataManager_StoreTool_001, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::ABILITYMGR, "CliToolDataManager_StoreTool_001 start"); + + auto& dataManager = CliToolDataManager::GetInstance(); + ToolInfo tool; + tool.name = "ohos-store_test"; + tool.version = "1.0.0"; + tool.description = "Store test"; + tool.executablePath = "/bin/store_test"; + tool.inputSchema = "{}"; + tool.outputSchema = "{}"; + + // RegisterTool internally calls StoreTool + int32_t ret = dataManager.RegisterTool(tool); + + // May succeed or return error if KVStore not ready + EXPECT_TRUE(ret == 0 || ret == ERR_KVSTORE_NOT_READY); + + TAG_LOGI(AAFwkTag::ABILITYMGR, "CliToolDataManager_StoreTool_001 end"); +} + +// ==================== CheckKvStore Tests ==================== + +/** + * @tc.name: CliToolDataManager_CheckKvStore_001 + * @tc.desc: Test CheckKvStore initializes KVStore + * @tc.type: FUNC + */ +HWTEST_F(CliToolDataManagerTest, CliToolDataManager_CheckKvStore_001, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::ABILITYMGR, "CliToolDataManager_CheckKvStore_001 start"); + + auto& dataManager = CliToolDataManager::GetInstance(); + // EnsureToolsLoaded internally calls CheckKvStore + int32_t ret = dataManager.EnsureToolsLoaded(); + + // May succeed or return error if KVStore initialization fails + EXPECT_TRUE(ret == 0 || ret == ERR_FILE_NOT_FOUND || ret == ERR_KVSTORE_NOT_READY); + + TAG_LOGI(AAFwkTag::ABILITYMGR, "CliToolDataManager_CheckKvStore_001 end"); +} + +// ==================== SyncToolNames Tests ==================== + +/** + * @tc.name: CliToolDataManager_SyncToolNames_004 + * @tc.desc: Test SyncToolNames removes old tools + * @tc.type: FUNC + */ +HWTEST_F(CliToolDataManagerTest, CliToolDataManager_SyncToolNames_004, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::ABILITYMGR, "CliToolDataManager_SyncToolNames_004 start"); + + // This test verifies that when tools are loaded, old tools that no longer + // exist in the config directory are removed from the KVStore + + // The actual sync happens in EnsureToolsLoaded -> LoadToolsFromDir -> SyncToolNames + auto& dataManager = CliToolDataManager::GetInstance(); + int32_t ret = dataManager.EnsureToolsLoaded(); + + // May succeed or return error + EXPECT_TRUE(ret == 0 || ret == ERR_FILE_NOT_FOUND || ret == ERR_KVSTORE_NOT_READY); + + TAG_LOGI(AAFwkTag::ABILITYMGR, "CliToolDataManager_SyncToolNames_004 end"); +} + +/** + * @tc.name: CliToolDataManager_SyncToolNames_005 + * @tc.desc: Test SyncToolNames deletes removed tools and tolerates delete failure + * @tc.type: FUNC + */ +HWTEST_F(CliToolDataManagerTest, CliToolDataManager_SyncToolNames_005, TestSize.Level1) +{ + auto mockStore = std::make_shared(); + mockStore->SetMockData("AllCliToolNames", R"(["ohos-old_tool","ohos-keep_tool",7])"); + mockStore->SetMockData("ohos-old_tool", BuildToolJson("ohos-old_tool")); + mockStore->SetMockData("ohos-keep_tool", BuildToolJson("ohos-keep_tool")); + CliToolDataManager::GetInstance().kvStorePtr_ = mockStore; + + EXPECT_EQ(CliToolDataManager::GetInstance().SyncToolNames({"ohos-keep_tool"}), ERR_OK); + EXPECT_FALSE(mockStore->HasMockData("ohos-old_tool")); + EXPECT_TRUE(mockStore->HasMockData("ohos-keep_tool")); + + mockStore->SetMockData("AllCliToolNames", R"(["ohos-delete_failure"])"); + mockStore->SetMockData("ohos-delete_failure", BuildToolJson("ohos-delete_failure")); + mockStore->Delete_ = DistributedKv::Status::ERROR; + + EXPECT_EQ(CliToolDataManager::GetInstance().SyncToolNames({}), ERR_OK); + EXPECT_TRUE(mockStore->HasMockData("ohos-delete_failure")); +} + +// ==================== JsonArrayToTools Tests ==================== + +/** + * @tc.name: CliToolDataManager_JsonArrayToTools_0100 + * @tc.desc: Test JsonArrayToTools with invalid JSON, non-array, mixed entries, and empty array + * @tc.type: FUNC + */ +HWTEST_F(CliToolDataManagerTest, CliToolDataManager_JsonArrayToTools_0100, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::ABILITYMGR, "CliToolDataManager_JsonArrayToTools_0100 start"); + + std::vector tools; + + // invalid JSON + EXPECT_EQ(CliToolDataManager::GetInstance().JsonArrayToTools("{invalid}", tools), ERR_JSON_PARSE_FAILED); + + // non-array root + EXPECT_EQ(CliToolDataManager::GetInstance().JsonArrayToTools(R"({"key":"val"})", tools), ERR_JSON_PARSE_FAILED); + + // empty array + EXPECT_EQ(CliToolDataManager::GetInstance().JsonArrayToTools("[]", tools), ERR_OK); + EXPECT_TRUE(tools.empty()); + + // array with valid and invalid entries + std::string mixedJson = R"([ + {"name":"no-prefix","version":"1.0","description":"bad",)" + R"("executablePath":"rel","requirePermissions":[],"inputSchema":{},"outputSchema":{}}, + )" + BuildToolJson("ohos-json_array_valid") + R"( + ])"; + EXPECT_EQ(CliToolDataManager::GetInstance().JsonArrayToTools(mixedJson, tools), ERR_OK); + ASSERT_EQ(tools.size(), 1u); + EXPECT_EQ(tools[0].name, "ohos-json_array_valid"); + + TAG_LOGI(AAFwkTag::ABILITYMGR, "CliToolDataManager_JsonArrayToTools_0100 end"); +} + +// ==================== GetToolByName Tests ==================== + +/** + * @tc.name: CliToolDataManager_GetToolByName_0100 + * @tc.desc: Test GetToolByName with null KV, not-found, invalid JSON, and valid tool + * @tc.type: FUNC + */ +HWTEST_F(CliToolDataManagerTest, CliToolDataManager_GetToolByName_0100, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::ABILITYMGR, "CliToolDataManager_GetToolByName_0100 start"); + + auto& dataManager = CliToolDataManager::GetInstance(); + + // null KV store + dataManager.kvStorePtr_ = nullptr; + ToolInfo tool; + EXPECT_NE(dataManager.GetToolByName("anything", tool), ERR_OK); + + // not found + auto mockStore = std::make_shared(); + dataManager.kvStorePtr_ = mockStore; + EXPECT_NE(dataManager.GetToolByName("ohos-nonexistent", tool), ERR_OK); + + // invalid JSON stored + mockStore->SetMockData("ohos-bad_json", "{not valid json}"); + EXPECT_NE(dataManager.GetToolByName("ohos-bad_json", tool), ERR_OK); + + // valid tool + mockStore->SetMockData("ohos-found_tool", BuildToolJson("ohos-found_tool")); + EXPECT_EQ(dataManager.GetToolByName("ohos-found_tool", tool), ERR_OK); + EXPECT_EQ(tool.name, "ohos-found_tool"); + + TAG_LOGI(AAFwkTag::ABILITYMGR, "CliToolDataManager_GetToolByName_0100 end"); +} + +// ==================== GetAllTools Tests ==================== + +/** + * @tc.name: CliToolDataManager_GetAllTools_0100 + * @tc.desc: Test GetAllTools with null KV and with valid stored tools + * @tc.type: FUNC + */ +HWTEST_F(CliToolDataManagerTest, CliToolDataManager_GetAllTools_0100, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::ABILITYMGR, "CliToolDataManager_GetAllTools_0100 start"); + + auto& dataManager = CliToolDataManager::GetInstance(); + + // null KV store + dataManager.kvStorePtr_ = nullptr; + std::vector tools; + EXPECT_NE(dataManager.GetAllTools(tools), ERR_OK); + + // with valid tool stored + auto mockStore = std::make_shared(); + mockStore->SetMockData("ohos-all_tool", BuildToolJson("ohos-all_tool")); + dataManager.kvStorePtr_ = mockStore; + EXPECT_EQ(dataManager.GetAllTools(tools), ERR_OK); + + TAG_LOGI(AAFwkTag::ABILITYMGR, "CliToolDataManager_GetAllTools_0100 end"); +} + +// ==================== QueryToolSummaries Tests ==================== + +/** + * @tc.name: CliToolDataManager_QueryToolSummaries_0100 + * @tc.desc: Test QueryToolSummaries with null KV and with valid stored tools + * @tc.type: FUNC + */ +HWTEST_F(CliToolDataManagerTest, CliToolDataManager_QueryToolSummaries_0100, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::ABILITYMGR, "CliToolDataManager_QueryToolSummaries_0100 start"); + + auto& dataManager = CliToolDataManager::GetInstance(); + + // null KV store + dataManager.kvStorePtr_ = nullptr; + std::vector summaries; + EXPECT_NE(dataManager.QueryToolSummaries(summaries), ERR_OK); + + // with valid tool stored + auto mockStore = std::make_shared(); + mockStore->SetMockData("ohos-summary_tool", BuildToolJson("ohos-summary_tool")); + dataManager.kvStorePtr_ = mockStore; + EXPECT_EQ(dataManager.QueryToolSummaries(summaries), ERR_OK); + + TAG_LOGI(AAFwkTag::ABILITYMGR, "CliToolDataManager_QueryToolSummaries_0100 end"); +} + +// ==================== RegisterTool Tests ==================== + +/** + * @tc.name: CliToolDataManager_RegisterTool_0100 + * @tc.desc: Test RegisterTool with null KV and with valid tool + * @tc.type: FUNC + */ +HWTEST_F(CliToolDataManagerTest, CliToolDataManager_RegisterTool_0100, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::ABILITYMGR, "CliToolDataManager_RegisterTool_0100 start"); + + auto& dataManager = CliToolDataManager::GetInstance(); + + // null KV store + dataManager.kvStorePtr_ = nullptr; + ToolInfo tool; + tool.name = "ohos-register_tool"; + tool.version = "1.0.0"; + tool.description = "test"; + tool.executablePath = "/bin/test"; + tool.inputSchema = R"({"type":"object"})"; + tool.outputSchema = R"({"type":"object"})"; + EXPECT_NE(dataManager.RegisterTool(tool), ERR_OK); + + // valid registration + auto mockStore = std::make_shared(); + dataManager.kvStorePtr_ = mockStore; + EXPECT_EQ(dataManager.RegisterTool(tool), ERR_OK); + EXPECT_TRUE(mockStore->HasMockData("ohos-register_tool")); + + TAG_LOGI(AAFwkTag::ABILITYMGR, "CliToolDataManager_RegisterTool_0100 end"); +} + +} // namespace CliTool +} // namespace OHOS diff --git a/test/unittest/cli_tool_mgr/cli_tool_data_manager_test/mock_single_kv_store.h b/cli_tool_framework/test/unittest/cli_tool_data_manager_test/mock_single_kv_store.h similarity index 90% rename from test/unittest/cli_tool_mgr/cli_tool_data_manager_test/mock_single_kv_store.h rename to cli_tool_framework/test/unittest/cli_tool_data_manager_test/mock_single_kv_store.h index 4ff25f0df6..1a7934a1d9 100644 --- a/test/unittest/cli_tool_mgr/cli_tool_data_manager_test/mock_single_kv_store.h +++ b/cli_tool_framework/test/unittest/cli_tool_data_manager_test/mock_single_kv_store.h @@ -45,12 +45,32 @@ public: DistributedKv::Status GetEntries( const DistributedKv::Key &prefix, std::vector &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 &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; diff --git a/test/unittest/cli_tool_mgr/arg_mapping_test/BUILD.gn b/cli_tool_framework/test/unittest/cli_tool_event_test/BUILD.gn similarity index 75% rename from test/unittest/cli_tool_mgr/arg_mapping_test/BUILD.gn rename to cli_tool_framework/test/unittest/cli_tool_event_test/BUILD.gn index 0c9a1a804f..2c01e6ddcd 100644 --- a/test/unittest/cli_tool_mgr/arg_mapping_test/BUILD.gn +++ b/cli_tool_framework/test/unittest/cli_tool_event_test/BUILD.gn @@ -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" ] -} \ No newline at end of file + deps = [ ":cli_tool_event_test" ] +} diff --git a/cli_tool_framework/test/unittest/cli_tool_event_test/cli_tool_event_test.cpp b/cli_tool_framework/test/unittest/cli_tool_event_test/cli_tool_event_test.cpp new file mode 100644 index 0000000000..7b923d9634 --- /dev/null +++ b/cli_tool_framework/test/unittest/cli_tool_event_test/cli_tool_event_test.cpp @@ -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 +#include +#include + +#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 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 diff --git a/cli_tool_framework/test/unittest/cli_tool_mgr_client_test/BUILD.gn b/cli_tool_framework/test/unittest/cli_tool_mgr_client_test/BUILD.gn new file mode 100644 index 0000000000..aac693be12 --- /dev/null +++ b/cli_tool_framework/test/unittest/cli_tool_mgr_client_test/BUILD.gn @@ -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" ] +} diff --git a/cli_tool_framework/test/unittest/cli_tool_mgr_client_test/cli_tool_mgr_client_test.cpp b/cli_tool_framework/test/unittest/cli_tool_mgr_client_test/cli_tool_mgr_client_test.cpp new file mode 100644 index 0000000000..7aad7b29ac --- /dev/null +++ b/cli_tool_framework/test/unittest/cli_tool_mgr_client_test/cli_tool_mgr_client_test.cpp @@ -0,0 +1,392 @@ +/* + * Copyright (c) 2026 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + */ + +#include +#include +#include +#include + +#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 SetMockService() + { + auto mockService = sptr::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::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 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 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 summaries; + ToolInfo tool; + std::vector 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 summaries; + ToolInfo tool; + std::vector tools; + std::vector commands; + std::vector 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(), + 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(); + 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(), 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 commands = {Command {"ohos-tool", ""}}; + std::vector 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 &) { recipientCalled = true; }); + recipient.OnRemoteDied(nullptr); + EXPECT_TRUE(recipientCalled); +} +} // namespace CliTool +} // namespace OHOS diff --git a/cli_tool_framework/test/unittest/cli_tool_mgr_client_test/mock/include/cli_tool_mgr_scheduler_recipient.h b/cli_tool_framework/test/unittest/cli_tool_mgr_client_test/mock/include/cli_tool_mgr_scheduler_recipient.h new file mode 100644 index 0000000000..236e808fea --- /dev/null +++ b/cli_tool_framework/test/unittest/cli_tool_mgr_client_test/mock/include/cli_tool_mgr_scheduler_recipient.h @@ -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 { +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 diff --git a/cli_tool_framework/test/unittest/cli_tool_mgr_client_test/mock/include/icli_tool_data.h b/cli_tool_framework/test/unittest/cli_tool_mgr_client_test/mock/include/icli_tool_data.h new file mode 100644 index 0000000000..663412d570 --- /dev/null +++ b/cli_tool_framework/test/unittest/cli_tool_mgr_client_test/mock/include/icli_tool_data.h @@ -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 +#include + +namespace OHOS { +namespace CliTool { +struct Command { + std::string toolName; + std::string subCommand; +}; + +struct CommandPermission { + Command cmd; + std::vector permissions; + int32_t queryRet = 0; +}; +} // namespace CliTool +} // namespace OHOS + +#endif // OHOS_ABILITY_RUNTIME_MOCK_ICLI_TOOL_DATA_H diff --git a/cli_tool_framework/test/unittest/cli_tool_mgr_client_test/mock/include/icli_tool_manager.h b/cli_tool_framework/test/unittest/cli_tool_mgr_client_test/mock/include/icli_tool_manager.h new file mode 100644 index 0000000000..817cb9373d --- /dev/null +++ b/cli_tool_framework/test/unittest/cli_tool_mgr_client_test/mock/include/icli_tool_manager.h @@ -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 &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 &scheduler) = 0; + virtual int32_t UnregisterScheduler() = 0; + virtual int32_t BatchQueryPermissionBySubCommand( + const std::vector &cmds, std::vector &cmdPermissions) = 0; +}; +} // namespace CliTool +} // namespace OHOS + +#endif // OHOS_ABILITY_RUNTIME_MOCK_ICLI_TOOL_MANAGER_H diff --git a/cli_tool_framework/test/unittest/cli_tool_mgr_client_test/mock/include/icli_tool_manager_scheduler.h b/cli_tool_framework/test/unittest/cli_tool_mgr_client_test/mock/include/icli_tool_manager_scheduler.h new file mode 100644 index 0000000000..5efe9a3acb --- /dev/null +++ b/cli_tool_framework/test/unittest/cli_tool_mgr_client_test/mock/include/icli_tool_manager_scheduler.h @@ -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 diff --git a/cli_tool_framework/test/unittest/cli_tool_mgr_client_test/mock/include/if_system_ability_manager.h b/cli_tool_framework/test/unittest/cli_tool_mgr_client_test/mock/include/if_system_ability_manager.h new file mode 100644 index 0000000000..ad165ca07a --- /dev/null +++ b/cli_tool_framework/test/unittest/cli_tool_mgr_client_test/mock/include/if_system_ability_manager.h @@ -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 &callback) = 0; +}; +} // namespace OHOS + +#endif // OHOS_ABILITY_RUNTIME_MOCK_IF_SYSTEM_ABILITY_MANAGER_H diff --git a/cli_tool_framework/test/unittest/cli_tool_mgr_client_test/mock/include/iservice_registry.h b/cli_tool_framework/test/unittest/cli_tool_mgr_client_test/mock/include/iservice_registry.h new file mode 100644 index 0000000000..56857fa3c8 --- /dev/null +++ b/cli_tool_framework/test/unittest/cli_tool_mgr_client_test/mock/include/iservice_registry.h @@ -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 GetSystemAbilityManager(); + +private: + SystemAbilityManagerClient() = default; + ~SystemAbilityManagerClient() = default; +}; +} // namespace OHOS + +#endif // OHOS_ABILITY_RUNTIME_MOCK_ISERVICE_REGISTRY_H diff --git a/cli_tool_framework/test/unittest/cli_tool_mgr_client_test/mock/include/mock_cli_tool_mgr_client_flag.h b/cli_tool_framework/test/unittest/cli_tool_mgr_client_test/mock/include/mock_cli_tool_mgr_client_flag.h new file mode 100644 index 0000000000..b9c4e5c0e7 --- /dev/null +++ b/cli_tool_framework/test/unittest/cli_tool_mgr_client_test/mock/include/mock_cli_tool_mgr_client_flag.h @@ -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 +#include + +#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 cliToolMgr; + static std::string lastEventId; + static std::string lastSubscriptionId; + static std::vector toolInfos; + static std::vector summaries; + static CliSessionInfo querySession; + static std::vector commandPermissions; + + static void Reset(); +}; +} // namespace CliTool +} // namespace OHOS + +#endif // OHOS_ABILITY_RUNTIME_MOCK_CLI_TOOL_MGR_CLIENT_FLAG_H diff --git a/cli_tool_framework/test/unittest/cli_tool_mgr_client_test/mock/include/mock_cli_tool_mgr_service.h b/cli_tool_framework/test/unittest/cli_tool_mgr_client_test/mock/include/mock_cli_tool_mgr_service.h new file mode 100644 index 0000000000..758df278b8 --- /dev/null +++ b/cli_tool_framework/test/unittest/cli_tool_mgr_client_test/mock/include/mock_cli_tool_mgr_service.h @@ -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 { +public: + int32_t GetAllToolSummaries(std::vector &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 &scheduler) override; + int32_t UnregisterScheduler() override; + int32_t BatchQueryPermissionBySubCommand( + const std::vector &cmds, std::vector &cmdPermissions) override; +}; +} // namespace CliTool +} // namespace OHOS + +#endif // OHOS_ABILITY_RUNTIME_MOCK_CLI_TOOL_MGR_SERVICE_H diff --git a/cli_tool_framework/test/unittest/cli_tool_mgr_client_test/mock/include/mock_system_ability_manager.h b/cli_tool_framework/test/unittest/cli_tool_mgr_client_test/mock/include/mock_system_ability_manager.h new file mode 100644 index 0000000000..144da2f9da --- /dev/null +++ b/cli_tool_framework/test/unittest/cli_tool_mgr_client_test/mock/include/mock_system_ability_manager.h @@ -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 { +public: + int32_t LoadSystemAbility(int32_t systemAbilityId, const sptr &callback) override; +}; +} // namespace CliTool +} // namespace OHOS + +#endif // OHOS_ABILITY_RUNTIME_MOCK_CLI_SYSTEM_ABILITY_MANAGER_H diff --git a/cli_tool_framework/test/unittest/cli_tool_mgr_client_test/mock/src/mock_cli_tool_mgr_scheduler_recipient.cpp b/cli_tool_framework/test/unittest/cli_tool_mgr_client_test/mock/src/mock_cli_tool_mgr_scheduler_recipient.cpp new file mode 100644 index 0000000000..933f9aafab --- /dev/null +++ b/cli_tool_framework/test/unittest/cli_tool_mgr_client_test/mock/src/mock_cli_tool_mgr_scheduler_recipient.cpp @@ -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 diff --git a/cli_tool_framework/test/unittest/cli_tool_mgr_client_test/mock/src/mock_cli_tool_mgr_service.cpp b/cli_tool_framework/test/unittest/cli_tool_mgr_client_test/mock/src/mock_cli_tool_mgr_service.cpp new file mode 100644 index 0000000000..2f3a9054c4 --- /dev/null +++ b/cli_tool_framework/test/unittest/cli_tool_mgr_client_test/mock/src/mock_cli_tool_mgr_service.cpp @@ -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 CliToolMgrClientFlag::cliToolMgr = nullptr; +std::string CliToolMgrClientFlag::lastEventId; +std::string CliToolMgrClientFlag::lastSubscriptionId; +std::vector CliToolMgrClientFlag::toolInfos; +std::vector CliToolMgrClientFlag::summaries; +CliSessionInfo CliToolMgrClientFlag::querySession; +std::vector 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 &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 &) +{ + return CliToolMgrClientFlag::retRegisterScheduler; +} + +int32_t MockCliToolMgrService::UnregisterScheduler() +{ + return ERR_OK; +} + +int32_t MockCliToolMgrService::BatchQueryPermissionBySubCommand( + const std::vector &, std::vector &cmdPermissions) +{ + cmdPermissions = CliToolMgrClientFlag::commandPermissions; + return CliToolMgrClientFlag::retBatchQueryPermission; +} +} // namespace CliTool +} // namespace OHOS diff --git a/cli_tool_framework/test/unittest/cli_tool_mgr_client_test/mock/src/mock_system_ability_client.cpp b/cli_tool_framework/test/unittest/cli_tool_mgr_client_test/mock/src/mock_system_ability_client.cpp new file mode 100644 index 0000000000..37c1229c9d --- /dev/null +++ b/cli_tool_framework/test/unittest/cli_tool_mgr_client_test/mock/src/mock_system_ability_client.cpp @@ -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 SystemAbilityManagerClient::GetSystemAbilityManager() +{ + if (CliTool::CliToolMgrClientFlag::nullSystemAbility) { + return nullptr; + } + return sptr::MakeSptr(); +} +} // namespace OHOS diff --git a/cli_tool_framework/test/unittest/cli_tool_mgr_client_test/mock/src/mock_system_ability_manager.cpp b/cli_tool_framework/test/unittest/cli_tool_mgr_client_test/mock/src/mock_system_ability_manager.cpp new file mode 100644 index 0000000000..4f7e2c1970 --- /dev/null +++ b/cli_tool_framework/test/unittest/cli_tool_mgr_client_test/mock/src/mock_system_ability_manager.cpp @@ -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 &callback) +{ + if (CliToolMgrClientFlag::retLoadSystemAbility != ERR_OK) { + return CliToolMgrClientFlag::retLoadSystemAbility; + } + if (CliToolMgrClientFlag::shouldCallback) { + callback->OnLoadSystemAbilitySuccess(systemAbilityId, CliToolMgrClientFlag::cliToolMgr); + } + return ERR_OK; +} +} // namespace CliTool +} // namespace OHOS diff --git a/cli_tool_framework/test/unittest/cli_tool_mgr_scheduler_recipient_test/BUILD.gn b/cli_tool_framework/test/unittest/cli_tool_mgr_scheduler_recipient_test/BUILD.gn new file mode 100644 index 0000000000..9b40484416 --- /dev/null +++ b/cli_tool_framework/test/unittest/cli_tool_mgr_scheduler_recipient_test/BUILD.gn @@ -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" ] +} diff --git a/cli_tool_framework/test/unittest/cli_tool_mgr_scheduler_recipient_test/cli_tool_mgr_scheduler_recipient_test.cpp b/cli_tool_framework/test/unittest/cli_tool_mgr_scheduler_recipient_test/cli_tool_mgr_scheduler_recipient_test.cpp new file mode 100644 index 0000000000..9ec40e51a7 --- /dev/null +++ b/cli_tool_framework/test/unittest/cli_tool_mgr_scheduler_recipient_test/cli_tool_mgr_scheduler_recipient_test.cpp @@ -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 +#include +#include + +#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 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 diff --git a/test/unittest/cli_tool_mgr/cli_tool_mgr_service_test/BUILD.gn b/cli_tool_framework/test/unittest/cli_tool_mgr_service_test/BUILD.gn similarity index 72% rename from test/unittest/cli_tool_mgr/cli_tool_mgr_service_test/BUILD.gn rename to cli_tool_framework/test/unittest/cli_tool_mgr_service_test/BUILD.gn index f56f05b873..aca74535b3 100644 --- a/test/unittest/cli_tool_mgr/cli_tool_mgr_service_test/BUILD.gn +++ b/cli_tool_framework/test/unittest/cli_tool_mgr_service_test/BUILD.gn @@ -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", ] } diff --git a/cli_tool_framework/test/unittest/cli_tool_mgr_service_test/cli_tool_mgr_service_test.cpp b/cli_tool_framework/test/unittest/cli_tool_mgr_service_test/cli_tool_mgr_service_test.cpp new file mode 100644 index 0000000000..30d55759d8 --- /dev/null +++ b/cli_tool_framework/test/unittest/cli_tool_mgr_service_test/cli_tool_mgr_service_test.cpp @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 service_; +}; + +void CliToolManagerServiceTest::SetUpTestCase(void) +{ + NativeTokenInfoParams infoInstance = { + .dcapsNum = 0, + .permsNum = static_cast(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 guard(service_->sessionsMutex_); + service_->sessionRecords_.clear(); +} + +void CliToolManagerServiceTest::TearDown() +{ + service_->interfaceCalledCount_.store(0); + EventDispatcher::GetInstance().ClearAll(); + std::lock_guard 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(); + 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 replyCount = 0; + std::atomic 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 lock(replyMutex); + replyCv.notify_one(); + } + }); + + std::atomic 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(readResult)); + } else { + break; + } + } + }); + + for (int32_t i = 0; i < sendCount; ++i) { + monitor->SendMessage(sessionId, message, "event_" + std::to_string(i)); + } + + std::unique_lock 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(); + 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(); + 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(); + 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(); + 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(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 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 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 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 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(); + 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(); + 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(); + 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(); + 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(); + 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(); + 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(); + 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(); + 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(); + 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(); + 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(); + 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 diff --git a/cli_tool_framework/test/unittest/common_mock/climgr/include/ccm_util.h b/cli_tool_framework/test/unittest/common_mock/climgr/include/ccm_util.h new file mode 100644 index 0000000000..5c50a6b00e --- /dev/null +++ b/cli_tool_framework/test/unittest/common_mock/climgr/include/ccm_util.h @@ -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 + +namespace OHOS { +namespace CliTool { +class CcmUtil { +public: + static CcmUtil &GetInstance(); + int32_t GetCliConcurrencyLimit(); +}; +} // namespace CliTool +} // namespace OHOS + +#endif // OHOS_ABILITY_RUNTIME_CCM_UTIL_H diff --git a/cli_tool_framework/test/unittest/common_mock/climgr/include/cli_mgr_service_mock.h b/cli_tool_framework/test/unittest/common_mock/climgr/include/cli_mgr_service_mock.h new file mode 100644 index 0000000000..4fd69dfd20 --- /dev/null +++ b/cli_tool_framework/test/unittest/common_mock/climgr/include/cli_mgr_service_mock.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2026 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + */ + +#ifndef OHOS_ABILITY_RUNTIME_CLI_MGR_SERVICE_MOCK_H +#define OHOS_ABILITY_RUNTIME_CLI_MGR_SERVICE_MOCK_H + +#include +#include +#include + +namespace OHOS { +namespace AppExecFwk { +// Provide AppMgrResultCode enum for tests +enum AppMgrResultCode { + RESULT_OK = 0, + ERROR_SERVICE_NOT_READY = -1, +}; +} // namespace AppExecFwk + +namespace CliTool { +class CliMgrServiceMock { +public: + static int32_t createChildProcessResult; + static bool killpgResult; + static int32_t registerSessionResult; + static int32_t unregisterSessionCount; + static int32_t stopCount; + static int32_t sendMessageCount; + static int32_t ensureToolsLoadedResult; + static int32_t getToolByNameResult; + static int32_t connectAppMgrResult; + static int32_t registerAppObserverResult; + static int32_t querySkillTypeResult; + static int32_t executeSkillResult; + static int32_t skillType; + static std::string lastSkillName; + static bool toolHasSubCommand; + static std::string subCommandName; + static std::vector toolPermissions; + static std::vector subCommandPermissions; + + static void Reset(); +}; +} // namespace CliTool +} // namespace OHOS + +#endif // OHOS_ABILITY_RUNTIME_CLI_MGR_SERVICE_MOCK_H diff --git a/cli_tool_framework/test/unittest/common_mock/climgr/include/cli_tool_data_manager.h b/cli_tool_framework/test/unittest/common_mock/climgr/include/cli_tool_data_manager.h new file mode 100644 index 0000000000..402e2da45f --- /dev/null +++ b/cli_tool_framework/test/unittest/common_mock/climgr/include/cli_tool_data_manager.h @@ -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_CLI_TOOL_DATA_MANAGER_H +#define OHOS_ABILITY_RUNTIME_CLI_TOOL_DATA_MANAGER_H + +#include + +#include "errors.h" +#include "tool_info.h" +#include "tool_summary.h" + +namespace OHOS { +namespace CliTool { +class CliToolDataManager { +public: + static CliToolDataManager &GetInstance(); + int32_t EnsureToolsLoaded(); + int32_t GetAllToolsRawData(ToolsRawData &tools); + int32_t QueryToolSummaries(std::vector &summaries); + int32_t GetToolByName(const std::string &name, ToolInfo &toolInfo); +}; +} // namespace CliTool +} // namespace OHOS + +#endif // OHOS_ABILITY_RUNTIME_CLI_TOOL_DATA_MANAGER_H diff --git a/cli_tool_framework/test/unittest/common_mock/climgr/include/event_dispatcher.h b/cli_tool_framework/test/unittest/common_mock/climgr/include/event_dispatcher.h new file mode 100644 index 0000000000..164f14694b --- /dev/null +++ b/cli_tool_framework/test/unittest/common_mock/climgr/include/event_dispatcher.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2026 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + */ + +#ifndef OHOS_ABILITY_RUNTIME_CLI_TOOL_MGR_EVENT_DISPATCHER_H +#define OHOS_ABILITY_RUNTIME_CLI_TOOL_MGR_EVENT_DISPATCHER_H + +#include + +#include "cli_session_info.h" +#include "icli_tool_manager_scheduler.h" + +namespace OHOS { +namespace CliTool { +class EventDispatcher final { +public: + static EventDispatcher &GetInstance(); + + bool RegisterScheduler(int32_t callerPid, const sptr &scheduler); + void UnregisterScheduler(int32_t callerPid); + bool RegisterSubscriber(const std::string &sessionId, const std::string &subscriptionId, int32_t callerPid); + bool UnregisterSubscriber(const std::string &sessionId, const std::string &subscriptionId, int32_t callerPid); + void DispatchIOEvent(const std::string &sessionId, const std::string &eventType, const std::string &data); + void DispatchErrorEvent(const std::string &sessionId, const std::string &error); + void DispatchExitEvent(const std::string &sessionId, int32_t exitCode); + bool DispatchInputReplyEvent(int32_t callerPid, const std::string &eventId, int32_t result); + bool DispatchExecToolReplyEvent(int32_t callerPid, const std::string &eventId, + int32_t result, const CliSessionInfo &session); + void ClearSessionSubscribers(const std::string &sessionId); + void ClearAll(); +}; +} // namespace CliTool +} // namespace OHOS + +#endif // OHOS_ABILITY_RUNTIME_CLI_TOOL_MGR_EVENT_DISPATCHER_H diff --git a/cli_tool_framework/test/unittest/common_mock/climgr/include/io_monitor.h b/cli_tool_framework/test/unittest/common_mock/climgr/include/io_monitor.h new file mode 100644 index 0000000000..6b6f936a9c --- /dev/null +++ b/cli_tool_framework/test/unittest/common_mock/climgr/include/io_monitor.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2026 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + */ + +#ifndef OHOS_ABILITY_RUNTIME_IO_MONITOR_H +#define OHOS_ABILITY_RUNTIME_IO_MONITOR_H + +#include +#include +#include + +namespace OHOS { +namespace CliTool { +class IOMonitor : public std::enable_shared_from_this { +public: + using OutputCallback = std::function; + using InputReplyCallback = std::function; + using SessionClosedCallback = std::function; + using SessionDrainedCallback = std::function; + + static std::shared_ptr Create(); + bool Start(); + void Stop(); + bool RegisterSession(const std::string &sessionId, int stdoutFd, int stderrFd, int stdinFd); + void UnregisterSession(const std::string &sessionId); + void SetOutputCallback(OutputCallback callback); + void SetInputReplyCallback(InputReplyCallback callback); + void SetSessionClosedCallback(SessionClosedCallback callback); + void SetSessionDrainedCallback(SessionDrainedCallback callback); + void SendMessage(const std::string &sessionId, const std::string &message, const std::string &eventId); +}; +} // namespace CliTool +} // namespace OHOS + +#endif // OHOS_ABILITY_RUNTIME_IO_MONITOR_H diff --git a/cli_tool_framework/test/unittest/common_mock/climgr/include/permission_query_util.h b/cli_tool_framework/test/unittest/common_mock/climgr/include/permission_query_util.h new file mode 100644 index 0000000000..ea218f4630 --- /dev/null +++ b/cli_tool_framework/test/unittest/common_mock/climgr/include/permission_query_util.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2026 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + */ + +#ifndef OHOS_ABILITY_RUNTIME_PERMISSION_QUERY_UTIL_H +#define OHOS_ABILITY_RUNTIME_PERMISSION_QUERY_UTIL_H + +#include + +#include "cli_error_code.h" +#include "icli_tool_data.h" + +namespace OHOS { +namespace CliTool { +namespace QueryResult { + constexpr int32_t SUCCESS = 0; + constexpr int32_t COMMAND_NOT_EXIST = 1; + constexpr int32_t DB_ERROR = 2; +} // namespace QueryResult + +class PermissionQueryUtil { +public: + static int32_t BatchQueryPermissions( + const std::vector &cmds, + std::vector &cmdPermissions); +}; +} // namespace CliTool +} // namespace OHOS + +#endif // OHOS_ABILITY_RUNTIME_PERMISSION_QUERY_UTIL_H diff --git a/cli_tool_framework/test/unittest/common_mock/climgr/include/permission_util.h b/cli_tool_framework/test/unittest/common_mock/climgr/include/permission_util.h new file mode 100644 index 0000000000..4dc168ac63 --- /dev/null +++ b/cli_tool_framework/test/unittest/common_mock/climgr/include/permission_util.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2026 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + */ + +#ifndef OHOS_ABILITY_RUNTIME_PERMISSION_UTIL_H +#define OHOS_ABILITY_RUNTIME_PERMISSION_UTIL_H + +#include +#include + +#include "access_token.h" + +namespace OHOS { +namespace CliTool { + +using namespace OHOS::Security; + +class PermissionUtil { +public: + PermissionUtil() = default; + ~PermissionUtil() = default; + + static bool VerifyAccessToken(AccessToken::AccessTokenID tokenId, + const std::vector &requirePermissions); + static bool VerifyAccessToken(AccessToken::AccessTokenID tokenId, + const std::string &requirePermission); +}; + +} // namespace CliTool +} // namespace OHOS + +#endif // OHOS_ABILITY_RUNTIME_PERMISSION_UTIL_H diff --git a/cli_tool_framework/test/unittest/common_mock/climgr/include/process_manager.h b/cli_tool_framework/test/unittest/common_mock/climgr/include/process_manager.h new file mode 100644 index 0000000000..01662199eb --- /dev/null +++ b/cli_tool_framework/test/unittest/common_mock/climgr/include/process_manager.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2026 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + */ + +#ifndef OHOS_ABILITY_RUNTIME_PROCESS_MANAGER_H +#define OHOS_ABILITY_RUNTIME_PROCESS_MANAGER_H + +#include +#include +#include + +#include "errors.h" +#include "session_record.h" + +namespace OHOS { +namespace CliTool { +class ExecToolParam; +class ToolInfo; + +class ProcessManager { +public: + static ProcessManager &GetInstance(); + int32_t CreateChildProcess(const ExecToolParam ¶m, const std::string &sandboxConfig, + const ToolInfo &toolInfo, std::shared_ptr record) const; + bool Killpg(pid_t pid) const; +}; +} // namespace CliTool +} // namespace OHOS + +#endif // OHOS_ABILITY_RUNTIME_PROCESS_MANAGER_H diff --git a/cli_tool_framework/test/unittest/common_mock/climgr/include/tool_util.h b/cli_tool_framework/test/unittest/common_mock/climgr/include/tool_util.h new file mode 100644 index 0000000000..f82ab44e32 --- /dev/null +++ b/cli_tool_framework/test/unittest/common_mock/climgr/include/tool_util.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2026 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + */ + +#ifndef OHOS_ABILITY_RUNTIME_TOOL_UTIL_H +#define OHOS_ABILITY_RUNTIME_TOOL_UTIL_H + +#include +#include +#include + +#include "cli_session_info.h" +#include "want_params.h" + +namespace OHOS { +namespace AppExecFwk { +struct SkillExecuteResult; +} // namespace AppExecFwk +namespace CliTool { +class ExecToolParam; +class SessionRecord; +class ToolInfo; + +class ToolUtil { +public: + static int32_t ValidateProperties(const ToolInfo &toolInfo, ExecToolParam ¶m, + Security::AccessToken::AccessTokenID tokenId); + static std::string GenerateCliSessionId(const std::string &name, std::shared_ptr record); + static bool GenerateSandboxConfig(const ExecToolParam ¶m, Security::AccessToken::AccessTokenID tokenId, + std::string &sandboxConfig, std::string &bundleName); + static void TransferToCmdParam(const ToolInfo &toolInfo, const AAFwk::WantParams &args, std::string &cmdLine); + static bool IsSkillTool(const std::string &toolName); + static void NormalizeSkillParamKeys(AAFwk::WantParams &args); + static void ExpandArgsJsonString(AAFwk::WantParams &args); + static std::shared_ptr FilterSkillArgs(const AAFwk::WantParams &args); + static CliSessionInfo BuildSkillSessionInfo(const std::string &sessionId, + int32_t resultCode, const AppExecFwk::SkillExecuteResult &skillResult); +}; +} // namespace CliTool +} // namespace OHOS + +#endif // OHOS_ABILITY_RUNTIME_TOOL_UTIL_H diff --git a/cli_tool_framework/test/unittest/common_mock/climgr/src/cli_mgr_service_mock.cpp b/cli_tool_framework/test/unittest/common_mock/climgr/src/cli_mgr_service_mock.cpp new file mode 100644 index 0000000000..0160aac65f --- /dev/null +++ b/cli_tool_framework/test/unittest/common_mock/climgr/src/cli_mgr_service_mock.cpp @@ -0,0 +1,289 @@ +/* + * Copyright (c) 2026 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + */ + +#include "cli_mgr_service_mock.h" + +#include +#include +#include + +#include "ccm_util.h" +#include "cli_error_code.h" +#include "cli_tool_data_manager.h" +#include "event_dispatcher.h" +#include "exec_result.h" +#include "io_monitor.h" +#include "permission_query_util.h" +#include "permission_util.h" +#include "process_manager.h" +#include "skill/skill_execute_result.h" +#include "tool_info.h" +#include "tool_util.h" + +namespace OHOS { +namespace CliTool { +int32_t CliMgrServiceMock::createChildProcessResult = ERR_OK; +bool CliMgrServiceMock::killpgResult = true; +int32_t CliMgrServiceMock::registerSessionResult = ERR_OK; +int32_t CliMgrServiceMock::unregisterSessionCount = 0; +int32_t CliMgrServiceMock::stopCount = 0; +int32_t CliMgrServiceMock::sendMessageCount = 0; +int32_t CliMgrServiceMock::ensureToolsLoadedResult = ERR_OK; +int32_t CliMgrServiceMock::getToolByNameResult = ERR_OK; +int32_t CliMgrServiceMock::connectAppMgrResult = 0; +int32_t CliMgrServiceMock::registerAppObserverResult = ERR_OK; +int32_t CliMgrServiceMock::querySkillTypeResult = ERR_OK; +int32_t CliMgrServiceMock::executeSkillResult = ERR_OK; +int32_t CliMgrServiceMock::skillType = -1; +std::string CliMgrServiceMock::lastSkillName; +bool CliMgrServiceMock::toolHasSubCommand = false; +std::string CliMgrServiceMock::subCommandName = "build"; +std::vector CliMgrServiceMock::toolPermissions = {}; +std::vector CliMgrServiceMock::subCommandPermissions = {}; + +void CliMgrServiceMock::Reset() +{ + createChildProcessResult = ERR_OK; + killpgResult = true; + registerSessionResult = ERR_OK; + unregisterSessionCount = 0; + stopCount = 0; + sendMessageCount = 0; + ensureToolsLoadedResult = ERR_OK; + getToolByNameResult = ERR_OK; + connectAppMgrResult = 0; + registerAppObserverResult = ERR_OK; + querySkillTypeResult = ERR_OK; + executeSkillResult = ERR_OK; + skillType = -1; + lastSkillName.clear(); + toolHasSubCommand = false; + subCommandName = "build"; + toolPermissions.clear(); + subCommandPermissions.clear(); +} + +ProcessManager &ProcessManager::GetInstance() +{ + static ProcessManager instance; + return instance; +} + +int32_t ProcessManager::CreateChildProcess(const ExecToolParam &, const std::string &, const ToolInfo &, + std::shared_ptr record) const +{ + if (record != nullptr) { + record->processId = 1001; + record->stdoutPipe[0] = -1; + record->stderrPipe[0] = -1; + record->stdinPipe[1] = -1; + } + return CliMgrServiceMock::createChildProcessResult; +} + +bool ProcessManager::Killpg(pid_t) const +{ + return CliMgrServiceMock::killpgResult; +} + +std::shared_ptr IOMonitor::Create() +{ + return std::make_shared(); +} + +bool IOMonitor::Start() +{ + return true; +} + +void IOMonitor::Stop() +{ + CliMgrServiceMock::stopCount++; +} + +bool IOMonitor::RegisterSession(const std::string &, int, int, int) +{ + return CliMgrServiceMock::registerSessionResult == ERR_OK; +} + +void IOMonitor::UnregisterSession(const std::string &) +{ + CliMgrServiceMock::unregisterSessionCount++; +} + +void IOMonitor::SetOutputCallback(OutputCallback) {} +void IOMonitor::SetInputReplyCallback(InputReplyCallback) {} +void IOMonitor::SetSessionClosedCallback(SessionClosedCallback) {} +void IOMonitor::SetSessionDrainedCallback(SessionDrainedCallback) {} + +void IOMonitor::SendMessage(const std::string &, const std::string &, const std::string &) +{ + CliMgrServiceMock::sendMessageCount++; +} + +CliToolDataManager &CliToolDataManager::GetInstance() +{ + static CliToolDataManager instance; + return instance; +} + +int32_t CliToolDataManager::EnsureToolsLoaded() +{ + return CliMgrServiceMock::ensureToolsLoadedResult; +} + +int32_t CliToolDataManager::GetAllToolsRawData(ToolsRawData &) +{ + return ERR_OK; +} + +int32_t CliToolDataManager::QueryToolSummaries(std::vector &) +{ + return ERR_OK; +} + +int32_t CliToolDataManager::GetToolByName(const std::string &name, ToolInfo &toolInfo) +{ + if (CliMgrServiceMock::getToolByNameResult != ERR_OK) { + return CliMgrServiceMock::getToolByNameResult; + } + toolInfo.name = name; + toolInfo.version = "1.0.0"; + toolInfo.description = "mock tool"; + toolInfo.executablePath = "/system/bin/mock"; + toolInfo.requirePermissions = CliMgrServiceMock::toolPermissions; + toolInfo.inputSchema = "{}"; + toolInfo.outputSchema = "{}"; + toolInfo.hasSubCommand = CliMgrServiceMock::toolHasSubCommand; + if (toolInfo.hasSubCommand) { + SubCommandInfo subCommand; + subCommand.description = "mock subcommand"; + subCommand.requirePermissions = CliMgrServiceMock::subCommandPermissions; + subCommand.inputSchema = "{}"; + subCommand.outputSchema = "{}"; + toolInfo.subcommands[CliMgrServiceMock::subCommandName] = subCommand; + } + return ERR_OK; +} + +bool PermissionUtil::VerifyAccessToken(Security::AccessToken::AccessTokenID, const std::vector &) +{ + return true; +} + +bool PermissionUtil::VerifyAccessToken(Security::AccessToken::AccessTokenID, const std::string &) +{ + return true; +} + +CcmUtil &CcmUtil::GetInstance() +{ + static CcmUtil instance; + return instance; +} + +int32_t CcmUtil::GetCliConcurrencyLimit() +{ + return 10; +} + +EventDispatcher &EventDispatcher::GetInstance() +{ + static EventDispatcher instance; + return instance; +} + +bool EventDispatcher::RegisterScheduler(int32_t, const sptr &) +{ + return true; +} + +void EventDispatcher::UnregisterScheduler(int32_t) {} + +bool EventDispatcher::RegisterSubscriber(const std::string &, const std::string &, int32_t) +{ + return true; +} + +bool EventDispatcher::UnregisterSubscriber(const std::string &, const std::string &, int32_t) +{ + return true; +} + +void EventDispatcher::DispatchIOEvent(const std::string &, const std::string &, const std::string &) {} +void EventDispatcher::DispatchErrorEvent(const std::string &, const std::string &) {} +void EventDispatcher::DispatchExitEvent(const std::string &, int32_t) {} + +bool EventDispatcher::DispatchInputReplyEvent(int32_t, const std::string &, int32_t) +{ + return true; +} + +bool EventDispatcher::DispatchExecToolReplyEvent(int32_t, const std::string &, int32_t, const CliSessionInfo &) +{ + return true; +} + +void EventDispatcher::ClearSessionSubscribers(const std::string &) {} +void EventDispatcher::ClearAll() {} + +int32_t PermissionQueryUtil::BatchQueryPermissions( + const std::vector &, std::vector &cmdPermissions) +{ + cmdPermissions.clear(); + return ERR_OK; +} + +int32_t ToolUtil::ValidateProperties(const ToolInfo &, ExecToolParam &, Security::AccessToken::AccessTokenID) +{ + return ERR_OK; +} + +std::string ToolUtil::GenerateCliSessionId(const std::string &name, std::shared_ptr record) +{ + if (record != nullptr) { + record->startTime = 1000; + } + return name + "_session"; +} + +bool ToolUtil::GenerateSandboxConfig(const ExecToolParam &, Security::AccessToken::AccessTokenID, + std::string &sandboxConfig, std::string &bundleName) +{ + sandboxConfig = "{}"; + bundleName = "bundle.mock"; + return true; +} + +void ToolUtil::TransferToCmdParam(const ToolInfo &, const AAFwk::WantParams &, std::string &) {} + +bool ToolUtil::IsSkillTool(const std::string &toolName) +{ + return toolName == "ohos-arkTSScript"; +} + +void ToolUtil::NormalizeSkillParamKeys(AAFwk::WantParams &) {} +void ToolUtil::ExpandArgsJsonString(AAFwk::WantParams &) {} + +std::shared_ptr ToolUtil::FilterSkillArgs(const AAFwk::WantParams &args) +{ + return std::make_shared(args); +} + +CliSessionInfo ToolUtil::BuildSkillSessionInfo( + const std::string &sessionId, int32_t resultCode, const AppExecFwk::SkillExecuteResult &result) +{ + CliSessionInfo session; + session.sessionId = sessionId; + session.status = resultCode == ERR_OK ? "completed" : "failed"; + session.result = std::make_shared(); + session.result->exitCode = resultCode; + if (result.result != nullptr) { + session.result->outputText = result.result->ToString(); + } + return session; +} +} // namespace CliTool +} // namespace OHOS diff --git a/cli_tool_framework/test/unittest/common_mock/climgr_data/include/cli_tool_data_manager.h b/cli_tool_framework/test/unittest/common_mock/climgr_data/include/cli_tool_data_manager.h new file mode 100644 index 0000000000..1c39282a19 --- /dev/null +++ b/cli_tool_framework/test/unittest/common_mock/climgr_data/include/cli_tool_data_manager.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2026 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + */ + +#ifndef OHOS_ABILITY_RUNTIME_CLI_TOOL_DATA_MANAGER_COMMON_MOCK_SHIM_H +#define OHOS_ABILITY_RUNTIME_CLI_TOOL_DATA_MANAGER_COMMON_MOCK_SHIM_H + +#include +#include + +#include "tool_info.h" +#include "tool_summary.h" + +namespace OHOS { +namespace CliTool { +class CliToolDataManager { +public: + CliToolDataManager() noexcept; + ~CliToolDataManager(); + + static CliToolDataManager &GetInstance(); + int32_t EnsureToolsLoaded(); + int32_t GetAllTools(std::vector &tools); + int32_t GetAllToolsRawData(ToolsRawData &tools); + int32_t QueryToolSummaries(std::vector &summaries); + int32_t RegisterTool(const ToolInfo &tool); + int32_t JsonArrayToTools(const std::string &jsonStr, std::vector &tools); + int32_t GetToolByName(const std::string &name, ToolInfo &toolInfo); +}; +} // namespace CliTool +} // namespace OHOS + +#endif // OHOS_ABILITY_RUNTIME_CLI_TOOL_DATA_MANAGER_COMMON_MOCK_SHIM_H diff --git a/cli_tool_framework/test/unittest/common_mock/climgr_data/include/cli_tool_data_manager_mock.h b/cli_tool_framework/test/unittest/common_mock/climgr_data/include/cli_tool_data_manager_mock.h new file mode 100644 index 0000000000..36952a730b --- /dev/null +++ b/cli_tool_framework/test/unittest/common_mock/climgr_data/include/cli_tool_data_manager_mock.h @@ -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_CLI_TOOL_DATA_MANAGER_COMMON_MOCK_H +#define OHOS_ABILITY_RUNTIME_CLI_TOOL_DATA_MANAGER_COMMON_MOCK_H + +#include +#include +#include + +namespace OHOS { +namespace CliTool { +class CliToolDataManagerMock { +public: + static int32_t getToolByNameResult; + static bool toolHasSubCommand; + static std::string subCommandName; + static std::vector toolPermissions; + static std::vector subCommandPermissions; + + static void Reset(); +}; +} // namespace CliTool +} // namespace OHOS + +#endif // OHOS_ABILITY_RUNTIME_CLI_TOOL_DATA_MANAGER_COMMON_MOCK_H diff --git a/cli_tool_framework/test/unittest/common_mock/climgr_data/src/cli_tool_data_manager_mock.cpp b/cli_tool_framework/test/unittest/common_mock/climgr_data/src/cli_tool_data_manager_mock.cpp new file mode 100644 index 0000000000..7cbf318775 --- /dev/null +++ b/cli_tool_framework/test/unittest/common_mock/climgr_data/src/cli_tool_data_manager_mock.cpp @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2026 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + */ + +#include "cli_tool_data_manager_mock.h" + +#include "../include/cli_tool_data_manager.h" +#include "cli_error_code.h" +#include "tool_info.h" + +namespace OHOS { +namespace CliTool { +int32_t CliToolDataManagerMock::getToolByNameResult = ERR_TOOL_NOT_EXIST; +bool CliToolDataManagerMock::toolHasSubCommand = false; +std::string CliToolDataManagerMock::subCommandName = "build"; +std::vector CliToolDataManagerMock::toolPermissions = {}; +std::vector CliToolDataManagerMock::subCommandPermissions = {}; + +void CliToolDataManagerMock::Reset() +{ + getToolByNameResult = ERR_TOOL_NOT_EXIST; + toolHasSubCommand = false; + subCommandName = "build"; + toolPermissions.clear(); + subCommandPermissions.clear(); +} + +CliToolDataManager::CliToolDataManager() noexcept = default; + +CliToolDataManager::~CliToolDataManager() = default; + +CliToolDataManager &CliToolDataManager::GetInstance() +{ + static CliToolDataManager instance; + return instance; +} + +int32_t CliToolDataManager::EnsureToolsLoaded() +{ + return ERR_OK; +} + +int32_t CliToolDataManager::GetAllTools(std::vector &tools) +{ + tools.clear(); + return ERR_OK; +} + +int32_t CliToolDataManager::GetAllToolsRawData(ToolsRawData &) +{ + return ERR_OK; +} + +int32_t CliToolDataManager::QueryToolSummaries(std::vector &summaries) +{ + summaries.clear(); + return ERR_OK; +} + +int32_t CliToolDataManager::RegisterTool(const ToolInfo &) +{ + return ERR_OK; +} + +int32_t CliToolDataManager::JsonArrayToTools(const std::string &, std::vector &tools) +{ + tools.clear(); + return ERR_OK; +} + +int32_t CliToolDataManager::GetToolByName(const std::string &name, ToolInfo &toolInfo) +{ + if (CliToolDataManagerMock::getToolByNameResult != ERR_OK) { + return CliToolDataManagerMock::getToolByNameResult; + } + toolInfo.name = name; + toolInfo.version = "1.0.0"; + toolInfo.description = "mock tool"; + toolInfo.executablePath = "/system/bin/mock"; + toolInfo.requirePermissions = CliToolDataManagerMock::toolPermissions; + toolInfo.inputSchema = "{}"; + toolInfo.outputSchema = "{}"; + toolInfo.hasSubCommand = CliToolDataManagerMock::toolHasSubCommand; + if (toolInfo.hasSubCommand) { + SubCommandInfo subCommand; + subCommand.description = "mock subcommand"; + subCommand.requirePermissions = CliToolDataManagerMock::subCommandPermissions; + subCommand.inputSchema = "{}"; + subCommand.outputSchema = "{}"; + toolInfo.subcommands[CliToolDataManagerMock::subCommandName] = subCommand; + } + return ERR_OK; +} +} // namespace CliTool +} // namespace OHOS diff --git a/cli_tool_framework/test/unittest/event_dispatcher_test/BUILD.gn b/cli_tool_framework/test/unittest/event_dispatcher_test/BUILD.gn new file mode 100644 index 0000000000..5dd1c8a5b7 --- /dev/null +++ b/cli_tool_framework/test/unittest/event_dispatcher_test/BUILD.gn @@ -0,0 +1,49 @@ +# 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("event_dispatcher_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}/services/climgr/include", + ] + + sources = [ + "event_dispatcher_test.cpp", + "${cli_tool_framework_path}/services/climgr/src/event_dispatcher.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", + "json:nlohmann_json_static", + ] +} + +group("unittest") { + testonly = true + deps = [ ":event_dispatcher_test" ] +} diff --git a/cli_tool_framework/test/unittest/event_dispatcher_test/event_dispatcher_test.cpp b/cli_tool_framework/test/unittest/event_dispatcher_test/event_dispatcher_test.cpp new file mode 100644 index 0000000000..afe65c7be9 --- /dev/null +++ b/cli_tool_framework/test/unittest/event_dispatcher_test/event_dispatcher_test.cpp @@ -0,0 +1,261 @@ +/* + * 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 +#include + +#include "cli_tool_manager_scheduler_stub.h" +#include "event_dispatcher.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_CALLER_PID = 1000; +constexpr int32_t TEST_CALLER_PID_SECOND = 1001; +constexpr int32_t TEST_EXIT_CODE = 3; +constexpr int32_t TEST_REPLY_RESULT = 5; +} + +class TestScheduler : public CliToolManagerSchedulerStub { +public: + int32_t SchedulerSessionEvent(const std::string &sessionId, const std::string &subscriptionId, + const CliToolEvent &event) override + { + sessionEventCount++; + lastSessionId = sessionId; + lastSubscriptionId = subscriptionId; + lastEventType = event.type; + lastEventData = event.eventData; + lastExitCode = event.exitCode; + return sessionEventResult; + } + + int32_t SchedulerInputReplyEvent(const std::string &eventId, int32_t resultCode) override + { + inputReplyCount++; + lastInputEventId = eventId; + lastInputResult = resultCode; + return inputReplyResult; + } + + int32_t SchedulerExecToolReplyEvent(const std::string &eventId, int32_t resultCode, + const CliSessionInfo &session) override + { + execReplyCount++; + lastExecEventId = eventId; + lastExecResult = resultCode; + lastExecSessionId = session.sessionId; + return execReplyResult; + } + + int32_t sessionEventResult = ERR_OK; + int32_t inputReplyResult = ERR_OK; + int32_t execReplyResult = ERR_OK; + int32_t sessionEventCount = 0; + int32_t inputReplyCount = 0; + int32_t execReplyCount = 0; + int32_t lastExitCode = 0; + int32_t lastInputResult = 0; + int32_t lastExecResult = 0; + std::string lastSessionId; + std::string lastSubscriptionId; + std::string lastEventType; + std::string lastEventData; + std::string lastInputEventId; + std::string lastExecEventId; + std::string lastExecSessionId; +}; + +class EventDispatcherTest : public testing::Test { +public: + void SetUp() override + { + EventDispatcher::GetInstance().ClearAll(); + } + + void TearDown() override + { + EventDispatcher::GetInstance().ClearAll(); + } +}; + +/** + * @tc.name: EventDispatcher_Register_0100 + * @tc.desc: Test scheduler and subscriber registration validation branches + * @tc.type: FUNC + */ +HWTEST_F(EventDispatcherTest, EventDispatcher_Register_0100, TestSize.Level1) +{ + auto &dispatcher = EventDispatcher::GetInstance(); + sptr scheduler = new TestScheduler(); + + EXPECT_FALSE(dispatcher.RegisterScheduler(0, scheduler)); + EXPECT_FALSE(dispatcher.RegisterScheduler(TEST_CALLER_PID, nullptr)); + EXPECT_TRUE(dispatcher.RegisterScheduler(TEST_CALLER_PID, scheduler)); + + EXPECT_FALSE(dispatcher.RegisterSubscriber("", "subscription", TEST_CALLER_PID)); + EXPECT_FALSE(dispatcher.RegisterSubscriber("session", "", TEST_CALLER_PID)); + EXPECT_FALSE(dispatcher.RegisterSubscriber("session", "subscription", 0)); + EXPECT_FALSE(dispatcher.RegisterSubscriber("session", "subscription", TEST_CALLER_PID + 1)); + EXPECT_TRUE(dispatcher.RegisterSubscriber("session", "subscription", TEST_CALLER_PID)); + + EXPECT_FALSE(dispatcher.UnregisterSubscriber("", "subscription", TEST_CALLER_PID)); + EXPECT_FALSE(dispatcher.UnregisterSubscriber("session", "", TEST_CALLER_PID)); + EXPECT_FALSE(dispatcher.UnregisterSubscriber("session", "subscription", 0)); + EXPECT_TRUE(dispatcher.UnregisterSubscriber("unknown-session", "subscription", TEST_CALLER_PID)); + EXPECT_TRUE(dispatcher.UnregisterSubscriber("session", "subscription", TEST_CALLER_PID)); +} + +/** + * @tc.name: EventDispatcher_Dispatch_0100 + * @tc.desc: Test dispatch IO, error, exit and reply success branches + * @tc.type: FUNC + */ +HWTEST_F(EventDispatcherTest, EventDispatcher_Dispatch_0100, TestSize.Level1) +{ + auto &dispatcher = EventDispatcher::GetInstance(); + sptr scheduler = new TestScheduler(); + ASSERT_TRUE(dispatcher.RegisterScheduler(TEST_CALLER_PID, scheduler)); + ASSERT_TRUE(dispatcher.RegisterSubscriber("session", "subscription", TEST_CALLER_PID)); + + dispatcher.DispatchIOEvent("session", "stdout", "payload"); + EXPECT_EQ(scheduler->sessionEventCount, 1); + EXPECT_EQ(scheduler->lastSessionId, "session"); + EXPECT_EQ(scheduler->lastSubscriptionId, "subscription"); + EXPECT_EQ(scheduler->lastEventType, "stdout"); + EXPECT_EQ(scheduler->lastEventData, "payload"); + + dispatcher.DispatchErrorEvent("session", "error text"); + EXPECT_EQ(scheduler->sessionEventCount, 2); + EXPECT_EQ(scheduler->lastEventType, "error"); + EXPECT_EQ(scheduler->lastEventData, "error text"); + + dispatcher.DispatchExitEvent("session", TEST_EXIT_CODE); + EXPECT_EQ(scheduler->sessionEventCount, 3); + EXPECT_EQ(scheduler->lastEventType, "exit"); + EXPECT_EQ(scheduler->lastExitCode, TEST_EXIT_CODE); + + EXPECT_TRUE(dispatcher.DispatchInputReplyEvent(TEST_CALLER_PID, "input-event", TEST_REPLY_RESULT)); + EXPECT_EQ(scheduler->inputReplyCount, 1); + EXPECT_EQ(scheduler->lastInputEventId, "input-event"); + EXPECT_EQ(scheduler->lastInputResult, TEST_REPLY_RESULT); + + CliSessionInfo session; + session.sessionId = "exec-session"; + EXPECT_TRUE(dispatcher.DispatchExecToolReplyEvent(TEST_CALLER_PID, "exec-event", ERR_OK, session)); + EXPECT_EQ(scheduler->execReplyCount, 1); + EXPECT_EQ(scheduler->lastExecEventId, "exec-event"); + EXPECT_EQ(scheduler->lastExecResult, ERR_OK); + EXPECT_EQ(scheduler->lastExecSessionId, "exec-session"); +} + +/** + * @tc.name: EventDispatcher_Dispatch_0200 + * @tc.desc: Test missing scheduler, failed subscriber and clear branches + * @tc.type: FUNC + */ +HWTEST_F(EventDispatcherTest, EventDispatcher_Dispatch_0200, TestSize.Level1) +{ + auto &dispatcher = EventDispatcher::GetInstance(); + CliSessionInfo session; + EXPECT_FALSE(dispatcher.DispatchInputReplyEvent(TEST_CALLER_PID, "input-event", TEST_REPLY_RESULT)); + EXPECT_FALSE(dispatcher.DispatchExecToolReplyEvent(TEST_CALLER_PID, "exec-event", ERR_OK, session)); + + sptr scheduler = new TestScheduler(); + ASSERT_TRUE(dispatcher.RegisterScheduler(TEST_CALLER_PID, scheduler)); + ASSERT_TRUE(dispatcher.RegisterSubscriber("session", "subscription", TEST_CALLER_PID)); + + scheduler->sessionEventResult = ERROR_CODE; + dispatcher.DispatchIOEvent("session", "stdout", "payload"); + EXPECT_EQ(scheduler->sessionEventCount, 1); + + scheduler->sessionEventResult = ERR_OK; + dispatcher.DispatchIOEvent("session", "stdout", "payload"); + EXPECT_EQ(scheduler->sessionEventCount, 1); + + ASSERT_TRUE(dispatcher.RegisterSubscriber("session", "subscription", TEST_CALLER_PID)); + dispatcher.ClearSessionSubscribers("session"); + dispatcher.DispatchIOEvent("session", "stdout", "payload"); + EXPECT_EQ(scheduler->sessionEventCount, 1); + + dispatcher.UnregisterScheduler(TEST_CALLER_PID); + EXPECT_FALSE(dispatcher.DispatchInputReplyEvent(TEST_CALLER_PID, "input-event", TEST_REPLY_RESULT)); +} + +/** + * @tc.name: EventDispatcher_Register_0200 + * @tc.desc: Test scheduler replacement and reply failure branches + * @tc.type: FUNC + */ +HWTEST_F(EventDispatcherTest, EventDispatcher_Register_0200, TestSize.Level1) +{ + auto &dispatcher = EventDispatcher::GetInstance(); + sptr oldScheduler = new TestScheduler(); + sptr newScheduler = new TestScheduler(); + ASSERT_TRUE(dispatcher.RegisterScheduler(TEST_CALLER_PID, oldScheduler)); + ASSERT_TRUE(dispatcher.RegisterSubscriber("session", "old-subscription", TEST_CALLER_PID)); + + ASSERT_TRUE(dispatcher.RegisterScheduler(TEST_CALLER_PID, newScheduler)); + dispatcher.DispatchIOEvent("session", "stdout", "payload"); + EXPECT_EQ(oldScheduler->sessionEventCount, 0); + EXPECT_EQ(newScheduler->sessionEventCount, 0); + + ASSERT_TRUE(dispatcher.RegisterSubscriber("session", "new-subscription", TEST_CALLER_PID)); + dispatcher.DispatchIOEvent("session", "stdout", "payload"); + EXPECT_EQ(newScheduler->sessionEventCount, 1); + + newScheduler->inputReplyResult = ERROR_CODE; + EXPECT_FALSE(dispatcher.DispatchInputReplyEvent(TEST_CALLER_PID, "input-event", TEST_REPLY_RESULT)); + EXPECT_EQ(newScheduler->inputReplyCount, 1); + + newScheduler->execReplyResult = ERROR_CODE; + CliSessionInfo session; + EXPECT_FALSE(dispatcher.DispatchExecToolReplyEvent(TEST_CALLER_PID, "exec-event", ERR_OK, session)); + EXPECT_EQ(newScheduler->execReplyCount, 1); +} + +/** + * @tc.name: EventDispatcher_Dispatch_0300 + * @tc.desc: Test failed subscriber removal keeps other subscribers + * @tc.type: FUNC + */ +HWTEST_F(EventDispatcherTest, EventDispatcher_Dispatch_0300, TestSize.Level1) +{ + auto &dispatcher = EventDispatcher::GetInstance(); + sptr failedScheduler = new TestScheduler(); + sptr okScheduler = new TestScheduler(); + failedScheduler->sessionEventResult = ERROR_CODE; + + ASSERT_TRUE(dispatcher.RegisterScheduler(TEST_CALLER_PID, failedScheduler)); + ASSERT_TRUE(dispatcher.RegisterScheduler(TEST_CALLER_PID_SECOND, okScheduler)); + ASSERT_TRUE(dispatcher.RegisterSubscriber("session", "failed-subscription", TEST_CALLER_PID)); + ASSERT_TRUE(dispatcher.RegisterSubscriber("session", "ok-subscription", TEST_CALLER_PID_SECOND)); + + dispatcher.DispatchIOEvent("session", "stdout", "payload"); + EXPECT_EQ(failedScheduler->sessionEventCount, 1); + EXPECT_EQ(okScheduler->sessionEventCount, 1); + + dispatcher.DispatchIOEvent("session", "stderr", "payload"); + EXPECT_EQ(failedScheduler->sessionEventCount, 1); + EXPECT_EQ(okScheduler->sessionEventCount, 2); + EXPECT_EQ(okScheduler->lastSubscriptionId, "ok-subscription"); + EXPECT_EQ(okScheduler->lastEventType, "stderr"); +} +} // namespace CliTool +} // namespace OHOS diff --git a/cli_tool_framework/test/unittest/exec_options_test/BUILD.gn b/cli_tool_framework/test/unittest/exec_options_test/BUILD.gn new file mode 100644 index 0000000000..4b7be166a3 --- /dev/null +++ b/cli_tool_framework/test/unittest/exec_options_test/BUILD.gn @@ -0,0 +1,40 @@ +# 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("exec_options_test") { + module_out_path = module_output_path + + include_dirs = [ "${cli_tool_framework_path}/interfaces/cli_tool/include" ] + + sources = [ + "exec_options_test.cpp", + "${cli_tool_framework_path}/interfaces/cli_tool/src/exec_options.cpp", + ] + + external_deps = [ + "c_utils:utils", + "googletest:gmock_main", + "googletest:gtest_main", + "ipc:ipc_core", + ] +} + +group("unittest") { + testonly = true + deps = [ ":exec_options_test" ] +} diff --git a/cli_tool_framework/test/unittest/exec_options_test/exec_options_test.cpp b/cli_tool_framework/test/unittest/exec_options_test/exec_options_test.cpp new file mode 100644 index 0000000000..a96308ad51 --- /dev/null +++ b/cli_tool_framework/test/unittest/exec_options_test/exec_options_test.cpp @@ -0,0 +1,78 @@ +/* + * 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 +#include +#include + +#include "exec_options.h" + +using namespace testing::ext; + +namespace OHOS { +namespace CliTool { +namespace { +constexpr int64_t TEST_TIMEOUT = 3000; +constexpr int64_t TEST_YIELD_MS = 50; +} + +class ExecOptionsTest : public testing::Test {}; + +/** + * @tc.name: ExecOptions_Parcelable_0100 + * @tc.desc: Test ExecOptions marshalling and unmarshalling success path + * @tc.type: FUNC + */ +HWTEST_F(ExecOptionsTest, ExecOptions_Parcelable_0100, TestSize.Level1) +{ + ExecOptions options; + options.background = true; + options.yieldMs = TEST_YIELD_MS; + options.timeout = TEST_TIMEOUT; + + Parcel parcel; + ASSERT_TRUE(options.Marshalling(parcel)); + parcel.RewindRead(0); + + std::unique_ptr result(ExecOptions::Unmarshalling(parcel)); + ASSERT_NE(result, nullptr); + EXPECT_TRUE(result->background); + EXPECT_EQ(result->yieldMs, TEST_YIELD_MS); + EXPECT_EQ(result->timeout, TEST_TIMEOUT); +} + +/** + * @tc.name: ExecOptions_Unmarshalling_0200 + * @tc.desc: Test ExecOptions unmarshalling failure branches with incomplete parcel data + * @tc.type: FUNC + */ +HWTEST_F(ExecOptionsTest, ExecOptions_Unmarshalling_0200, TestSize.Level1) +{ + Parcel emptyParcel; + EXPECT_EQ(ExecOptions::Unmarshalling(emptyParcel), nullptr); + + Parcel partialParcel; + ASSERT_TRUE(partialParcel.WriteBool(true)); + partialParcel.RewindRead(0); + EXPECT_EQ(ExecOptions::Unmarshalling(partialParcel), nullptr); + + Parcel missingTimeoutParcel; + ASSERT_TRUE(missingTimeoutParcel.WriteBool(false)); + ASSERT_TRUE(missingTimeoutParcel.WriteInt64(TEST_YIELD_MS)); + missingTimeoutParcel.RewindRead(0); + EXPECT_EQ(ExecOptions::Unmarshalling(missingTimeoutParcel), nullptr); +} +} // namespace CliTool +} // namespace OHOS diff --git a/cli_tool_framework/test/unittest/exec_result_test/BUILD.gn b/cli_tool_framework/test/unittest/exec_result_test/BUILD.gn new file mode 100644 index 0000000000..d1329a831a --- /dev/null +++ b/cli_tool_framework/test/unittest/exec_result_test/BUILD.gn @@ -0,0 +1,40 @@ +# 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("exec_result_test") { + module_out_path = module_output_path + + include_dirs = [ "${cli_tool_framework_path}/interfaces/cli_tool/include" ] + + sources = [ + "exec_result_test.cpp", + "${cli_tool_framework_path}/interfaces/cli_tool/src/exec_result.cpp", + ] + + external_deps = [ + "c_utils:utils", + "googletest:gmock_main", + "googletest:gtest_main", + "ipc:ipc_core", + ] +} + +group("unittest") { + testonly = true + deps = [ ":exec_result_test" ] +} diff --git a/cli_tool_framework/test/unittest/exec_result_test/exec_result_test.cpp b/cli_tool_framework/test/unittest/exec_result_test/exec_result_test.cpp new file mode 100644 index 0000000000..d02195e580 --- /dev/null +++ b/cli_tool_framework/test/unittest/exec_result_test/exec_result_test.cpp @@ -0,0 +1,108 @@ +/* + * 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 +#include +#include + +#include "exec_result.h" + +using namespace testing::ext; + +namespace OHOS { +namespace CliTool { +namespace { +constexpr int32_t TEST_EXIT_CODE = 7; +constexpr int64_t TEST_TIMEOUT = 3000; +} + +class ExecResultTest : public testing::Test {}; + +/** + * @tc.name: ExecResult_Parcelable_0100 + * @tc.desc: Test ExecResult marshalling and unmarshalling success path + * @tc.type: FUNC + */ +HWTEST_F(ExecResultTest, ExecResult_Parcelable_0100, TestSize.Level1) +{ + ExecResult result; + result.exitCode = TEST_EXIT_CODE; + result.outputText = "stdout text"; + result.errorText = "stderr text"; + result.signalNumber = 9; + result.timedOut = true; + result.executionTime = TEST_TIMEOUT; + + Parcel parcel; + ASSERT_TRUE(result.Marshalling(parcel)); + parcel.RewindRead(0); + + std::unique_ptr unmarshalled(ExecResult::Unmarshalling(parcel)); + ASSERT_NE(unmarshalled, nullptr); + EXPECT_EQ(unmarshalled->exitCode, TEST_EXIT_CODE); + EXPECT_EQ(unmarshalled->outputText, "stdout text"); + EXPECT_EQ(unmarshalled->errorText, "stderr text"); + EXPECT_EQ(unmarshalled->signalNumber, 9); + EXPECT_TRUE(unmarshalled->timedOut); + EXPECT_EQ(unmarshalled->executionTime, TEST_TIMEOUT); +} + +/** + * @tc.name: ExecResult_Unmarshalling_0200 + * @tc.desc: Test ExecResult unmarshalling failure branches with incomplete parcel data + * @tc.type: FUNC + */ +HWTEST_F(ExecResultTest, ExecResult_Unmarshalling_0200, TestSize.Level1) +{ + Parcel emptyParcel; + EXPECT_EQ(ExecResult::Unmarshalling(emptyParcel), nullptr); + + Parcel missingOutputParcel; + ASSERT_TRUE(missingOutputParcel.WriteInt32(TEST_EXIT_CODE)); + missingOutputParcel.RewindRead(0); + EXPECT_EQ(ExecResult::Unmarshalling(missingOutputParcel), nullptr); + + Parcel missingErrorParcel; + ASSERT_TRUE(missingErrorParcel.WriteInt32(TEST_EXIT_CODE)); + ASSERT_TRUE(missingErrorParcel.WriteString("stdout")); + missingErrorParcel.RewindRead(0); + EXPECT_EQ(ExecResult::Unmarshalling(missingErrorParcel), nullptr); + + Parcel missingSignalParcel; + ASSERT_TRUE(missingSignalParcel.WriteInt32(TEST_EXIT_CODE)); + ASSERT_TRUE(missingSignalParcel.WriteString("stdout")); + ASSERT_TRUE(missingSignalParcel.WriteString("stderr")); + missingSignalParcel.RewindRead(0); + EXPECT_EQ(ExecResult::Unmarshalling(missingSignalParcel), nullptr); + + Parcel missingTimedOutParcel; + ASSERT_TRUE(missingTimedOutParcel.WriteInt32(TEST_EXIT_CODE)); + ASSERT_TRUE(missingTimedOutParcel.WriteString("stdout")); + ASSERT_TRUE(missingTimedOutParcel.WriteString("stderr")); + ASSERT_TRUE(missingTimedOutParcel.WriteInt32(0)); + missingTimedOutParcel.RewindRead(0); + EXPECT_EQ(ExecResult::Unmarshalling(missingTimedOutParcel), nullptr); + + Parcel missingExecutionTimeParcel; + ASSERT_TRUE(missingExecutionTimeParcel.WriteInt32(TEST_EXIT_CODE)); + ASSERT_TRUE(missingExecutionTimeParcel.WriteString("stdout")); + ASSERT_TRUE(missingExecutionTimeParcel.WriteString("stderr")); + ASSERT_TRUE(missingExecutionTimeParcel.WriteInt32(0)); + ASSERT_TRUE(missingExecutionTimeParcel.WriteBool(false)); + missingExecutionTimeParcel.RewindRead(0); + EXPECT_EQ(ExecResult::Unmarshalling(missingExecutionTimeParcel), nullptr); +} +} // namespace CliTool +} // namespace OHOS diff --git a/cli_tool_framework/test/unittest/exec_tool_param_test/BUILD.gn b/cli_tool_framework/test/unittest/exec_tool_param_test/BUILD.gn new file mode 100644 index 0000000000..6d0d400df6 --- /dev/null +++ b/cli_tool_framework/test/unittest/exec_tool_param_test/BUILD.gn @@ -0,0 +1,46 @@ +# 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("exec_tool_param_test") { + module_out_path = module_output_path + + include_dirs = [ + "${ability_runtime_services_path}/common/include", + "${cli_tool_framework_path}/interfaces/cli_tool/include", + ] + + sources = [ + "exec_tool_param_test.cpp", + "${cli_tool_framework_path}/interfaces/cli_tool/src/exec_options.cpp", + "${cli_tool_framework_path}/interfaces/cli_tool/src/exec_tool_param.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 = [ ":exec_tool_param_test" ] +} diff --git a/cli_tool_framework/test/unittest/exec_tool_param_test/exec_tool_param_test.cpp b/cli_tool_framework/test/unittest/exec_tool_param_test/exec_tool_param_test.cpp new file mode 100644 index 0000000000..a726729fe0 --- /dev/null +++ b/cli_tool_framework/test/unittest/exec_tool_param_test/exec_tool_param_test.cpp @@ -0,0 +1,90 @@ +/* + * 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 +#include +#include + +#include "exec_tool_param.h" + +using namespace testing::ext; + +namespace OHOS { +namespace CliTool { +namespace { +constexpr int64_t TEST_TIMEOUT = 3000; +constexpr int64_t TEST_YIELD_MS = 50; +} + +class ExecToolParamTest : public testing::Test {}; + +/** + * @tc.name: ExecToolParam_Parcelable_0100 + * @tc.desc: Test ExecToolParam nested parcelable marshalling branches + * @tc.type: FUNC + */ +HWTEST_F(ExecToolParamTest, ExecToolParam_Parcelable_0100, TestSize.Level1) +{ + ExecToolParam param; + param.toolName = "tool"; + param.subcommand = "run"; + param.challenge = "challenge"; + param.options.background = true; + param.options.yieldMs = TEST_YIELD_MS; + param.options.timeout = TEST_TIMEOUT; + + Parcel parcel; + ASSERT_TRUE(param.Marshalling(parcel)); + parcel.RewindRead(0); + + std::unique_ptr result(ExecToolParam::Unmarshalling(parcel)); + ASSERT_NE(result, nullptr); + EXPECT_EQ(result->toolName, "tool"); + EXPECT_EQ(result->subcommand, "run"); + EXPECT_EQ(result->challenge, "challenge"); + EXPECT_TRUE(result->options.background); + EXPECT_EQ(result->options.timeout, TEST_TIMEOUT); + + Parcel missingOptionsParcel; + ASSERT_TRUE(missingOptionsParcel.WriteString("tool")); + ASSERT_TRUE(missingOptionsParcel.WriteString("run")); + ASSERT_TRUE(missingOptionsParcel.WriteString("challenge")); + missingOptionsParcel.RewindRead(0); + EXPECT_EQ(ExecToolParam::Unmarshalling(missingOptionsParcel), nullptr); + + Parcel emptyParcel; + EXPECT_EQ(ExecToolParam::Unmarshalling(emptyParcel), nullptr); + + Parcel missingSubcommandParcel; + ASSERT_TRUE(missingSubcommandParcel.WriteString("tool")); + missingSubcommandParcel.RewindRead(0); + EXPECT_EQ(ExecToolParam::Unmarshalling(missingSubcommandParcel), nullptr); + + Parcel missingChallengeParcel; + ASSERT_TRUE(missingChallengeParcel.WriteString("tool")); + ASSERT_TRUE(missingChallengeParcel.WriteString("run")); + missingChallengeParcel.RewindRead(0); + EXPECT_EQ(ExecToolParam::Unmarshalling(missingChallengeParcel), nullptr); + + Parcel missingArgsParcel; + ASSERT_TRUE(missingArgsParcel.WriteString("tool")); + ASSERT_TRUE(missingArgsParcel.WriteString("run")); + ASSERT_TRUE(missingArgsParcel.WriteString("challenge")); + ASSERT_TRUE(missingArgsParcel.WriteParcelable(¶m.options)); + missingArgsParcel.RewindRead(0); + EXPECT_EQ(ExecToolParam::Unmarshalling(missingArgsParcel), nullptr); +} +} // namespace CliTool +} // namespace OHOS diff --git a/cli_tool_framework/test/unittest/io_monitor_test/BUILD.gn b/cli_tool_framework/test/unittest/io_monitor_test/BUILD.gn new file mode 100644 index 0000000000..cf6217a535 --- /dev/null +++ b/cli_tool_framework/test/unittest/io_monitor_test/BUILD.gn @@ -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. + +import("//build/test.gni") +import("//foundation/ability/ability_runtime/ability_runtime.gni") + +module_output_path = "ability_runtime/ability_runtime/clitool" + +ohos_unittest("io_monitor_test") { + module_out_path = module_output_path + + include_dirs = [ + "${ability_runtime_services_path}/common/include", + "${cli_tool_framework_path}/services/climgr/include", + ] + + sources = [ + "io_monitor_test.cpp", + "${cli_tool_framework_path}/services/climgr/src/io_monitor.cpp", + ] + + external_deps = [ + "ffrt:libffrt", + "googletest:gmock_main", + "googletest:gtest_main", + "hilog:libhilog", + ] +} + +group("unittest") { + testonly = true + deps = [ ":io_monitor_test" ] +} diff --git a/cli_tool_framework/test/unittest/io_monitor_test/io_monitor_test.cpp b/cli_tool_framework/test/unittest/io_monitor_test/io_monitor_test.cpp new file mode 100644 index 0000000000..2b166c59aa --- /dev/null +++ b/cli_tool_framework/test/unittest/io_monitor_test/io_monitor_test.cpp @@ -0,0 +1,260 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define private public +#include "io_monitor.h" +#undef private + +using namespace testing::ext; + +namespace OHOS { +namespace CliTool { +namespace { +constexpr int32_t INVALID_FD = -1; + +void CloseFd(int &fd) +{ + if (fd >= 0) { + close(fd); + fd = INVALID_FD; + } +} +} + +class IOMonitorTest : public testing::Test {}; + +/** + * @tc.name: IOMonitor_StartStop_0100 + * @tc.desc: Test monitor start, repeated start, repeated stop and create branches + * @tc.type: FUNC + */ +HWTEST_F(IOMonitorTest, IOMonitor_StartStop_0100, TestSize.Level1) +{ + auto monitor = IOMonitor::Create(); + ASSERT_NE(monitor, nullptr); + + EXPECT_TRUE(monitor->Start()); + EXPECT_TRUE(monitor->Start()); + monitor->Stop(); + monitor->Stop(); +} + +/** + * @tc.name: IOMonitor_RegisterSession_0100 + * @tc.desc: Test register, stdin lookup, unregister and no-fd branches + * @tc.type: FUNC + */ +HWTEST_F(IOMonitorTest, IOMonitor_RegisterSession_0100, TestSize.Level1) +{ + auto monitor = IOMonitor::Create(); + ASSERT_NE(monitor, nullptr); + ASSERT_TRUE(monitor->Start()); + + int stdoutPipe[2] = {INVALID_FD, INVALID_FD}; + int stderrPipe[2] = {INVALID_FD, INVALID_FD}; + int stdinPipe[2] = {INVALID_FD, INVALID_FD}; + ASSERT_EQ(pipe(stdoutPipe), 0); + ASSERT_EQ(pipe(stderrPipe), 0); + ASSERT_EQ(pipe(stdinPipe), 0); + + EXPECT_TRUE(monitor->RegisterSession("session", stdoutPipe[0], stderrPipe[0], stdinPipe[1])); + stdoutPipe[0] = INVALID_FD; + stderrPipe[0] = INVALID_FD; + stdinPipe[1] = INVALID_FD; + EXPECT_EQ(monitor->GetStdinFd("session"), monitor->GetStdinFdLocked("session")); + EXPECT_GE(monitor->GetStdinFd("session"), 0); + + monitor->UnregisterSession("session"); + EXPECT_EQ(monitor->GetStdinFd("session"), INVALID_FD); + + EXPECT_TRUE(monitor->RegisterSession("no-fds", INVALID_FD, INVALID_FD, INVALID_FD)); + monitor->UnregisterSession("no-fds"); + monitor->Stop(); + + CloseFd(stdoutPipe[0]); + CloseFd(stdoutPipe[1]); + CloseFd(stderrPipe[0]); + CloseFd(stderrPipe[1]); + CloseFd(stdinPipe[0]); + CloseFd(stdinPipe[1]); +} + +/** + * @tc.name: IOMonitor_RegisterSession_0200 + * @tc.desc: Test register failure branches when epoll is unavailable or stderr registration fails + * @tc.type: FUNC + */ +HWTEST_F(IOMonitorTest, IOMonitor_RegisterSession_0200, TestSize.Level1) +{ + auto monitor = IOMonitor::Create(); + ASSERT_NE(monitor, nullptr); + + int stdoutPipe[2] = {INVALID_FD, INVALID_FD}; + ASSERT_EQ(pipe(stdoutPipe), 0); + EXPECT_FALSE(monitor->RegisterSession("session", stdoutPipe[0], INVALID_FD, INVALID_FD)); + CloseFd(stdoutPipe[0]); + CloseFd(stdoutPipe[1]); + + ASSERT_TRUE(monitor->Start()); + int rollbackStdoutPipe[2] = {INVALID_FD, INVALID_FD}; + int rollbackStderrPipe[2] = {INVALID_FD, INVALID_FD}; + ASSERT_EQ(pipe(rollbackStdoutPipe), 0); + ASSERT_EQ(pipe(rollbackStderrPipe), 0); + int closedStderrFd = rollbackStderrPipe[0]; + close(rollbackStderrPipe[0]); + rollbackStderrPipe[0] = INVALID_FD; + EXPECT_FALSE(monitor->RegisterSession("rollback-session", rollbackStdoutPipe[0], closedStderrFd, INVALID_FD)); + EXPECT_TRUE(monitor->fdMap_.empty()); + monitor->Stop(); + + CloseFd(rollbackStdoutPipe[0]); + CloseFd(rollbackStdoutPipe[1]); + CloseFd(rollbackStderrPipe[0]); + CloseFd(rollbackStderrPipe[1]); +} + +/** + * @tc.name: IOMonitor_WriteMessage_0100 + * @tc.desc: Test write message empty, success and invalid fd branches + * @tc.type: FUNC + */ +HWTEST_F(IOMonitorTest, IOMonitor_WriteMessage_0100, TestSize.Level1) +{ + auto monitor = IOMonitor::Create(); + ASSERT_NE(monitor, nullptr); + + EXPECT_TRUE(monitor->WriteMessage(INVALID_FD, "session", "")); + EXPECT_FALSE(monitor->WriteMessage(INVALID_FD, "session", "data")); + + int stdinPipe[2] = {INVALID_FD, INVALID_FD}; + ASSERT_EQ(pipe(stdinPipe), 0); + EXPECT_TRUE(monitor->WriteMessage(stdinPipe[1], "session", "hello")); + + char buffer[6] = {}; + ASSERT_EQ(read(stdinPipe[0], buffer, 5), 5); + EXPECT_EQ(std::string(buffer), "hello"); + + CloseFd(stdinPipe[0]); + CloseFd(stdinPipe[1]); +} + +/** + * @tc.name: IOMonitor_SendMessage_0100 + * @tc.desc: Test send message reject branch when session stdin is missing + * @tc.type: FUNC + */ +HWTEST_F(IOMonitorTest, IOMonitor_SendMessage_0100, TestSize.Level1) +{ + auto monitor = IOMonitor::Create(); + ASSERT_NE(monitor, nullptr); + + std::string callbackSessionId; + std::string callbackEventId; + bool callbackResult = true; + monitor->SetInputReplyCallback([&](const std::string &sessionId, const std::string &eventId, bool result) { + callbackSessionId = sessionId; + callbackEventId = eventId; + callbackResult = result; + }); + + monitor->SendMessage("missing-session", "payload", "event-id"); + EXPECT_EQ(callbackSessionId, "missing-session"); + EXPECT_EQ(callbackEventId, "event-id"); + EXPECT_FALSE(callbackResult); +} + +/** + * @tc.name: IOMonitor_ProcessWriteQueue_0100 + * @tc.desc: Test process write queue success path and queue cleanup + * @tc.type: FUNC + */ +HWTEST_F(IOMonitorTest, IOMonitor_ProcessWriteQueue_0100, TestSize.Level1) +{ + auto monitor = IOMonitor::Create(); + ASSERT_NE(monitor, nullptr); + + int stdinPipe[2] = {INVALID_FD, INVALID_FD}; + ASSERT_EQ(pipe(stdinPipe), 0); + monitor->fdMap_[stdinPipe[1]] = IOMonitor::FdInfo {"session", false, true}; + monitor->inputQueues_["session"].pendingInputs.emplace_back(IOMonitor::PendingInput {"hello", "event-id"}); + monitor->inputQueues_["session"].pendingBytes = 5; + monitor->inputQueues_["session"].writeTaskRunning = true; + + std::vector repliedEvents; + std::vector repliedResults; + monitor->SetInputReplyCallback([&](const std::string &, const std::string &eventId, bool result) { + repliedEvents.push_back(eventId); + repliedResults.push_back(result); + }); + + monitor->ProcessWriteQueue("session"); + + char buffer[6] = {}; + ASSERT_EQ(read(stdinPipe[0], buffer, 5), 5); + EXPECT_EQ(std::string(buffer), "hello"); + ASSERT_EQ(repliedEvents.size(), 1u); + EXPECT_EQ(repliedEvents[0], "event-id"); + EXPECT_TRUE(repliedResults[0]); + EXPECT_TRUE(monitor->inputQueues_.empty()); + + monitor->fdMap_.erase(stdinPipe[1]); + CloseFd(stdinPipe[0]); + CloseFd(stdinPipe[1]); +} + +/** + * @tc.name: IOMonitor_ProcessWriteQueue_0200 + * @tc.desc: Test process write queue failure drains pending inputs + * @tc.type: FUNC + */ +HWTEST_F(IOMonitorTest, IOMonitor_ProcessWriteQueue_0200, TestSize.Level1) +{ + auto monitor = IOMonitor::Create(); + ASSERT_NE(monitor, nullptr); + + monitor->inputQueues_["session"].pendingInputs.emplace_back(IOMonitor::PendingInput {"first", "event-1"}); + monitor->inputQueues_["session"].pendingInputs.emplace_back(IOMonitor::PendingInput {"second", "event-2"}); + monitor->inputQueues_["session"].pendingBytes = 11; + monitor->inputQueues_["session"].writeTaskRunning = true; + + std::vector repliedEvents; + std::vector repliedResults; + monitor->SetInputReplyCallback([&](const std::string &, const std::string &eventId, bool result) { + repliedEvents.push_back(eventId); + repliedResults.push_back(result); + }); + + monitor->ProcessWriteQueue("session"); + + ASSERT_EQ(repliedEvents.size(), 2u); + EXPECT_EQ(repliedEvents[0], "event-1"); + EXPECT_EQ(repliedEvents[1], "event-2"); + EXPECT_FALSE(repliedResults[0]); + EXPECT_FALSE(repliedResults[1]); + EXPECT_TRUE(monitor->inputQueues_.empty()); +} +} // namespace CliTool +} // namespace OHOS diff --git a/cli_tool_framework/test/unittest/permission_query_util_test/BUILD.gn b/cli_tool_framework/test/unittest/permission_query_util_test/BUILD.gn new file mode 100644 index 0000000000..9a62034f9e --- /dev/null +++ b/cli_tool_framework/test/unittest/permission_query_util_test/BUILD.gn @@ -0,0 +1,71 @@ +# 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("permission_query_util_test") { + module_out_path = module_output_path + + include_dirs = [ + "${cli_tool_framework_path}/test/unittest/common_mock/climgr_data/include", + "${ability_runtime_path}/cli_tool_framework/interfaces/cli_tool/include", + "${ability_runtime_path}/cli_tool_framework/services/climgr/include", + "${ability_runtime_path}/interfaces/kits/native/appkit/ability_bundle_manager_helper", + "${ability_runtime_services_path}/common/include", + "${cli_tool_framework_path}/services/common/include", + ] + + sources = [ + "permission_query_util_test.cpp", + "${cli_tool_framework_path}/test/unittest/common_mock/climgr_data/src/cli_tool_data_manager_mock.cpp", + "${cli_tool_framework_path}/services/climgr/src/permission_query_util.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 += [ "-DBINDER_IPC_32BIT" ] + } + + deps = [ + "${ability_runtime_path}/cli_tool_framework/interfaces/cli_tool:cli_tool_client", + ] + + external_deps = [ + "ability_base:want", + "access_token:libaccesstoken_sdk", + "access_token:libtokenid_sdk", + "bundle_framework:appexecfwk_base", + "bundle_framework:appexecfwk_core", + "c_utils:utils", + "googletest:gmock_main", + "googletest:gtest_main", + "hilog:libhilog", + "ipc:ipc_core", + "json:nlohmann_json_static", + "kv_store:distributeddata_inner", + "safwk:system_ability_fwk", + "samgr:samgr_proxy", + ] +} + +group("unittest") { + testonly = true + + deps = [ ":permission_query_util_test" ] +} diff --git a/cli_tool_framework/test/unittest/permission_query_util_test/permission_query_util_test.cpp b/cli_tool_framework/test/unittest/permission_query_util_test/permission_query_util_test.cpp new file mode 100644 index 0000000000..372aa4e6f4 --- /dev/null +++ b/cli_tool_framework/test/unittest/permission_query_util_test/permission_query_util_test.cpp @@ -0,0 +1,356 @@ +/* + * 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 +#include +#include +#include + +#define private public +#include "permission_query_util.h" +#undef private +#include "cli_error_code.h" +#include "cli_tool_data_manager_mock.h" +#include "icli_tool_data.h" +#include "tool_info.h" + +using namespace testing::ext; +using namespace OHOS::CliTool; + +namespace OHOS { +namespace CliTool { +namespace { +// Mock test data +constexpr const char* TEST_TOOL_NAME = "test_tool"; +constexpr const char* TEST_SUBCOMMAND = "build"; +constexpr const char* TEST_PERMISSION_1 = "ohos.permission.TEST_PERMISSION_1"; +constexpr const char* TEST_PERMISSION_2 = "ohos.permission.TEST_PERMISSION_2"; + +// Helper function to create a test Command +Command CreateTestCommand(const std::string& toolName, const std::string& subCommand = "") +{ + Command cmd; + cmd.toolName = toolName; + cmd.subCommand = subCommand; + return cmd; +} +} // namespace + +class PermissionQueryUtilTest : public testing::Test { +public: + static void SetUpTestCase(); + static void TearDownTestCase(); + void SetUp(); + void TearDown(); +}; + +void PermissionQueryUtilTest::SetUpTestCase() +{ + // Initialize test environment +} + +void PermissionQueryUtilTest::TearDownTestCase() +{ + // Cleanup test environment +} + +void PermissionQueryUtilTest::SetUp() +{ + CliToolDataManagerMock::Reset(); +} + +void PermissionQueryUtilTest::TearDown() +{ + // Cleanup after each test +} + +/** + * @tc.name: PermissionQueryUtil::BatchQueryPermissions_001 + * @tc.desc: Test batch query with empty command list + * @tc.type: FUNC + */ +HWTEST_F(PermissionQueryUtilTest, BatchQueryPermissions_EmptyCommands, TestSize.Level1) +{ + std::vector cmds; + std::vector cmdPermissions; + + int32_t ret = PermissionQueryUtil::BatchQueryPermissions(cmds, cmdPermissions); + + EXPECT_EQ(ret, ERR_OK); + EXPECT_EQ(cmdPermissions.size(), 0u); +} + +/** + * @tc.name: PermissionQueryUtil::BatchQueryPermissions_002 + * @tc.desc: Test batch query with non-existent tool + * @tc.type: FUNC + */ +HWTEST_F(PermissionQueryUtilTest, BatchQueryPermissions_ToolNotExist, TestSize.Level1) +{ + std::vector cmds; + cmds.push_back(CreateTestCommand("non_existent_tool")); + + std::vector cmdPermissions; + + int32_t ret = PermissionQueryUtil::BatchQueryPermissions(cmds, cmdPermissions); + + EXPECT_EQ(ret, ERR_OK); + EXPECT_EQ(cmdPermissions.size(), 1u); + EXPECT_EQ(cmdPermissions[0].queryRet, QueryResult::COMMAND_NOT_EXIST); + EXPECT_EQ(cmdPermissions[0].permissions.size(), 0u); +} + +/** + * @tc.name: PermissionQueryUtil::BatchQueryPermissions_003 + * @tc.desc: Test batch query with empty tool name + * @tc.type: FUNC + */ +HWTEST_F(PermissionQueryUtilTest, BatchQueryPermissions_EmptyToolName, TestSize.Level1) +{ + std::vector cmds; + Command cmd; + cmd.toolName = ""; + cmds.push_back(cmd); + + std::vector cmdPermissions; + + int32_t ret = PermissionQueryUtil::BatchQueryPermissions(cmds, cmdPermissions); + + EXPECT_EQ(ret, ERR_OK); + EXPECT_EQ(cmdPermissions.size(), 1u); + EXPECT_EQ(cmdPermissions[0].queryRet, QueryResult::COMMAND_NOT_EXIST); + EXPECT_EQ(cmdPermissions[0].permissions.size(), 0u); +} + +/** + * @tc.name: PermissionQueryUtil::BatchQueryPermissions_004 + * @tc.desc: Test batch query clears output parameter + * @tc.type: FUNC + */ +HWTEST_F(PermissionQueryUtilTest, BatchQueryPermissions_ClearsOutputParameter, TestSize.Level1) +{ + std::vector cmdPermissions; + CommandPermission oldPerm; + oldPerm.cmd.toolName = "old_tool"; + oldPerm.queryRet = 99; + oldPerm.permissions.push_back("old_permission"); + cmdPermissions.push_back(oldPerm); + + EXPECT_EQ(cmdPermissions.size(), 1u); + EXPECT_EQ(cmdPermissions[0].cmd.toolName, "old_tool"); + + std::vector cmds; + int32_t ret = PermissionQueryUtil::BatchQueryPermissions(cmds, cmdPermissions); + + EXPECT_EQ(ret, ERR_OK); + EXPECT_EQ(cmdPermissions.size(), 0u); +} + +/** + * @tc.name: PermissionQueryUtil::BatchQueryPermissions_005 + * @tc.desc: Test batch query with multiple commands + * @tc.type: FUNC + */ +HWTEST_F(PermissionQueryUtilTest, BatchQueryPermissions_MultipleCommands, TestSize.Level1) +{ + std::vector cmds; + cmds.push_back(CreateTestCommand("tool1")); + cmds.push_back(CreateTestCommand("tool2", "sub1")); + cmds.push_back(CreateTestCommand("tool3")); + + std::vector cmdPermissions; + + int32_t ret = PermissionQueryUtil::BatchQueryPermissions(cmds, cmdPermissions); + + EXPECT_EQ(ret, ERR_OK); + EXPECT_EQ(cmdPermissions.size(), 3u); + + EXPECT_EQ(cmdPermissions[0].queryRet, QueryResult::COMMAND_NOT_EXIST); + EXPECT_EQ(cmdPermissions[0].permissions.size(), 0u); + + EXPECT_EQ(cmdPermissions[1].queryRet, QueryResult::COMMAND_NOT_EXIST); + EXPECT_EQ(cmdPermissions[1].permissions.size(), 0u); + + EXPECT_EQ(cmdPermissions[2].queryRet, QueryResult::COMMAND_NOT_EXIST); + EXPECT_EQ(cmdPermissions[2].permissions.size(), 0u); +} + +/** + * @tc.name: PermissionQueryUtil::QuerySingleCommand_001 + * @tc.desc: Test QuerySingleCommand with empty tool name + * @tc.type: FUNC + */ +HWTEST_F(PermissionQueryUtilTest, QuerySingleCommand_EmptyToolName, TestSize.Level1) +{ + Command cmd; + cmd.toolName = ""; + std::vector permissions; + + int32_t ret = PermissionQueryUtil::QuerySingleCommand(cmd, permissions); + + EXPECT_EQ(ret, ERR_TOOL_NOT_EXIST); + EXPECT_EQ(permissions.size(), 0u); +} + +/** + * @tc.name: PermissionQueryUtil::QuerySingleCommand_002 + * @tc.desc: Test QuerySingleCommand with non-existent tool + * @tc.type: FUNC + */ +HWTEST_F(PermissionQueryUtilTest, QuerySingleCommand_ToolNotExist, TestSize.Level1) +{ + Command cmd = CreateTestCommand("non_existent_tool"); + std::vector permissions; + + int32_t ret = PermissionQueryUtil::QuerySingleCommand(cmd, permissions); + + EXPECT_EQ(ret, ERR_TOOL_NOT_EXIST); + EXPECT_EQ(permissions.size(), 0u); +} + +/** + * @tc.name: PermissionQueryUtil::QuerySingleCommand_003 + * @tc.desc: Test QuerySingleCommand returns permissions for existing main command + * @tc.type: FUNC + */ +HWTEST_F(PermissionQueryUtilTest, QuerySingleCommand_MainCommandSuccess, TestSize.Level1) +{ + CliToolDataManagerMock::getToolByNameResult = ERR_OK; + CliToolDataManagerMock::toolPermissions = {TEST_PERMISSION_1, TEST_PERMISSION_2}; + Command cmd = CreateTestCommand(TEST_TOOL_NAME); + std::vector permissions; + + int32_t ret = PermissionQueryUtil::QuerySingleCommand(cmd, permissions); + + EXPECT_EQ(ret, ERR_OK); + EXPECT_EQ(permissions.size(), 2u); + EXPECT_EQ(permissions[0], TEST_PERMISSION_1); + EXPECT_EQ(permissions[1], TEST_PERMISSION_2); +} + +/** + * @tc.name: PermissionQueryUtil::QuerySingleCommand_004 + * @tc.desc: Test QuerySingleCommand returns permissions for existing subcommand + * @tc.type: FUNC + */ +HWTEST_F(PermissionQueryUtilTest, QuerySingleCommand_SubCommandSuccess, TestSize.Level1) +{ + CliToolDataManagerMock::getToolByNameResult = ERR_OK; + CliToolDataManagerMock::toolHasSubCommand = true; + CliToolDataManagerMock::subCommandName = TEST_SUBCOMMAND; + CliToolDataManagerMock::subCommandPermissions = {TEST_PERMISSION_2}; + Command cmd = CreateTestCommand(TEST_TOOL_NAME, TEST_SUBCOMMAND); + std::vector permissions; + + int32_t ret = PermissionQueryUtil::QuerySingleCommand(cmd, permissions); + + EXPECT_EQ(ret, ERR_OK); + ASSERT_EQ(permissions.size(), 1u); + EXPECT_EQ(permissions[0], TEST_PERMISSION_2); +} + +/** + * @tc.name: PermissionQueryUtil::QuerySingleCommand_005 + * @tc.desc: Test subcommand query fails when tool has no subcommands + * @tc.type: FUNC + */ +HWTEST_F(PermissionQueryUtilTest, QuerySingleCommand_NoSubCommand, TestSize.Level1) +{ + CliToolDataManagerMock::getToolByNameResult = ERR_OK; + CliToolDataManagerMock::toolHasSubCommand = false; + Command cmd = CreateTestCommand(TEST_TOOL_NAME, TEST_SUBCOMMAND); + std::vector permissions = {TEST_PERMISSION_1}; + + int32_t ret = PermissionQueryUtil::QuerySingleCommand(cmd, permissions); + + EXPECT_EQ(ret, ERR_TOOL_NOT_EXIST); +} + +/** + * @tc.name: PermissionQueryUtil::QuerySingleCommand_006 + * @tc.desc: Test database error is preserved by single command query + * @tc.type: FUNC + */ +HWTEST_F(PermissionQueryUtilTest, QuerySingleCommand_DbError, TestSize.Level1) +{ + CliToolDataManagerMock::getToolByNameResult = ERR_NO_INIT; + Command cmd = CreateTestCommand(TEST_TOOL_NAME); + std::vector permissions; + + int32_t ret = PermissionQueryUtil::QuerySingleCommand(cmd, permissions); + + EXPECT_EQ(ret, ERR_NO_INIT); +} + +/** + * @tc.name: PermissionQueryUtil::BatchQueryPermissions_006 + * @tc.desc: Test batch query maps database error to DB_ERROR result + * @tc.type: FUNC + */ +HWTEST_F(PermissionQueryUtilTest, BatchQueryPermissions_DbError, TestSize.Level1) +{ + CliToolDataManagerMock::getToolByNameResult = ERR_NO_INIT; + std::vector cmds = {CreateTestCommand(TEST_TOOL_NAME)}; + std::vector cmdPermissions; + + int32_t ret = PermissionQueryUtil::BatchQueryPermissions(cmds, cmdPermissions); + + EXPECT_EQ(ret, ERR_OK); + ASSERT_EQ(cmdPermissions.size(), 1u); + EXPECT_EQ(cmdPermissions[0].queryRet, QueryResult::DB_ERROR); + EXPECT_TRUE(cmdPermissions[0].permissions.empty()); +} + +/** + * @tc.name: PermissionQueryUtil::BuildCommandPermission_001 + * @tc.desc: Test BuildCommandPermission creates correct object + * @tc.type: FUNC + */ +HWTEST_F(PermissionQueryUtilTest, BuildCommandPermission_CreatesCorrectObject, TestSize.Level1) +{ + Command cmd = CreateTestCommand(TEST_TOOL_NAME, TEST_SUBCOMMAND); + std::vector permissions = {TEST_PERMISSION_1, TEST_PERMISSION_2}; + int32_t queryRet = QueryResult::SUCCESS; + + CommandPermission cmdPerm = PermissionQueryUtil::BuildCommandPermission(cmd, permissions, queryRet); + + EXPECT_EQ(cmdPerm.cmd.toolName, TEST_TOOL_NAME); + EXPECT_EQ(cmdPerm.cmd.subCommand, TEST_SUBCOMMAND); + EXPECT_EQ(cmdPerm.queryRet, QueryResult::SUCCESS); + EXPECT_EQ(cmdPerm.permissions.size(), 2u); + EXPECT_EQ(cmdPerm.permissions[0], TEST_PERMISSION_1); + EXPECT_EQ(cmdPerm.permissions[1], TEST_PERMISSION_2); +} + +/** + * @tc.name: PermissionQueryUtil::BuildCommandPermission_002 + * @tc.desc: Test BuildCommandPermission with empty permissions + * @tc.type: FUNC + */ +HWTEST_F(PermissionQueryUtilTest, BuildCommandPermission_EmptyPermissions, TestSize.Level1) +{ + Command cmd = CreateTestCommand(TEST_TOOL_NAME); + std::vector permissions; + int32_t queryRet = QueryResult::COMMAND_NOT_EXIST; + + CommandPermission cmdPerm = PermissionQueryUtil::BuildCommandPermission(cmd, permissions, queryRet); + + EXPECT_EQ(cmdPerm.cmd.toolName, TEST_TOOL_NAME); + EXPECT_EQ(cmdPerm.cmd.subCommand, ""); + EXPECT_EQ(cmdPerm.queryRet, QueryResult::COMMAND_NOT_EXIST); + EXPECT_EQ(cmdPerm.permissions.size(), 0u); +} +} // namespace CliTool +} // namespace OHOS diff --git a/test/unittest/cli_tool_mgr/process_manager_test/BUILD.gn b/cli_tool_framework/test/unittest/process_manager_test/BUILD.gn similarity index 72% rename from test/unittest/cli_tool_mgr/process_manager_test/BUILD.gn rename to cli_tool_framework/test/unittest/process_manager_test/BUILD.gn index 0c6775415e..4c937445ee 100644 --- a/test/unittest/cli_tool_mgr/process_manager_test/BUILD.gn +++ b/cli_tool_framework/test/unittest/process_manager_test/BUILD.gn @@ -20,14 +20,18 @@ ohos_unittest("process_manager_test") { module_out_path = module_output_path include_dirs = [ - "${ability_runtime_path}/cli_tool_framework/services/climgr/include", "${ability_runtime_path}/cli_tool_framework/interfaces/cli_tool/include", + "${ability_runtime_path}/cli_tool_framework/services/climgr/include", + "${ability_runtime_path}/interfaces/kits/native/appkit/ability_bundle_manager_helper", "${ability_runtime_services_path}/common/include", + "${cli_tool_framework_path}/services/common/include", ] sources = [ "process_manager_test.cpp", "${cli_tool_framework_path}/services/climgr/src/process_manager.cpp", + "${cli_tool_framework_path}/services/climgr/src/tool_util.cpp", + "${cli_tool_framework_path}/services/common/src/permission_util.cpp", ] cflags = [] @@ -36,11 +40,18 @@ ohos_unittest("process_manager_test") { } deps = [ - "${ability_runtime_path}/cli_tool_framework/services/climgr:climgr", + "${ability_runtime_innerkits_path}/ability_manager:ability_manager", + "${ability_runtime_native_path}/appkit:appkit_manager_helper", "${ability_runtime_path}/cli_tool_framework/interfaces/cli_tool:cli_tool_client", + "${ability_runtime_path}/cli_tool_framework/services/climgr:climgr", ] external_deps = [ + "ability_base:want", + "access_token:libaccesstoken_sdk", + "access_token:libtokenid_sdk", + "bundle_framework:appexecfwk_base", + "bundle_framework:appexecfwk_core", "c_utils:utils", "googletest:gmock_main", "googletest:gtest_main", @@ -48,6 +59,8 @@ ohos_unittest("process_manager_test") { "ipc:ipc_core", "json:nlohmann_json_static", "kv_store:distributeddata_inner", + "safwk:system_ability_fwk", + "samgr:samgr_proxy", ] } diff --git a/test/unittest/cli_tool_mgr/process_manager_test/process_manager_test.cpp b/cli_tool_framework/test/unittest/process_manager_test/process_manager_test.cpp similarity index 82% rename from test/unittest/cli_tool_mgr/process_manager_test/process_manager_test.cpp rename to cli_tool_framework/test/unittest/process_manager_test/process_manager_test.cpp index b6a77ccd3a..a09feb8879 100644 --- a/test/unittest/cli_tool_mgr/process_manager_test/process_manager_test.cpp +++ b/cli_tool_framework/test/unittest/process_manager_test/process_manager_test.cpp @@ -13,12 +13,15 @@ * limitations under the License. */ -#include #include +#include -#include "process_manager.h" #include "cli_error_code.h" #include "exec_tool_param.h" +#define private public +#include "process_manager.h" +#undef private +#include "tool_info.h" using namespace testing::ext; using namespace OHOS::CliTool; @@ -33,7 +36,8 @@ public: void SetUp(); void TearDown(); - ExecToolParam CreateTestParam(const std::string& toolName, const std::string& subcommand = ""); + ExecToolParam CreateTestParam(const std::string &toolName, const std::string &subcommand = ""); + ToolInfo CreateTestToolInfo(const std::string &toolName); }; void ProcessManagerTest::SetUpTestCase(void) @@ -56,7 +60,7 @@ void ProcessManagerTest::TearDown() // Cleanup after each test } -ExecToolParam ProcessManagerTest::CreateTestParam(const std::string& toolName, const std::string& subcommand) +ExecToolParam ProcessManagerTest::CreateTestParam(const std::string &toolName, const std::string &subcommand) { ExecToolParam param; param.toolName = toolName; @@ -65,6 +69,14 @@ ExecToolParam ProcessManagerTest::CreateTestParam(const std::string& toolName, c return param; } +ToolInfo ProcessManagerTest::CreateTestToolInfo(const std::string &toolName) +{ + ToolInfo toolInfo; + toolInfo.name = toolName; + toolInfo.executablePath = "/system/bin/" + toolName; + return toolInfo; +} + /** * @tc.name: ProcessManager_GetInstance_0100 * @tc.desc: Test GetInstance returns singleton instance @@ -95,13 +107,13 @@ HWTEST_F(ProcessManagerTest, CreateChildProcess_0100, TestSize.Level1) ExecToolParam param = CreateTestParam("test_tool"); std::string sandboxConfig = "/etc/claw/test_config.json"; - std::map args; + ToolInfo toolInfo = CreateTestToolInfo("test_tool"); auto record = std::make_shared(); EXPECT_NE(record, nullptr); record->sessionId = "test_tool"; record->toolName = "test_tool"; - int32_t result = manager.CreateChildProcess(param, sandboxConfig, args, record); + int32_t result = manager.CreateChildProcess(param, sandboxConfig, toolInfo, record); // In test environment, fork will succeed and create a child process // The child process will then execvp which may fail if claw_sandbox doesn't exist @@ -124,13 +136,13 @@ HWTEST_F(ProcessManagerTest, CreateChildProcess_0200, TestSize.Level1) ExecToolParam param = CreateTestParam("test_tool", "build"); std::string sandboxConfig = "/etc/claw/test_config.json"; - std::map args; + ToolInfo toolInfo = CreateTestToolInfo("test_tool"); auto record = std::make_shared(); EXPECT_NE(record, nullptr); record->sessionId = "test_tool"; record->toolName = "test_tool"; - int32_t result = manager.CreateChildProcess(param, sandboxConfig, args, record); + int32_t result = manager.CreateChildProcess(param, sandboxConfig, toolInfo, record); EXPECT_EQ(result, ERR_OK); @@ -150,15 +162,13 @@ HWTEST_F(ProcessManagerTest, CreateChildProcess_0300, TestSize.Level1) ExecToolParam param = CreateTestParam("test_tool", "run"); std::string sandboxConfig = "/etc/claw/test_config.json"; - std::map args; - args["--verbose"] = "true"; - args["--output"] = "/tmp/output.txt"; + ToolInfo toolInfo = CreateTestToolInfo("test_tool"); auto record = std::make_shared(); EXPECT_NE(record, nullptr); record->sessionId = "test_tool"; record->toolName = "test_tool"; - int32_t result = manager.CreateChildProcess(param, sandboxConfig, args, record); + int32_t result = manager.CreateChildProcess(param, sandboxConfig, toolInfo, record); EXPECT_EQ(result, ERR_OK); @@ -178,13 +188,13 @@ HWTEST_F(ProcessManagerTest, CreateChildProcess_0400, TestSize.Level1) ExecToolParam param = CreateTestParam(""); std::string sandboxConfig = "/etc/claw/test_config.json"; - std::map args; + ToolInfo toolInfo = CreateTestToolInfo(""); auto record = std::make_shared(); EXPECT_NE(record, nullptr); record->sessionId = "test_tool"; record->toolName = "test_tool"; - int32_t result = manager.CreateChildProcess(param, sandboxConfig, args, record); + int32_t result = manager.CreateChildProcess(param, sandboxConfig, toolInfo, record); // Empty tool name should still work (will just execute claw_sandbox with empty cmd) EXPECT_EQ(result, ERR_OK); @@ -205,17 +215,13 @@ HWTEST_F(ProcessManagerTest, CreateChildProcess_0500, TestSize.Level1) ExecToolParam param = CreateTestParam("complex_tool", "deploy"); std::string sandboxConfig = "/etc/claw/complex_config.json"; - std::map args; - args["--env"] = "production"; - args["--region"] = "us-west-2"; - args["--instances"] = "3"; - args["--force"] = "true"; + ToolInfo toolInfo = CreateTestToolInfo("complex_tool"); auto record = std::make_shared(); EXPECT_NE(record, nullptr); record->sessionId = "complex_tool"; record->toolName = "complex_tool"; - int32_t result = manager.CreateChildProcess(param, sandboxConfig, args, record); + int32_t result = manager.CreateChildProcess(param, sandboxConfig, toolInfo, record); EXPECT_EQ(result, ERR_OK); @@ -235,13 +241,13 @@ HWTEST_F(ProcessManagerTest, CreateChildProcess_0600, TestSize.Level1) ExecToolParam param = CreateTestParam("test_tool"); std::string sandboxConfig = ""; - std::map args; + ToolInfo toolInfo = CreateTestToolInfo("test_tool"); auto record = std::make_shared(); EXPECT_NE(record, nullptr); record->sessionId = "test_tool"; record->toolName = "test_tool"; - int32_t result = manager.CreateChildProcess(param, sandboxConfig, args, record); + int32_t result = manager.CreateChildProcess(param, sandboxConfig, toolInfo, record); EXPECT_EQ(result, ERR_OK); @@ -261,13 +267,13 @@ HWTEST_F(ProcessManagerTest, CreateChildProcess_0700, TestSize.Level1) ExecToolParam param = CreateTestParam("simple_tool"); std::string sandboxConfig = "/etc/claw/simple_config.json"; - std::map args; + ToolInfo toolInfo = CreateTestToolInfo("simple_tool"); auto record = std::make_shared(); EXPECT_NE(record, nullptr); record->sessionId = "simple_tool"; record->toolName = "simple_tool"; - int32_t result = manager.CreateChildProcess(param, sandboxConfig, args, record); + int32_t result = manager.CreateChildProcess(param, sandboxConfig, toolInfo, record); EXPECT_EQ(result, ERR_OK); @@ -307,9 +313,7 @@ HWTEST_F(ProcessManagerTest, CommandLineConstruction_0100, TestSize.Level1) ExecToolParam param = CreateTestParam("my_tool", "subcommand1"); std::string sandboxConfig = "/etc/claw/config.json"; - std::map args; - args["arg1"] = "value1"; - args["arg2"] = "value2"; + ToolInfo toolInfo = CreateTestToolInfo("my_tool"); auto record = std::make_shared(); EXPECT_NE(record, nullptr); @@ -317,7 +321,7 @@ HWTEST_F(ProcessManagerTest, CommandLineConstruction_0100, TestSize.Level1) record->toolName = "my_tool"; // The command line should be: "my_tool subcommand1 arg1 value1 arg2 value2" // We can't directly verify this without fork/exec, but we can verify the call succeeds - int32_t result = manager.CreateChildProcess(param, sandboxConfig, args, record); + int32_t result = manager.CreateChildProcess(param, sandboxConfig, toolInfo, record); EXPECT_EQ(result, ERR_OK); @@ -337,17 +341,13 @@ HWTEST_F(ProcessManagerTest, ArgumentOrder_0100, TestSize.Level1) ExecToolParam param = CreateTestParam("ordered_tool"); std::string sandboxConfig = "/etc/claw/config.json"; - std::map args; - // Note: std::map maintains sorted order, not insertion order - args["z-last"] = "last_value"; - args["a-first"] = "first_value"; - args["m-middle"] = "middle_value"; + ToolInfo toolInfo = CreateTestToolInfo("ordered_tool"); auto record = std::make_shared(); EXPECT_NE(record, nullptr); record->sessionId = "ordered_tool"; record->toolName = "ordered_tool"; - int32_t result = manager.CreateChildProcess(param, sandboxConfig, args, record); + int32_t result = manager.CreateChildProcess(param, sandboxConfig, toolInfo, record); EXPECT_EQ(result, ERR_OK); @@ -367,19 +367,58 @@ HWTEST_F(ProcessManagerTest, ConstCorrectness_0100, TestSize.Level1) ExecToolParam param = CreateTestParam("const_test_tool"); std::string sandboxConfig = "/etc/claw/const_config.json"; - std::map args; + ToolInfo toolInfo = CreateTestToolInfo("const_test_tool"); auto record = std::make_shared(); EXPECT_NE(record, nullptr); record->sessionId = "const_test_tool"; record->toolName = "const_test_tool"; // This should compile and work because CreateChildProcess is const - int32_t result = manager.CreateChildProcess(param, sandboxConfig, args, record); + int32_t result = manager.CreateChildProcess(param, sandboxConfig, toolInfo, record); EXPECT_EQ(result, ERR_OK); GTEST_LOG_(INFO) << "ProcessManager_ConstCorrectness_0100 end"; } +/** + * @tc.name: ProcessManager_CreatePipes_0100 + * @tc.desc: Test private pipe creation and cleanup helpers + * @tc.type: FUNC + */ +HWTEST_F(ProcessManagerTest, CreatePipes_0100, TestSize.Level1) +{ + auto& manager = ProcessManager::GetInstance(); + SessionRecord record; + + EXPECT_TRUE(manager.CreatePipes(record)); + EXPECT_NE(record.stdinPipe[0], -1); + EXPECT_NE(record.stdinPipe[1], -1); + EXPECT_NE(record.stdoutPipe[0], -1); + EXPECT_NE(record.stdoutPipe[1], -1); + EXPECT_NE(record.stderrPipe[0], -1); + EXPECT_NE(record.stderrPipe[1], -1); + + manager.CloseAllPipes(record); + EXPECT_EQ(record.stdinPipe[0], -1); + EXPECT_EQ(record.stdinPipe[1], -1); + EXPECT_EQ(record.stdoutPipe[0], -1); + EXPECT_EQ(record.stdoutPipe[1], -1); + EXPECT_EQ(record.stderrPipe[0], -1); + EXPECT_EQ(record.stderrPipe[1], -1); +} + +/** + * @tc.name: ProcessManager_Killpg_0100 + * @tc.desc: Test Killpg false branch with a non-existent process group + * @tc.type: FUNC + */ +HWTEST_F(ProcessManagerTest, Killpg_0100, TestSize.Level1) +{ + auto& manager = ProcessManager::GetInstance(); + + EXPECT_FALSE(manager.Killpg(999999)); +} + } // namespace CliTool } // namespace OHOS diff --git a/test/unittest/cli_tool_mgr/cli_tool_mgr_client_test/BUILD.gn b/cli_tool_framework/test/unittest/session_record_test/BUILD.gn similarity index 76% rename from test/unittest/cli_tool_mgr/cli_tool_mgr_client_test/BUILD.gn rename to cli_tool_framework/test/unittest/session_record_test/BUILD.gn index 360e951f94..9f1ad56a26 100644 --- a/test/unittest/cli_tool_mgr/cli_tool_mgr_client_test/BUILD.gn +++ b/cli_tool_framework/test/unittest/session_record_test/BUILD.gn @@ -16,12 +16,18 @@ import("//foundation/ability/ability_runtime/ability_runtime.gni") module_output_path = "ability_runtime/ability_runtime/clitool" -ohos_unittest("cli_tool_mgr_client_test") { +ohos_unittest("session_record_test") { module_out_path = module_output_path - include_dirs = [ "${cli_tool_framework_path}/interfaces/cli_tool/include" ] + include_dirs = [ + "${cli_tool_framework_path}/interfaces/cli_tool/include", + "${cli_tool_framework_path}/services/climgr/include", + ] - sources = [ "cli_tool_mgr_client_test.cpp" ] + sources = [ + "session_record_test.cpp", + "${cli_tool_framework_path}/services/climgr/src/session_record.cpp", + ] cflags = [] if (target_cpu == "arm") { @@ -31,6 +37,7 @@ ohos_unittest("cli_tool_mgr_client_test") { 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", @@ -42,5 +49,5 @@ ohos_unittest("cli_tool_mgr_client_test") { group("unittest") { testonly = true - deps = [ ":cli_tool_mgr_client_test" ] + deps = [ ":session_record_test" ] } diff --git a/cli_tool_framework/test/unittest/session_record_test/session_record_test.cpp b/cli_tool_framework/test/unittest/session_record_test/session_record_test.cpp new file mode 100644 index 0000000000..fd4275c728 --- /dev/null +++ b/cli_tool_framework/test/unittest/session_record_test/session_record_test.cpp @@ -0,0 +1,189 @@ +/* + * 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 +#include + +#include "session_record.h" + +using namespace testing::ext; + +namespace OHOS { +namespace CliTool { +namespace { +constexpr int32_t TEST_STATUS_OK = 0; +constexpr int32_t TEST_STATUS_FAILED = 2; +constexpr int32_t TEST_SIGNAL = 15; +constexpr int32_t TEST_TIMEOUT_MS = 5000; +constexpr size_t MAX_BUFFERED_OUTPUT_BYTES = 64 * 1024; +} + +class SessionRecordTest : public testing::Test {}; + +/** + * @tc.name: SessionRecord_State_0100 + * @tc.desc: Test state, cleanup and background branches + * @tc.type: FUNC + */ +HWTEST_F(SessionRecordTest, SessionRecord_State_0100, TestSize.Level1) +{ + SessionRecord record; + EXPECT_EQ(record.GetState(), SessionState::SPAWNING); + + record.SetState(SessionState::RUNNING); + EXPECT_EQ(record.GetState(), SessionState::RUNNING); + + EXPECT_TRUE(record.Background()); + EXPECT_TRUE(record.SetBackground(false)); + EXPECT_FALSE(record.Background()); + EXPECT_FALSE(record.SetBackground(true)); + EXPECT_TRUE(record.Background()); + + EXPECT_TRUE(record.BeginCleanup()); + EXPECT_FALSE(record.BeginCleanup()); +} + +/** + * @tc.name: SessionRecord_OutputAndTerminal_0100 + * @tc.desc: Test output append, close flags and terminal result branches + * @tc.type: FUNC + */ +HWTEST_F(SessionRecordTest, SessionRecord_OutputAndTerminal_0100, TestSize.Level1) +{ + SessionRecord record; + EXPECT_FALSE(record.OutputDrained()); + record.MarkStdoutClosed(); + EXPECT_FALSE(record.OutputDrained()); + record.MarkStderrClosed(); + EXPECT_TRUE(record.OutputDrained()); + + EXPECT_FALSE(record.HasProcessExited()); + record.AppendOutput(true, "stdout"); + record.AppendOutput(false, "stderr"); + record.SetTerminalResult(TEST_STATUS_FAILED, TEST_SIGNAL); + + EXPECT_TRUE(record.HasProcessExited()); + EXPECT_EQ(record.GetTerminalStatus(), TEST_STATUS_FAILED); + EXPECT_GT(record.GetEndTimeMs(), 0); + + CliSessionInfo session; + record.BuildSessionInfo(session); + ASSERT_NE(session.result, nullptr); + EXPECT_EQ(session.status, "failed"); + EXPECT_EQ(session.result->exitCode, TEST_STATUS_FAILED); + EXPECT_EQ(session.result->signalNumber, TEST_SIGNAL); + EXPECT_EQ(session.result->outputText, "stdout"); + EXPECT_EQ(session.result->errorText, "stderr"); +} + +/** + * @tc.name: SessionRecord_BuildSessionInfo_0100 + * @tc.desc: Test running, completed, failed and timeout session info branches + * @tc.type: FUNC + */ +HWTEST_F(SessionRecordTest, SessionRecord_BuildSessionInfo_0100, TestSize.Level1) +{ + SessionRecord runningRecord; + runningRecord.sessionId = "running-session"; + runningRecord.toolName = "tool"; + CliSessionInfo runningSession; + runningRecord.BuildSessionInfo(runningSession); + EXPECT_EQ(runningSession.sessionId, "running-session"); + EXPECT_EQ(runningSession.toolName, "tool"); + EXPECT_EQ(runningSession.status, "running"); + EXPECT_EQ(runningSession.result, nullptr); + + SessionRecord completedRecord; + completedRecord.startTime = 1; + completedRecord.MarkStdoutClosed(); + completedRecord.MarkStderrClosed(); + completedRecord.SetTerminalResult(TEST_STATUS_OK, 0); + CliSessionInfo completedSession; + completedRecord.BuildSessionInfo(completedSession); + ASSERT_NE(completedSession.result, nullptr); + EXPECT_EQ(completedSession.status, "completed"); + EXPECT_EQ(completedSession.result->exitCode, TEST_STATUS_OK); + EXPECT_FALSE(completedSession.result->timedOut); + EXPECT_GT(completedSession.result->executionTime, 0); + + SessionRecord failedRecord; + failedRecord.MarkStdoutClosed(); + failedRecord.MarkStderrClosed(); + failedRecord.SetTerminalResult(TEST_STATUS_FAILED, 0); + CliSessionInfo failedSession; + failedRecord.BuildSessionInfo(failedSession); + ASSERT_NE(failedSession.result, nullptr); + EXPECT_EQ(failedSession.status, "failed"); + EXPECT_EQ(failedSession.result->exitCode, TEST_STATUS_FAILED); + + SessionRecord timeoutRecord; + timeoutRecord.timeoutMs = TEST_TIMEOUT_MS; + timeoutRecord.SetTimedOut(true); + EXPECT_TRUE(timeoutRecord.TimedOut()); + CliSessionInfo timeoutSession; + timeoutRecord.BuildSessionInfo(timeoutSession); + ASSERT_NE(timeoutSession.result, nullptr); + EXPECT_EQ(timeoutSession.status, "failed"); + EXPECT_TRUE(timeoutSession.result->timedOut); + EXPECT_EQ(timeoutSession.result->executionTime, TEST_TIMEOUT_MS); +} + +/** + * @tc.name: SessionRecord_SetSkillResult_0100 + * @tc.desc: Test skill result closes output and builds terminal result + * @tc.type: FUNC + */ +HWTEST_F(SessionRecordTest, SessionRecord_SetSkillResult_0100, TestSize.Level1) +{ + SessionRecord record; + record.SetSkillResult(TEST_STATUS_OK, "skill output"); + + EXPECT_TRUE(record.HasProcessExited()); + EXPECT_TRUE(record.OutputDrained()); + EXPECT_EQ(record.GetTerminalStatus(), TEST_STATUS_OK); + + CliSessionInfo session; + record.BuildSessionInfo(session); + ASSERT_NE(session.result, nullptr); + EXPECT_EQ(session.status, "completed"); + EXPECT_EQ(session.result->outputText, "skill output"); +} + +/** + * @tc.name: SessionRecord_TrimBufferedOutput_0100 + * @tc.desc: Test large stdout and stderr buffers keep only the latest bytes + * @tc.type: FUNC + */ +HWTEST_F(SessionRecordTest, SessionRecord_TrimBufferedOutput_0100, TestSize.Level1) +{ + SessionRecord record; + std::string prefix(10, 'a'); + std::string tail(MAX_BUFFERED_OUTPUT_BYTES, 'b'); + record.AppendOutput(true, prefix + tail); + record.AppendOutput(false, prefix + tail); + record.MarkStdoutClosed(); + record.MarkStderrClosed(); + record.SetTerminalResult(TEST_STATUS_FAILED, 0); + + CliSessionInfo session; + record.BuildSessionInfo(session); + ASSERT_NE(session.result, nullptr); + EXPECT_EQ(session.result->outputText.size(), MAX_BUFFERED_OUTPUT_BYTES); + EXPECT_EQ(session.result->errorText.size(), MAX_BUFFERED_OUTPUT_BYTES); + EXPECT_EQ(session.result->outputText, tail); + EXPECT_EQ(session.result->errorText, tail); +} +} // namespace CliTool +} // namespace OHOS diff --git a/test/unittest/cli_tool_mgr/sub_command_info_test/BUILD.gn b/cli_tool_framework/test/unittest/sub_command_info_test/BUILD.gn similarity index 100% rename from test/unittest/cli_tool_mgr/sub_command_info_test/BUILD.gn rename to cli_tool_framework/test/unittest/sub_command_info_test/BUILD.gn diff --git a/test/unittest/cli_tool_mgr/sub_command_info_test/sub_command_info_test.cpp b/cli_tool_framework/test/unittest/sub_command_info_test/sub_command_info_test.cpp similarity index 75% rename from test/unittest/cli_tool_mgr/sub_command_info_test/sub_command_info_test.cpp rename to cli_tool_framework/test/unittest/sub_command_info_test/sub_command_info_test.cpp index 465b581ef5..4310e2c122 100644 --- a/test/unittest/cli_tool_mgr/sub_command_info_test/sub_command_info_test.cpp +++ b/cli_tool_framework/test/unittest/sub_command_info_test/sub_command_info_test.cpp @@ -13,8 +13,8 @@ * limitations under the License. */ -#include #include +#include #include #include "sub_command_info.h" @@ -39,7 +39,7 @@ void SubCommandInfoTest::TearDown() {} /** * @tc.name: SubCommandInfo_Marshalling_0100 - * @tc.desc: Test SubCommandInfo Marshalling with argMapping + * @tc.desc: Test SubCommandInfo Marshalling with full data * @tc.type: FUNC */ HWTEST_F(SubCommandInfoTest, SubCommandInfo_Marshalling_0100, TestSize.Level1) @@ -51,8 +51,6 @@ HWTEST_F(SubCommandInfoTest, SubCommandInfo_Marshalling_0100, TestSize.Level1) subCmd.requirePermissions = {"ohos.permission.INTERNET"}; subCmd.inputSchema = "{}"; subCmd.outputSchema = "{}"; - subCmd.argMapping = std::make_shared(); - subCmd.argMapping->type = ArgMappingType::FLAG; subCmd.eventTypes = {"stdout", "stderr"}; subCmd.eventSchemas = "{}"; @@ -66,7 +64,7 @@ HWTEST_F(SubCommandInfoTest, SubCommandInfo_Marshalling_0100, TestSize.Level1) /** * @tc.name: SubCommandInfo_Marshalling_0200 - * @tc.desc: Test SubCommandInfo Marshalling without argMapping + * @tc.desc: Test SubCommandInfo Marshalling with empty data * @tc.type: FUNC */ HWTEST_F(SubCommandInfoTest, SubCommandInfo_Marshalling_0200, TestSize.Level1) @@ -74,13 +72,12 @@ HWTEST_F(SubCommandInfoTest, SubCommandInfo_Marshalling_0200, TestSize.Level1) GTEST_LOG_(INFO) << "SubCommandInfo_Marshalling_0200 start"; SubCommandInfo subCmd; - subCmd.description = "Test subcommand without argMapping"; - subCmd.requirePermissions = {}; - subCmd.inputSchema = "{}"; - subCmd.outputSchema = "{}"; - subCmd.argMapping = nullptr; + subCmd.description = "Test subcommand with empty data"; + subCmd.requirePermissions = {"ohos.permission.INTERNET"}; + subCmd.inputSchema = ""; + subCmd.outputSchema = ""; subCmd.eventTypes = {}; - subCmd.eventSchemas = "{}"; + subCmd.eventSchemas = ""; Parcel parcel; bool ret = subCmd.Marshalling(parcel); @@ -104,9 +101,6 @@ HWTEST_F(SubCommandInfoTest, SubCommandInfo_Marshalling_0300, TestSize.Level1) subCmd.requirePermissions = {"ohos.permission.INTERNET", "ohos.permission.CAMERA", "ohos.permission.READ_STORAGE"}; subCmd.inputSchema = R"({"type": "object", "properties": {"input": {"type": "string"}}})"; subCmd.outputSchema = R"({"type": "string"})"; - subCmd.argMapping = std::make_shared(); - subCmd.argMapping->type = ArgMappingType::POSITIONAL; - subCmd.argMapping->order = "arg1,arg2"; subCmd.eventTypes = {"stdout", "stderr", "exit"}; subCmd.eventSchemas = R"({"stdout": {"type": "string"}, "stderr": {"type": "string"}})"; @@ -118,56 +112,9 @@ HWTEST_F(SubCommandInfoTest, SubCommandInfo_Marshalling_0300, TestSize.Level1) GTEST_LOG_(INFO) << "SubCommandInfo_Marshalling_0300 end"; } -/** - * @tc.name: SubCommandInfo_Marshalling_0400 - * @tc.desc: Test SubCommandInfo Marshalling with all ArgMapping types - * @tc.type: FUNC - */ -HWTEST_F(SubCommandInfoTest, SubCommandInfo_Marshalling_0400, TestSize.Level1) -{ - GTEST_LOG_(INFO) << "SubCommandInfo_Marshalling_0400 start"; - - // Test FLAG type - SubCommandInfo subCmdFlag; - subCmdFlag.argMapping = std::make_shared(); - subCmdFlag.argMapping->type = ArgMappingType::FLAG; - Parcel parcelFlag; - EXPECT_TRUE(subCmdFlag.Marshalling(parcelFlag)); - - // Test POSITIONAL type - SubCommandInfo subCmdPos; - subCmdPos.argMapping = std::make_shared(); - subCmdPos.argMapping->type = ArgMappingType::POSITIONAL; - Parcel parcelPos; - EXPECT_TRUE(subCmdPos.Marshalling(parcelPos)); - - // Test FLATTENED type - SubCommandInfo subCmdFlat; - subCmdFlat.argMapping = std::make_shared(); - subCmdFlat.argMapping->type = ArgMappingType::FLATTENED; - Parcel parcelFlat; - EXPECT_TRUE(subCmdFlat.Marshalling(parcelFlat)); - - // Test JSONSTRING type - SubCommandInfo subCmdJson; - subCmdJson.argMapping = std::make_shared(); - subCmdJson.argMapping->type = ArgMappingType::JSONSTRING; - Parcel parcelJson; - EXPECT_TRUE(subCmdJson.Marshalling(parcelJson)); - - // Test MIXED type - SubCommandInfo subCmdMixed; - subCmdMixed.argMapping = std::make_shared(); - subCmdMixed.argMapping->type = ArgMappingType::MIXED; - Parcel parcelMixed; - EXPECT_TRUE(subCmdMixed.Marshalling(parcelMixed)); - - GTEST_LOG_(INFO) << "SubCommandInfo_Marshalling_0400 end"; -} - /** * @tc.name: SubCommandInfo_Unmarshalling_0100 - * @tc.desc: Test SubCommandInfo Unmarshalling with argMapping + * @tc.desc: Test SubCommandInfo Unmarshalling with full data * @tc.type: FUNC */ HWTEST_F(SubCommandInfoTest, SubCommandInfo_Unmarshalling_0100, TestSize.Level1) @@ -179,11 +126,6 @@ HWTEST_F(SubCommandInfoTest, SubCommandInfo_Unmarshalling_0100, TestSize.Level1) original.requirePermissions = {"ohos.permission.READ_STORAGE"}; original.inputSchema = R"({"type": "object"})"; original.outputSchema = R"({"type": "string"})"; - original.argMapping = std::make_shared(); - original.argMapping->type = ArgMappingType::JSONSTRING; - original.argMapping->separator = ""; - original.argMapping->order = ""; - original.argMapping->templates = "{}"; original.eventTypes = {"exit"}; original.eventSchemas = R"({"exit": {"type": "object"}})"; @@ -196,8 +138,6 @@ HWTEST_F(SubCommandInfoTest, SubCommandInfo_Unmarshalling_0100, TestSize.Level1) ASSERT_NE(result, nullptr); EXPECT_EQ(result->description, "Original subcommand"); EXPECT_EQ(result->requirePermissions.size(), 1u); - EXPECT_TRUE(result->argMapping != nullptr); - EXPECT_EQ(result->argMapping->type, ArgMappingType::JSONSTRING); delete result; @@ -206,7 +146,7 @@ HWTEST_F(SubCommandInfoTest, SubCommandInfo_Unmarshalling_0100, TestSize.Level1) /** * @tc.name: SubCommandInfo_Unmarshalling_0200 - * @tc.desc: Test SubCommandInfo Unmarshalling without argMapping + * @tc.desc: Test SubCommandInfo Unmarshalling with empty data * @tc.type: FUNC */ HWTEST_F(SubCommandInfoTest, SubCommandInfo_Unmarshalling_0200, TestSize.Level1) @@ -214,13 +154,12 @@ HWTEST_F(SubCommandInfoTest, SubCommandInfo_Unmarshalling_0200, TestSize.Level1) GTEST_LOG_(INFO) << "SubCommandInfo_Unmarshalling_0200 start"; SubCommandInfo original; - original.description = "No argMapping"; - original.requirePermissions = {}; - original.inputSchema = "{}"; - original.outputSchema = "{}"; - original.argMapping = nullptr; + original.description = "Empty data"; + original.requirePermissions = {"ohos.permission.INTERNET"}; + original.inputSchema = ""; + original.outputSchema = ""; original.eventTypes = {}; - original.eventSchemas = "{}"; + original.eventSchemas = ""; Parcel parcel; ASSERT_TRUE(original.Marshalling(parcel)); @@ -229,8 +168,7 @@ HWTEST_F(SubCommandInfoTest, SubCommandInfo_Unmarshalling_0200, TestSize.Level1) SubCommandInfo *result = SubCommandInfo::Unmarshalling(parcel); ASSERT_NE(result, nullptr); - EXPECT_EQ(result->description, "No argMapping"); - EXPECT_TRUE(result->argMapping == nullptr); + EXPECT_EQ(result->description, "Empty data"); delete result; @@ -254,6 +192,83 @@ HWTEST_F(SubCommandInfoTest, SubCommandInfo_Unmarshalling_0300, TestSize.Level1) GTEST_LOG_(INFO) << "SubCommandInfo_Unmarshalling_0300 end"; } +/** + * @tc.name: SubCommandInfo_Unmarshalling_0350 + * @tc.desc: Test SubCommandInfo Unmarshalling fails when requirePermissions is missing + * @tc.type: FUNC + */ +HWTEST_F(SubCommandInfoTest, SubCommandInfo_Unmarshalling_0350, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "SubCommandInfo_Unmarshalling_0350 start"; + + Parcel parcel; + ASSERT_TRUE(parcel.WriteString("partial subcommand")); + parcel.RewindRead(0); + + SubCommandInfo *result = SubCommandInfo::Unmarshalling(parcel); + + EXPECT_EQ(result, nullptr); + + GTEST_LOG_(INFO) << "SubCommandInfo_Unmarshalling_0350 end"; +} + +/** + * @tc.name: SubCommandInfo_Unmarshalling_0360 + * @tc.desc: Test SubCommandInfo Unmarshalling fails when eventSchemas is missing + * @tc.type: FUNC + */ +HWTEST_F(SubCommandInfoTest, SubCommandInfo_Unmarshalling_0360, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "SubCommandInfo_Unmarshalling_0360 start"; + + Parcel parcel; + ASSERT_TRUE(parcel.WriteString("partial subcommand")); + ASSERT_TRUE(parcel.WriteStringVector({"ohos.permission.INTERNET"})); + ASSERT_TRUE(parcel.WriteString("{}")); + ASSERT_TRUE(parcel.WriteString("{}")); + ASSERT_TRUE(parcel.WriteStringVector({"stdout"})); + parcel.RewindRead(0); + + SubCommandInfo *result = SubCommandInfo::Unmarshalling(parcel); + + EXPECT_EQ(result, nullptr); + + GTEST_LOG_(INFO) << "SubCommandInfo_Unmarshalling_0360 end"; +} + +/** + * @tc.name: SubCommandInfo_Unmarshalling_0370 + * @tc.desc: Test SubCommandInfo Unmarshalling fails on intermediate missing fields + * @tc.type: FUNC + */ +HWTEST_F(SubCommandInfoTest, SubCommandInfo_Unmarshalling_0370, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "SubCommandInfo_Unmarshalling_0370 start"; + + Parcel missingInputSchemaParcel; + ASSERT_TRUE(missingInputSchemaParcel.WriteString("partial subcommand")); + ASSERT_TRUE(missingInputSchemaParcel.WriteStringVector({"ohos.permission.INTERNET"})); + missingInputSchemaParcel.RewindRead(0); + EXPECT_EQ(SubCommandInfo::Unmarshalling(missingInputSchemaParcel), nullptr); + + Parcel missingOutputSchemaParcel; + ASSERT_TRUE(missingOutputSchemaParcel.WriteString("partial subcommand")); + ASSERT_TRUE(missingOutputSchemaParcel.WriteStringVector({"ohos.permission.INTERNET"})); + ASSERT_TRUE(missingOutputSchemaParcel.WriteString("{}")); + missingOutputSchemaParcel.RewindRead(0); + EXPECT_EQ(SubCommandInfo::Unmarshalling(missingOutputSchemaParcel), nullptr); + + Parcel missingEventTypesParcel; + ASSERT_TRUE(missingEventTypesParcel.WriteString("partial subcommand")); + ASSERT_TRUE(missingEventTypesParcel.WriteStringVector({"ohos.permission.INTERNET"})); + ASSERT_TRUE(missingEventTypesParcel.WriteString("{}")); + ASSERT_TRUE(missingEventTypesParcel.WriteString("{}")); + missingEventTypesParcel.RewindRead(0); + EXPECT_EQ(SubCommandInfo::Unmarshalling(missingEventTypesParcel), nullptr); + + GTEST_LOG_(INFO) << "SubCommandInfo_Unmarshalling_0370 end"; +} + /** * @tc.name: SubCommandInfo_Unmarshalling_0400 * @tc.desc: Test SubCommandInfo Unmarshalling with full data @@ -269,11 +284,6 @@ HWTEST_F(SubCommandInfoTest, SubCommandInfo_Unmarshalling_0400, TestSize.Level1) original.inputSchema = R"({"type": "object", "properties": {"arg1": {"type": "string"}, "arg2": {"type": "number"}}})"; original.outputSchema = R"({"type": "object", "properties": {"result": {"type": "string"}}})"; - original.argMapping = std::make_shared(); - original.argMapping->type = ArgMappingType::MIXED; - original.argMapping->separator = ","; - original.argMapping->order = "arg1,arg2,arg3"; - original.argMapping->templates = R"({"arg1": "--input=${value}", "arg2": "-o ${value}"})"; original.eventTypes = {"stdout", "stderr", "exit", "error"}; original.eventSchemas = R"({"stdout": {"type": "string"}, "stderr": {"type": "string"}, "exit": {"type": "number"}})"; @@ -289,10 +299,6 @@ HWTEST_F(SubCommandInfoTest, SubCommandInfo_Unmarshalling_0400, TestSize.Level1) EXPECT_EQ(result->requirePermissions.size(), 2u); EXPECT_EQ(result->requirePermissions[0], "ohos.permission.INTERNET"); EXPECT_EQ(result->requirePermissions[1], "ohos.permission.CAMERA"); - ASSERT_NE(result->argMapping, nullptr); - EXPECT_EQ(result->argMapping->type, ArgMappingType::MIXED); - EXPECT_EQ(result->argMapping->separator, ","); - EXPECT_EQ(result->argMapping->order, "arg1,arg2,arg3"); EXPECT_EQ(result->eventTypes.size(), 4u); delete result; @@ -314,11 +320,6 @@ HWTEST_F(SubCommandInfoTest, SubCommandInfo_Marshalling_Unmarshalling_RoundTrip_ original.requirePermissions = {"ohos.permission.WRITE_STORAGE"}; original.inputSchema = R"({"type": "object"})"; original.outputSchema = R"({"type": "array"})"; - original.argMapping = std::make_shared(); - original.argMapping->type = ArgMappingType::FLATTENED; - original.argMapping->separator = "|"; - original.argMapping->order = "a,b,c"; - original.argMapping->templates = "{}"; original.eventTypes = {"event1", "event2"}; original.eventSchemas = R"({"event1": {}, "event2": {}})"; @@ -333,10 +334,6 @@ HWTEST_F(SubCommandInfoTest, SubCommandInfo_Marshalling_Unmarshalling_RoundTrip_ EXPECT_EQ(result->requirePermissions, original.requirePermissions); EXPECT_EQ(result->inputSchema, original.inputSchema); EXPECT_EQ(result->outputSchema, original.outputSchema); - ASSERT_NE(result->argMapping, nullptr); - EXPECT_EQ(result->argMapping->type, original.argMapping->type); - EXPECT_EQ(result->argMapping->separator, original.argMapping->separator); - EXPECT_EQ(result->argMapping->order, original.argMapping->order); EXPECT_EQ(result->eventTypes, original.eventTypes); EXPECT_EQ(result->eventSchemas, original.eventSchemas); @@ -360,7 +357,6 @@ HWTEST_F(SubCommandInfoTest, SubCommandInfo_DefaultConstructor_0100, TestSize.Le EXPECT_TRUE(subCmd.requirePermissions.empty()); EXPECT_TRUE(subCmd.inputSchema.empty()); EXPECT_TRUE(subCmd.outputSchema.empty()); - EXPECT_EQ(subCmd.argMapping, nullptr); EXPECT_TRUE(subCmd.eventTypes.empty()); EXPECT_TRUE(subCmd.eventSchemas.empty()); @@ -383,7 +379,6 @@ HWTEST_F(SubCommandInfoTest, SubCommandInfo_ParseFromJson_0100, TestSize.Level1) "requirePermissions": ["ohos.permission.INTERNET", "ohos.permission.CAMERA"], "inputSchema": {"type": "object"}, "outputSchema": {"type": "string"}, - "argMapping": {"type": "positional", "order": "arg1,arg2"}, "eventTypes": ["stdout", "stderr"], "eventSchemas": {"stdout": {"type": "string"}} })"_json; @@ -398,9 +393,6 @@ HWTEST_F(SubCommandInfoTest, SubCommandInfo_ParseFromJson_0100, TestSize.Level1) EXPECT_EQ(subCmd.requirePermissions[1], "ohos.permission.CAMERA"); EXPECT_FALSE(subCmd.inputSchema.empty()); EXPECT_FALSE(subCmd.outputSchema.empty()); - ASSERT_NE(subCmd.argMapping, nullptr); - EXPECT_EQ(subCmd.argMapping->type, ArgMappingType::POSITIONAL); - EXPECT_EQ(subCmd.argMapping->order, "arg1,arg2"); EXPECT_EQ(subCmd.eventTypes.size(), 2u); EXPECT_FALSE(subCmd.eventSchemas.empty()); @@ -428,7 +420,7 @@ HWTEST_F(SubCommandInfoTest, SubCommandInfo_ParseFromJson_0200, TestSize.Level1) /** * @tc.name: SubCommandInfo_ParseFromJson_0300 - * @tc.desc: Test SubCommandInfo ParseFromJson without argMapping (argMapping is required) + * @tc.desc: Test SubCommandInfo ParseFromJson with minimal required data * @tc.type: FUNC */ HWTEST_F(SubCommandInfoTest, SubCommandInfo_ParseFromJson_0300, TestSize.Level1) @@ -436,85 +428,37 @@ HWTEST_F(SubCommandInfoTest, SubCommandInfo_ParseFromJson_0300, TestSize.Level1) GTEST_LOG_(INFO) << "SubCommandInfo_ParseFromJson_0300 start"; nlohmann::json json = R"({ - "description": "No argMapping", - "inputSchema": {"type": "object"}, - "outputSchema": {"type": "array"} + "description": "Minimal required data", + "requirePermissions": ["ohos.permission.INTERNET"], + "inputSchema": {}, + "outputSchema": {} })"_json; SubCommandInfo subCmd; bool result = SubCommandInfo::ParseFromJson(json, subCmd); - EXPECT_FALSE(result); // argMapping is required + EXPECT_TRUE(result); + EXPECT_EQ(subCmd.description, "Minimal required data"); GTEST_LOG_(INFO) << "SubCommandInfo_ParseFromJson_0300 end"; } -/** - * @tc.name: SubCommandInfo_ParseFromJson_0400 - * @tc.desc: Test SubCommandInfo ParseFromJson with invalid argMapping - * @tc.type: FUNC - */ -HWTEST_F(SubCommandInfoTest, SubCommandInfo_ParseFromJson_0400, TestSize.Level1) -{ - GTEST_LOG_(INFO) << "SubCommandInfo_ParseFromJson_0400 start"; - - nlohmann::json json = R"({ - "description": "Invalid argMapping", - "inputSchema": {"type": "object"}, - "outputSchema": {"type": "string"}, - "argMapping": {"type": "invalid_type"} - })"_json; - - SubCommandInfo subCmd; - bool result = SubCommandInfo::ParseFromJson(json, subCmd); - - EXPECT_FALSE(result); // argMapping parse failed - - GTEST_LOG_(INFO) << "SubCommandInfo_ParseFromJson_0400 end"; -} - -/** - * @tc.name: SubCommandInfo_ParseFromJson_0500 - * @tc.desc: Test SubCommandInfo ParseFromJson with argMapping missing type - * @tc.type: FUNC - */ -HWTEST_F(SubCommandInfoTest, SubCommandInfo_ParseFromJson_0500, TestSize.Level1) -{ - GTEST_LOG_(INFO) << "SubCommandInfo_ParseFromJson_0500 start"; - - nlohmann::json json = R"({ - "description": "argMapping without type", - "inputSchema": {"type": "object"}, - "outputSchema": {"type": "string"}, - "argMapping": {"separator": ","} - })"_json; - - SubCommandInfo subCmd; - bool result = SubCommandInfo::ParseFromJson(json, subCmd); - - EXPECT_FALSE(result); // argMapping type is required - - GTEST_LOG_(INFO) << "SubCommandInfo_ParseFromJson_0500 end"; -} - // ==================== ParseToJson Tests ==================== /** - * @tc.name: SubCommandInfo_ParseToJson_0100 + * @tc.name: SubCommandInfo_ParseToJson_1300 * @tc.desc: Test SubCommandInfo ParseToJson with full data * @tc.type: FUNC */ -HWTEST_F(SubCommandInfoTest, SubCommandInfo_ParseToJson_0100, TestSize.Level1) +HWTEST_F(SubCommandInfoTest, SubCommandInfo_ParseToJson_1300, TestSize.Level1) { - GTEST_LOG_(INFO) << "SubCommandInfo_ParseToJson_0100 start"; + GTEST_LOG_(INFO) << "SubCommandInfo_ParseToJson_1300 start"; SubCommandInfo subCmd; subCmd.description = "Test to JSON"; subCmd.requirePermissions = {"ohos.permission.READ_STORAGE"}; subCmd.inputSchema = R"({"type": "object"})"; subCmd.outputSchema = R"({"type": "string"})"; - subCmd.argMapping = std::make_shared(); - subCmd.argMapping->type = ArgMappingType::FLAG; subCmd.eventTypes = {"event1"}; subCmd.eventSchemas = R"({"event1": {"type": "object"}})"; @@ -522,18 +466,20 @@ HWTEST_F(SubCommandInfoTest, SubCommandInfo_ParseToJson_0100, TestSize.Level1) EXPECT_EQ(json["description"], "Test to JSON"); EXPECT_EQ(json["requirePermissions"].size(), 1u); - EXPECT_EQ(json["inputSchema"], R"({"type": "object"})"); - EXPECT_EQ(json["outputSchema"], R"({"type": "string"})"); - EXPECT_TRUE(json.contains("argMapping")); + EXPECT_TRUE(json["inputSchema"].is_object()); + EXPECT_EQ(json["inputSchema"]["type"], "object"); + EXPECT_TRUE(json["outputSchema"].is_object()); + EXPECT_EQ(json["outputSchema"]["type"], "string"); EXPECT_EQ(json["eventTypes"].size(), 1u); - EXPECT_EQ(json["eventSchemas"], R"({"event1": {"type": "object"}})"); + EXPECT_TRUE(json["eventSchemas"].is_object()); + EXPECT_EQ(json["eventSchemas"]["event1"]["type"], "object"); - GTEST_LOG_(INFO) << "SubCommandInfo_ParseToJson_0100 end"; + GTEST_LOG_(INFO) << "SubCommandInfo_ParseToJson_1300 end"; } /** * @tc.name: SubCommandInfo_ParseToJson_0200 - * @tc.desc: Test SubCommandInfo ParseToJson without argMapping + * @tc.desc: Test SubCommandInfo ParseToJson with empty data * @tc.type: FUNC */ HWTEST_F(SubCommandInfoTest, SubCommandInfo_ParseToJson_0200, TestSize.Level1) @@ -541,25 +487,23 @@ HWTEST_F(SubCommandInfoTest, SubCommandInfo_ParseToJson_0200, TestSize.Level1) GTEST_LOG_(INFO) << "SubCommandInfo_ParseToJson_0200 start"; SubCommandInfo subCmd; - subCmd.description = "No argMapping to JSON"; - subCmd.requirePermissions = {}; - subCmd.inputSchema = "{}"; - subCmd.outputSchema = "{}"; - subCmd.argMapping = nullptr; + subCmd.description = "Empty data to JSON"; + subCmd.requirePermissions = {"ohos.permission.INTERNET"}; + subCmd.inputSchema = ""; + subCmd.outputSchema = ""; subCmd.eventTypes = {}; - subCmd.eventSchemas = "{}"; + subCmd.eventSchemas = ""; nlohmann::json json = subCmd.ParseToJson(); - EXPECT_EQ(json["description"], "No argMapping to JSON"); - EXPECT_FALSE(json.contains("argMapping")); + EXPECT_EQ(json["description"], "Empty data to JSON"); GTEST_LOG_(INFO) << "SubCommandInfo_ParseToJson_0200 end"; } /** * @tc.name: SubCommandInfo_ParseToJson_0300 - * @tc.desc: Test SubCommandInfo ParseToJson with empty data + * @tc.desc: Test SubCommandInfo ParseToJson with default values * @tc.type: FUNC */ HWTEST_F(SubCommandInfoTest, SubCommandInfo_ParseToJson_0300, TestSize.Level1) @@ -576,16 +520,12 @@ HWTEST_F(SubCommandInfoTest, SubCommandInfo_ParseToJson_0300, TestSize.Level1) EXPECT_TRUE(json.contains("requirePermissions")); EXPECT_TRUE(json["requirePermissions"].is_array()); EXPECT_TRUE(json["requirePermissions"].empty()); - EXPECT_TRUE(json.contains("inputSchema")); - EXPECT_TRUE(json["inputSchema"].is_string()); - EXPECT_TRUE(json.contains("outputSchema")); - EXPECT_TRUE(json["outputSchema"].is_string()); + EXPECT_FALSE(json.contains("inputSchema")); + EXPECT_FALSE(json.contains("outputSchema")); EXPECT_TRUE(json.contains("eventTypes")); EXPECT_TRUE(json["eventTypes"].is_array()); EXPECT_TRUE(json["eventTypes"].empty()); - EXPECT_TRUE(json.contains("eventSchemas")); - EXPECT_TRUE(json["eventSchemas"].is_string()); - EXPECT_FALSE(json.contains("argMapping")); + EXPECT_FALSE(json.contains("eventSchemas")); GTEST_LOG_(INFO) << "SubCommandInfo_ParseToJson_0300 end"; } @@ -606,7 +546,6 @@ HWTEST_F(SubCommandInfoTest, SubCommandInfo_ParseFromJson_ParseToJson_RoundTrip_ "requirePermissions": ["ohos.permission.INTERNET"], "inputSchema": {"type": "object", "properties": {"input": {"type": "string"}}}, "outputSchema": {"type": "string"}, - "argMapping": {"type": "mixed", "separator": ",", "order": "a,b", "templates": "{}"}, "eventTypes": ["stdout", "stderr", "exit"], "eventSchemas": {"stdout": {"type": "string"}, "exit": {"type": "number"}} })"_json; @@ -644,7 +583,8 @@ HWTEST_F(SubCommandInfoTest, SubCommandInfo_ParseToJson_0400, TestSize.Level1) EXPECT_TRUE(json.contains("inputSchema")); EXPECT_EQ(json["inputSchema"], "invalid json string"); EXPECT_TRUE(json.contains("outputSchema")); - EXPECT_EQ(json["outputSchema"], R"({"type": "string"})"); + EXPECT_TRUE(json["outputSchema"].is_object()); + EXPECT_EQ(json["outputSchema"]["type"], "string"); EXPECT_TRUE(json.contains("eventSchemas")); GTEST_LOG_(INFO) << "SubCommandInfo_ParseToJson_0400 end"; @@ -669,7 +609,8 @@ HWTEST_F(SubCommandInfoTest, SubCommandInfo_ParseToJson_0500, TestSize.Level1) EXPECT_EQ(json["description"], "Invalid outputSchema test"); EXPECT_TRUE(json.contains("inputSchema")); - EXPECT_EQ(json["inputSchema"], R"({"type": "object"})"); + EXPECT_TRUE(json["inputSchema"].is_object()); + EXPECT_EQ(json["inputSchema"]["type"], "object"); EXPECT_TRUE(json.contains("outputSchema")); EXPECT_EQ(json["outputSchema"], "{invalid json}"); @@ -902,8 +843,7 @@ HWTEST_F(SubCommandInfoTest, SubCommandInfo_ParseFromJson_Validation_0100, TestS nlohmann::json json = R"({ "description": "", "inputSchema": {"type": "object"}, - "outputSchema": {"type": "string"}, - "argMapping": {"type": "flag"} + "outputSchema": {"type": "string"} })"_json; SubCommandInfo subCmd; @@ -925,8 +865,7 @@ HWTEST_F(SubCommandInfoTest, SubCommandInfo_ParseFromJson_Validation_0200, TestS nlohmann::json json = R"({ "inputSchema": {"type": "object"}, - "outputSchema": {"type": "string"}, - "argMapping": {"type": "flag"} + "outputSchema": {"type": "string"} })"_json; SubCommandInfo subCmd; @@ -950,8 +889,7 @@ HWTEST_F(SubCommandInfoTest, SubCommandInfo_ParseFromJson_Validation_0300, TestS "description": "Duplicate permissions", "requirePermissions": ["ohos.permission.INTERNET", "ohos.permission.INTERNET"], "inputSchema": {"type": "object"}, - "outputSchema": {"type": "string"}, - "argMapping": {"type": "flag"} + "outputSchema": {"type": "string"} })"_json; SubCommandInfo subCmd; @@ -975,8 +913,7 @@ HWTEST_F(SubCommandInfoTest, SubCommandInfo_ParseFromJson_Validation_0400, TestS "description": "Non-string permission", "requirePermissions": ["ohos.permission.INTERNET", 123], "inputSchema": {"type": "object"}, - "outputSchema": {"type": "string"}, - "argMapping": {"type": "flag"} + "outputSchema": {"type": "string"} })"_json; SubCommandInfo subCmd; @@ -1000,8 +937,7 @@ HWTEST_F(SubCommandInfoTest, SubCommandInfo_ParseFromJson_Validation_0401, TestS "description": "Empty string permission", "requirePermissions": ["ohos.permission.INTERNET", "", "ohos.permission.CAMERA"], "inputSchema": {"type": "object"}, - "outputSchema": {"type": "string"}, - "argMapping": {"type": "flag"} + "outputSchema": {"type": "string"} })"_json; SubCommandInfo subCmd; @@ -1028,8 +964,7 @@ HWTEST_F(SubCommandInfoTest, SubCommandInfo_ParseFromJson_Validation_0500, TestS "description": "Permissions not array", "requirePermissions": "ohos.permission.INTERNET", "inputSchema": {"type": "object"}, - "outputSchema": {"type": "string"}, - "argMapping": {"type": "flag"} + "outputSchema": {"type": "string"} })"_json; SubCommandInfo subCmd; @@ -1051,8 +986,7 @@ HWTEST_F(SubCommandInfoTest, SubCommandInfo_ParseFromJson_Validation_0600, TestS nlohmann::json json = R"({ "description": "No inputSchema", - "outputSchema": {"type": "string"}, - "argMapping": {"type": "flag"} + "outputSchema": {"type": "string"} })"_json; SubCommandInfo subCmd; @@ -1075,8 +1009,7 @@ HWTEST_F(SubCommandInfoTest, SubCommandInfo_ParseFromJson_Validation_0700, TestS nlohmann::json json = R"({ "description": "inputSchema not object", "inputSchema": "not an object", - "outputSchema": {"type": "string"}, - "argMapping": {"type": "flag"} + "outputSchema": {"type": "string"} })"_json; SubCommandInfo subCmd; @@ -1098,8 +1031,7 @@ HWTEST_F(SubCommandInfoTest, SubCommandInfo_ParseFromJson_Validation_0800, TestS nlohmann::json json = R"({ "description": "No outputSchema", - "inputSchema": {"type": "object"}, - "argMapping": {"type": "flag"} + "inputSchema": {"type": "object"} })"_json; SubCommandInfo subCmd; @@ -1122,8 +1054,7 @@ HWTEST_F(SubCommandInfoTest, SubCommandInfo_ParseFromJson_Validation_0900, TestS nlohmann::json json = R"({ "description": "outputSchema not object", "inputSchema": {"type": "object"}, - "outputSchema": 123, - "argMapping": {"type": "flag"} + "outputSchema": 123 })"_json; SubCommandInfo subCmd; @@ -1145,9 +1076,9 @@ HWTEST_F(SubCommandInfoTest, SubCommandInfo_ParseFromJson_Validation_1000, TestS nlohmann::json json = R"({ "description": "Duplicate eventTypes", + "requirePermissions": [], "inputSchema": {"type": "object"}, "outputSchema": {"type": "string"}, - "argMapping": {"type": "flag"}, "eventTypes": ["stdout", "stdout"] })"_json; @@ -1172,7 +1103,6 @@ HWTEST_F(SubCommandInfoTest, SubCommandInfo_ParseFromJson_Validation_1100, TestS "description": "Non-string eventType", "inputSchema": {"type": "object"}, "outputSchema": {"type": "string"}, - "argMapping": {"type": "flag"}, "eventTypes": ["stdout", 123] })"_json; @@ -1195,16 +1125,16 @@ HWTEST_F(SubCommandInfoTest, SubCommandInfo_ParseFromJson_Validation_1101, TestS nlohmann::json json = R"({ "description": "Empty string eventType", + "requirePermissions": [], "inputSchema": {"type": "object"}, "outputSchema": {"type": "string"}, - "argMapping": {"type": "flag"}, "eventTypes": ["stdout", "", "stderr"] })"_json; SubCommandInfo subCmd; bool result = SubCommandInfo::ParseFromJson(json, subCmd); - EXPECT_TRUE(result); // empty strings are skipped + ASSERT_TRUE(result); // empty strings are skipped EXPECT_EQ(subCmd.eventTypes.size(), 2u); // only non-empty eventTypes stored EXPECT_EQ(subCmd.eventTypes[0], "stdout"); EXPECT_EQ(subCmd.eventTypes[1], "stderr"); @@ -1225,7 +1155,6 @@ HWTEST_F(SubCommandInfoTest, SubCommandInfo_ParseFromJson_Validation_1200, TestS "description": "eventTypes not array", "inputSchema": {"type": "object"}, "outputSchema": {"type": "string"}, - "argMapping": {"type": "flag"}, "eventTypes": "stdout" })"_json; @@ -1250,7 +1179,6 @@ HWTEST_F(SubCommandInfoTest, SubCommandInfo_ParseFromJson_Validation_1300, TestS "description": "eventSchemas not object", "inputSchema": {"type": "object"}, "outputSchema": {"type": "string"}, - "argMapping": {"type": "flag"}, "eventSchemas": "not an object" })"_json; @@ -1264,18 +1192,66 @@ HWTEST_F(SubCommandInfoTest, SubCommandInfo_ParseFromJson_Validation_1300, TestS /** * @tc.name: SubCommandInfo_ParseFromJson_Validation_1400 - * @tc.desc: Test SubCommandInfo ParseFromJson with valid minimal data + * @tc.desc: Test SubCommandInfo ParseFromJson without requirePermissions (requirePermissions is required) * @tc.type: FUNC */ HWTEST_F(SubCommandInfoTest, SubCommandInfo_ParseFromJson_Validation_1400, TestSize.Level1) { GTEST_LOG_(INFO) << "SubCommandInfo_ParseFromJson_Validation_1400 start"; + nlohmann::json json = R"({ + "description": "No requirePermissions", + "inputSchema": {"type": "object"}, + "outputSchema": {"type": "string"} + })"_json; + + SubCommandInfo subCmd; + bool result = SubCommandInfo::ParseFromJson(json, subCmd); + + EXPECT_FALSE(result); // requirePermissions is required + + GTEST_LOG_(INFO) << "SubCommandInfo_ParseFromJson_Validation_1400 end"; +} + +/** + * @tc.name: SubCommandInfo_ParseFromJson_Validation_1401 + * @tc.desc: Test SubCommandInfo ParseFromJson with empty requirePermissions array (valid) + * @tc.type: FUNC + */ +HWTEST_F(SubCommandInfoTest, SubCommandInfo_ParseFromJson_Validation_1401, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "SubCommandInfo_ParseFromJson_Validation_1401 start"; + + nlohmann::json json = R"({ + "description": "Empty requirePermissions", + "requirePermissions": [], + "inputSchema": {"type": "object"}, + "outputSchema": {"type": "string"} + })"_json; + + SubCommandInfo subCmd; + bool result = SubCommandInfo::ParseFromJson(json, subCmd); + + EXPECT_TRUE(result); // empty requirePermissions array is valid + EXPECT_TRUE(subCmd.requirePermissions.empty()); + + GTEST_LOG_(INFO) << "SubCommandInfo_ParseFromJson_Validation_1401 end"; +} + +/** + * @tc.name: SubCommandInfo_ParseFromJson_Validation_1402 + * @tc.desc: Test SubCommandInfo ParseFromJson with valid minimal data + * @tc.type: FUNC + */ +HWTEST_F(SubCommandInfoTest, SubCommandInfo_ParseFromJson_Validation_1402, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "SubCommandInfo_ParseFromJson_Validation_1402 start"; + nlohmann::json json = R"({ "description": "Minimal valid subcommand", - "inputSchema": {}, - "outputSchema": {}, - "argMapping": {"type": "flag"} + "requirePermissions": ["ohos.permission.INTERNET"], + "inputSchema": {"type": "object"}, + "outputSchema": {"type": "string"} })"_json; SubCommandInfo subCmd; @@ -1283,11 +1259,11 @@ HWTEST_F(SubCommandInfoTest, SubCommandInfo_ParseFromJson_Validation_1400, TestS EXPECT_TRUE(result); EXPECT_EQ(subCmd.description, "Minimal valid subcommand"); - EXPECT_TRUE(subCmd.requirePermissions.empty()); + EXPECT_EQ(subCmd.requirePermissions.size(), 1u); EXPECT_TRUE(subCmd.eventTypes.empty()); EXPECT_TRUE(subCmd.eventSchemas.empty()); - GTEST_LOG_(INFO) << "SubCommandInfo_ParseFromJson_Validation_1400 end"; + GTEST_LOG_(INFO) << "SubCommandInfo_ParseFromJson_Validation_1402 end"; } /** @@ -1303,8 +1279,7 @@ HWTEST_F(SubCommandInfoTest, SubCommandInfo_ParseFromJson_Validation_1500, TestS "description": "Unique permissions", "requirePermissions": ["ohos.permission.INTERNET", "ohos.permission.CAMERA", "ohos.permission.READ_STORAGE"], "inputSchema": {"type": "object"}, - "outputSchema": {"type": "string"}, - "argMapping": {"type": "flag"} + "outputSchema": {"type": "string"} })"_json; SubCommandInfo subCmd; @@ -1327,9 +1302,9 @@ HWTEST_F(SubCommandInfoTest, SubCommandInfo_ParseFromJson_Validation_1600, TestS nlohmann::json json = R"({ "description": "Unique eventTypes", + "requirePermissions": [], "inputSchema": {"type": "object"}, "outputSchema": {"type": "string"}, - "argMapping": {"type": "flag"}, "eventTypes": ["stdout", "stderr", "exit"] })"_json; @@ -1353,9 +1328,9 @@ HWTEST_F(SubCommandInfoTest, SubCommandInfo_ParseFromJson_Validation_1700, TestS nlohmann::json json = R"({ "description": "Valid eventSchemas", + "requirePermissions": [], "inputSchema": {"type": "object"}, "outputSchema": {"type": "string"}, - "argMapping": {"type": "flag"}, "eventSchemas": {"stdout": {"type": "string"}, "exit": {"type": "number"}} })"_json; @@ -1381,10 +1356,9 @@ HWTEST_F(SubCommandInfoTest, SubCommandInfo_Validate_0100, TestSize.Level1) SubCommandInfo subCmd; subCmd.description = "Valid subcommand"; + subCmd.requirePermissions = {"ohos.permission.INTERNET"}; subCmd.inputSchema = R"({"type": "object"})"; subCmd.outputSchema = R"({"type": "string"})"; - subCmd.argMapping = std::make_shared(); - subCmd.argMapping->type = ArgMappingType::FLAG; EXPECT_TRUE(SubCommandInfo::Validate(subCmd)); @@ -1402,15 +1376,35 @@ HWTEST_F(SubCommandInfoTest, SubCommandInfo_Validate_0200, TestSize.Level1) SubCommandInfo subCmd; subCmd.description = ""; + subCmd.requirePermissions = {"ohos.permission.INTERNET"}; subCmd.inputSchema = R"({"type": "object"})"; subCmd.outputSchema = R"({"type": "string"})"; - subCmd.argMapping = std::make_shared(); EXPECT_FALSE(SubCommandInfo::Validate(subCmd)); GTEST_LOG_(INFO) << "SubCommandInfo_Validate_0200 end"; } +/** + * @tc.name: SubCommandInfo_Validate_0250 + * @tc.desc: Test SubCommandInfo::Validate with empty requirePermissions (valid) + * @tc.type: FUNC + */ +HWTEST_F(SubCommandInfoTest, SubCommandInfo_Validate_0250, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "SubCommandInfo_Validate_0250 start"; + + SubCommandInfo subCmd; + subCmd.description = "Empty requirePermissions"; + subCmd.requirePermissions = {}; + subCmd.inputSchema = R"({"type": "object"})"; + subCmd.outputSchema = R"({"type": "string"})"; + + EXPECT_TRUE(SubCommandInfo::Validate(subCmd)); // empty requirePermissions is valid + + GTEST_LOG_(INFO) << "SubCommandInfo_Validate_0250 end"; +} + /** * @tc.name: SubCommandInfo_Validate_0300 * @tc.desc: Test SubCommandInfo::Validate with duplicate requirePermissions (duplicates are allowed) @@ -1425,7 +1419,6 @@ HWTEST_F(SubCommandInfoTest, SubCommandInfo_Validate_0300, TestSize.Level1) subCmd.requirePermissions = {"ohos.permission.INTERNET", "ohos.permission.INTERNET"}; subCmd.inputSchema = R"({"type": "object"})"; subCmd.outputSchema = R"({"type": "string"})"; - subCmd.argMapping = std::make_shared(); EXPECT_TRUE(SubCommandInfo::Validate(subCmd)); // duplicate permissions are now allowed @@ -1446,7 +1439,6 @@ HWTEST_F(SubCommandInfoTest, SubCommandInfo_Validate_0400, TestSize.Level1) subCmd.requirePermissions = {"ohos.permission.INTERNET", "ohos.permission.CAMERA"}; subCmd.inputSchema = R"({"type": "object"})"; subCmd.outputSchema = R"({"type": "string"})"; - subCmd.argMapping = std::make_shared(); EXPECT_TRUE(SubCommandInfo::Validate(subCmd)); @@ -1455,7 +1447,7 @@ HWTEST_F(SubCommandInfoTest, SubCommandInfo_Validate_0400, TestSize.Level1) /** * @tc.name: SubCommandInfo_Validate_0500 - * @tc.desc: Test SubCommandInfo::Validate with empty inputSchema + * @tc.desc: Test SubCommandInfo::Validate with empty inputSchema (inputSchema is required) * @tc.type: FUNC */ HWTEST_F(SubCommandInfoTest, SubCommandInfo_Validate_0500, TestSize.Level1) @@ -1464,11 +1456,11 @@ HWTEST_F(SubCommandInfoTest, SubCommandInfo_Validate_0500, TestSize.Level1) SubCommandInfo subCmd; subCmd.description = "Empty inputSchema"; + subCmd.requirePermissions = {"ohos.permission.INTERNET"}; subCmd.inputSchema = ""; subCmd.outputSchema = R"({"type": "string"})"; - subCmd.argMapping = std::make_shared(); - EXPECT_FALSE(SubCommandInfo::Validate(subCmd)); + EXPECT_FALSE(SubCommandInfo::Validate(subCmd)); // inputSchema is required GTEST_LOG_(INFO) << "SubCommandInfo_Validate_0500 end"; } @@ -1484,9 +1476,9 @@ HWTEST_F(SubCommandInfoTest, SubCommandInfo_Validate_0600, TestSize.Level1) SubCommandInfo subCmd; subCmd.description = "Invalid inputSchema"; + subCmd.requirePermissions = {"ohos.permission.INTERNET"}; subCmd.inputSchema = "not a valid json"; subCmd.outputSchema = R"({"type": "string"})"; - subCmd.argMapping = std::make_shared(); EXPECT_FALSE(SubCommandInfo::Validate(subCmd)); @@ -1504,9 +1496,9 @@ HWTEST_F(SubCommandInfoTest, SubCommandInfo_Validate_0700, TestSize.Level1) SubCommandInfo subCmd; subCmd.description = "inputSchema not object"; + subCmd.requirePermissions = {"ohos.permission.INTERNET"}; subCmd.inputSchema = R"("just a string")"; subCmd.outputSchema = R"({"type": "string"})"; - subCmd.argMapping = std::make_shared(); EXPECT_FALSE(SubCommandInfo::Validate(subCmd)); @@ -1515,7 +1507,7 @@ HWTEST_F(SubCommandInfoTest, SubCommandInfo_Validate_0700, TestSize.Level1) /** * @tc.name: SubCommandInfo_Validate_0800 - * @tc.desc: Test SubCommandInfo::Validate with empty outputSchema + * @tc.desc: Test SubCommandInfo::Validate with empty outputSchema (outputSchema is required) * @tc.type: FUNC */ HWTEST_F(SubCommandInfoTest, SubCommandInfo_Validate_0800, TestSize.Level1) @@ -1524,11 +1516,11 @@ HWTEST_F(SubCommandInfoTest, SubCommandInfo_Validate_0800, TestSize.Level1) SubCommandInfo subCmd; subCmd.description = "Empty outputSchema"; + subCmd.requirePermissions = {"ohos.permission.INTERNET"}; subCmd.inputSchema = R"({"type": "object"})"; subCmd.outputSchema = ""; - subCmd.argMapping = std::make_shared(); - EXPECT_FALSE(SubCommandInfo::Validate(subCmd)); + EXPECT_FALSE(SubCommandInfo::Validate(subCmd)); // outputSchema is required GTEST_LOG_(INFO) << "SubCommandInfo_Validate_0800 end"; } @@ -1544,9 +1536,9 @@ HWTEST_F(SubCommandInfoTest, SubCommandInfo_Validate_0900, TestSize.Level1) SubCommandInfo subCmd; subCmd.description = "Invalid outputSchema"; + subCmd.requirePermissions = {"ohos.permission.INTERNET"}; subCmd.inputSchema = R"({"type": "object"})"; subCmd.outputSchema = "{invalid json}"; - subCmd.argMapping = std::make_shared(); EXPECT_FALSE(SubCommandInfo::Validate(subCmd)); @@ -1564,9 +1556,9 @@ HWTEST_F(SubCommandInfoTest, SubCommandInfo_Validate_1000, TestSize.Level1) SubCommandInfo subCmd; subCmd.description = "outputSchema not object"; + subCmd.requirePermissions = {"ohos.permission.INTERNET"}; subCmd.inputSchema = R"({"type": "object"})"; subCmd.outputSchema = "123"; - subCmd.argMapping = std::make_shared(); EXPECT_FALSE(SubCommandInfo::Validate(subCmd)); @@ -1575,7 +1567,7 @@ HWTEST_F(SubCommandInfoTest, SubCommandInfo_Validate_1000, TestSize.Level1) /** * @tc.name: SubCommandInfo_Validate_1100 - * @tc.desc: Test SubCommandInfo::Validate with null argMapping + * @tc.desc: Test SubCommandInfo::Validate with duplicate eventTypes (duplicates are allowed) * @tc.type: FUNC */ HWTEST_F(SubCommandInfoTest, SubCommandInfo_Validate_1100, TestSize.Level1) @@ -1583,19 +1575,20 @@ HWTEST_F(SubCommandInfoTest, SubCommandInfo_Validate_1100, TestSize.Level1) GTEST_LOG_(INFO) << "SubCommandInfo_Validate_1100 start"; SubCommandInfo subCmd; - subCmd.description = "Null argMapping"; + subCmd.description = "Duplicate eventTypes"; + subCmd.requirePermissions = {"ohos.permission.INTERNET"}; subCmd.inputSchema = R"({"type": "object"})"; subCmd.outputSchema = R"({"type": "string"})"; - subCmd.argMapping = nullptr; + subCmd.eventTypes = {"stdout", "stdout"}; - EXPECT_FALSE(SubCommandInfo::Validate(subCmd)); + EXPECT_TRUE(SubCommandInfo::Validate(subCmd)); // duplicate eventTypes are now allowed GTEST_LOG_(INFO) << "SubCommandInfo_Validate_1100 end"; } /** * @tc.name: SubCommandInfo_Validate_1200 - * @tc.desc: Test SubCommandInfo::Validate with invalid argMapping + * @tc.desc: Test SubCommandInfo::Validate with unique eventTypes * @tc.type: FUNC */ HWTEST_F(SubCommandInfoTest, SubCommandInfo_Validate_1200, TestSize.Level1) @@ -1603,20 +1596,20 @@ HWTEST_F(SubCommandInfoTest, SubCommandInfo_Validate_1200, TestSize.Level1) GTEST_LOG_(INFO) << "SubCommandInfo_Validate_1200 start"; SubCommandInfo subCmd; - subCmd.description = "Invalid argMapping"; + subCmd.description = "Unique eventTypes"; + subCmd.requirePermissions = {"ohos.permission.INTERNET"}; subCmd.inputSchema = R"({"type": "object"})"; subCmd.outputSchema = R"({"type": "string"})"; - subCmd.argMapping = std::make_shared(); - subCmd.argMapping->type = static_cast(-1); // invalid type + subCmd.eventTypes = {"stdout", "stderr", "exit"}; - EXPECT_FALSE(SubCommandInfo::Validate(subCmd)); + EXPECT_TRUE(SubCommandInfo::Validate(subCmd)); GTEST_LOG_(INFO) << "SubCommandInfo_Validate_1200 end"; } /** * @tc.name: SubCommandInfo_Validate_1300 - * @tc.desc: Test SubCommandInfo::Validate with duplicate eventTypes (duplicates are allowed) + * @tc.desc: Test SubCommandInfo::Validate with invalid eventSchemas JSON * @tc.type: FUNC */ HWTEST_F(SubCommandInfoTest, SubCommandInfo_Validate_1300, TestSize.Level1) @@ -1624,20 +1617,20 @@ HWTEST_F(SubCommandInfoTest, SubCommandInfo_Validate_1300, TestSize.Level1) GTEST_LOG_(INFO) << "SubCommandInfo_Validate_1300 start"; SubCommandInfo subCmd; - subCmd.description = "Duplicate eventTypes"; + subCmd.description = "Invalid eventSchemas"; + subCmd.requirePermissions = {"ohos.permission.INTERNET"}; subCmd.inputSchema = R"({"type": "object"})"; subCmd.outputSchema = R"({"type": "string"})"; - subCmd.argMapping = std::make_shared(); - subCmd.eventTypes = {"stdout", "stdout"}; + subCmd.eventSchemas = "not a valid json"; - EXPECT_TRUE(SubCommandInfo::Validate(subCmd)); // duplicate eventTypes are now allowed + EXPECT_FALSE(SubCommandInfo::Validate(subCmd)); GTEST_LOG_(INFO) << "SubCommandInfo_Validate_1300 end"; } /** * @tc.name: SubCommandInfo_Validate_1400 - * @tc.desc: Test SubCommandInfo::Validate with unique eventTypes + * @tc.desc: Test SubCommandInfo::Validate with eventSchemas not object * @tc.type: FUNC */ HWTEST_F(SubCommandInfoTest, SubCommandInfo_Validate_1400, TestSize.Level1) @@ -1645,20 +1638,20 @@ HWTEST_F(SubCommandInfoTest, SubCommandInfo_Validate_1400, TestSize.Level1) GTEST_LOG_(INFO) << "SubCommandInfo_Validate_1400 start"; SubCommandInfo subCmd; - subCmd.description = "Unique eventTypes"; + subCmd.description = "eventSchemas not object"; + subCmd.requirePermissions = {"ohos.permission.INTERNET"}; subCmd.inputSchema = R"({"type": "object"})"; subCmd.outputSchema = R"({"type": "string"})"; - subCmd.argMapping = std::make_shared(); - subCmd.eventTypes = {"stdout", "stderr", "exit"}; + subCmd.eventSchemas = R"("just a string")"; - EXPECT_TRUE(SubCommandInfo::Validate(subCmd)); + EXPECT_FALSE(SubCommandInfo::Validate(subCmd)); GTEST_LOG_(INFO) << "SubCommandInfo_Validate_1400 end"; } /** * @tc.name: SubCommandInfo_Validate_1500 - * @tc.desc: Test SubCommandInfo::Validate with invalid eventSchemas JSON + * @tc.desc: Test SubCommandInfo::Validate with valid eventSchemas object * @tc.type: FUNC */ HWTEST_F(SubCommandInfoTest, SubCommandInfo_Validate_1500, TestSize.Level1) @@ -1666,20 +1659,20 @@ HWTEST_F(SubCommandInfoTest, SubCommandInfo_Validate_1500, TestSize.Level1) GTEST_LOG_(INFO) << "SubCommandInfo_Validate_1500 start"; SubCommandInfo subCmd; - subCmd.description = "Invalid eventSchemas"; + subCmd.description = "Valid eventSchemas"; + subCmd.requirePermissions = {"ohos.permission.INTERNET"}; subCmd.inputSchema = R"({"type": "object"})"; subCmd.outputSchema = R"({"type": "string"})"; - subCmd.argMapping = std::make_shared(); - subCmd.eventSchemas = "not a valid json"; + subCmd.eventSchemas = R"({"stdout": {"type": "string"}})"; - EXPECT_FALSE(SubCommandInfo::Validate(subCmd)); + EXPECT_TRUE(SubCommandInfo::Validate(subCmd)); GTEST_LOG_(INFO) << "SubCommandInfo_Validate_1500 end"; } /** * @tc.name: SubCommandInfo_Validate_1600 - * @tc.desc: Test SubCommandInfo::Validate with eventSchemas not object + * @tc.desc: Test SubCommandInfo::Validate with all valid fields * @tc.type: FUNC */ HWTEST_F(SubCommandInfoTest, SubCommandInfo_Validate_1600, TestSize.Level1) @@ -1687,20 +1680,21 @@ HWTEST_F(SubCommandInfoTest, SubCommandInfo_Validate_1600, TestSize.Level1) GTEST_LOG_(INFO) << "SubCommandInfo_Validate_1600 start"; SubCommandInfo subCmd; - subCmd.description = "eventSchemas not object"; - subCmd.inputSchema = R"({"type": "object"})"; - subCmd.outputSchema = R"({"type": "string"})"; - subCmd.argMapping = std::make_shared(); - subCmd.eventSchemas = R"("just a string")"; + subCmd.description = "All valid fields"; + subCmd.requirePermissions = {"ohos.permission.INTERNET", "ohos.permission.CAMERA"}; + subCmd.inputSchema = R"({"type": "object", "properties": {"input": {"type": "string"}}})"; + subCmd.outputSchema = R"({"type": "array", "items": {"type": "string"}})"; + subCmd.eventTypes = {"stdout", "stderr", "exit"}; + subCmd.eventSchemas = R"({"stdout": {"type": "string"}, "exit": {"type": "number"}})"; - EXPECT_FALSE(SubCommandInfo::Validate(subCmd)); + EXPECT_TRUE(SubCommandInfo::Validate(subCmd)); GTEST_LOG_(INFO) << "SubCommandInfo_Validate_1600 end"; } /** * @tc.name: SubCommandInfo_Validate_1700 - * @tc.desc: Test SubCommandInfo::Validate with valid eventSchemas object + * @tc.desc: Test SubCommandInfo::Validate with minimal valid data * @tc.type: FUNC */ HWTEST_F(SubCommandInfoTest, SubCommandInfo_Validate_1700, TestSize.Level1) @@ -1708,61 +1702,245 @@ HWTEST_F(SubCommandInfoTest, SubCommandInfo_Validate_1700, TestSize.Level1) GTEST_LOG_(INFO) << "SubCommandInfo_Validate_1700 start"; SubCommandInfo subCmd; - subCmd.description = "Valid eventSchemas"; - subCmd.inputSchema = R"({"type": "object"})"; - subCmd.outputSchema = R"({"type": "string"})"; - subCmd.argMapping = std::make_shared(); - subCmd.eventSchemas = R"({"stdout": {"type": "string"}})"; + subCmd.description = "Minimal"; + subCmd.requirePermissions = {"ohos.permission.INTERNET"}; + subCmd.inputSchema = "{}"; + subCmd.outputSchema = "{}"; EXPECT_TRUE(SubCommandInfo::Validate(subCmd)); GTEST_LOG_(INFO) << "SubCommandInfo_Validate_1700 end"; } +// ==================== ParseFromJson Missing/Wrong Field Tests ==================== + +/** + * @tc.name: SubCommandInfo_ParseFromJson_0180 + * @tc.desc: Test ParseFromJson fails for each missing/wrong required field + * @tc.type: FUNC + */ +HWTEST_F(SubCommandInfoTest, SubCommandInfo_ParseFromJson_0180, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "SubCommandInfo_ParseFromJson_0180 start"; + + auto validJson = R"({ + "description": "test sub", + "requirePermissions": ["ohos.permission.INTERNET"], + "inputSchema": {"type": "object"}, + "outputSchema": {"type": "object"} + })"_json; + + // missing description + auto missingDesc = validJson; + missingDesc.erase("description"); + SubCommandInfo subCmd; + EXPECT_FALSE(SubCommandInfo::ParseFromJson(missingDesc, subCmd)); + + // wrong type description + auto wrongDesc = validJson; + wrongDesc["description"] = 42; + EXPECT_FALSE(SubCommandInfo::ParseFromJson(wrongDesc, subCmd)); + + // empty description + auto emptyDesc = validJson; + emptyDesc["description"] = ""; + EXPECT_FALSE(SubCommandInfo::ParseFromJson(emptyDesc, subCmd)); + + // missing requirePermissions + auto missingPerms = validJson; + missingPerms.erase("requirePermissions"); + EXPECT_FALSE(SubCommandInfo::ParseFromJson(missingPerms, subCmd)); + + // non-array requirePermissions + auto wrongPerms = validJson; + wrongPerms["requirePermissions"] = "not-array"; + EXPECT_FALSE(SubCommandInfo::ParseFromJson(wrongPerms, subCmd)); + + // non-string permission item + auto badPerm = validJson; + badPerm["requirePermissions"] = {42}; + EXPECT_FALSE(SubCommandInfo::ParseFromJson(badPerm, subCmd)); + + // empty permission entries skipped + auto emptyPerm = validJson; + emptyPerm["requirePermissions"] = {"", "ohos.permission.INTERNET"}; + EXPECT_TRUE(SubCommandInfo::ParseFromJson(emptyPerm, subCmd)); + EXPECT_EQ(subCmd.requirePermissions.size(), 1u); + + // missing inputSchema + auto missingInput = validJson; + missingInput.erase("inputSchema"); + EXPECT_FALSE(SubCommandInfo::ParseFromJson(missingInput, subCmd)); + + // non-object inputSchema + auto wrongInput = validJson; + wrongInput["inputSchema"] = "string"; + EXPECT_FALSE(SubCommandInfo::ParseFromJson(wrongInput, subCmd)); + + // missing outputSchema + auto missingOutput = validJson; + missingOutput.erase("outputSchema"); + EXPECT_FALSE(SubCommandInfo::ParseFromJson(missingOutput, subCmd)); + + // non-object outputSchema + auto wrongOutput = validJson; + wrongOutput["outputSchema"] = 123; + EXPECT_FALSE(SubCommandInfo::ParseFromJson(wrongOutput, subCmd)); + + GTEST_LOG_(INFO) << "SubCommandInfo_ParseFromJson_0180 end"; +} + +/** + * @tc.name: SubCommandInfo_ParseFromJson_0190 + * @tc.desc: Test ParseFromJson optional eventTypes and eventSchemas branches + * @tc.type: FUNC + */ +HWTEST_F(SubCommandInfoTest, SubCommandInfo_ParseFromJson_0190, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "SubCommandInfo_ParseFromJson_0190 start"; + + auto validJson = R"({ + "description": "test sub", + "requirePermissions": [], + "inputSchema": {"type": "object"}, + "outputSchema": {"type": "object"} + })"_json; + + // non-array eventTypes + auto badEventTypes = validJson; + badEventTypes["eventTypes"] = "not-array"; + SubCommandInfo subCmd; + EXPECT_FALSE(SubCommandInfo::ParseFromJson(badEventTypes, subCmd)); + + // non-string event type + auto badEventItem = validJson; + badEventItem["eventTypes"] = {42}; + EXPECT_FALSE(SubCommandInfo::ParseFromJson(badEventItem, subCmd)); + + // empty event type entries skipped + auto emptyEvent = validJson; + emptyEvent["eventTypes"] = {"", "exit"}; + EXPECT_TRUE(SubCommandInfo::ParseFromJson(emptyEvent, subCmd)); + EXPECT_EQ(subCmd.eventTypes.size(), 1u); + + // non-object eventSchemas + auto badEventSchemas = validJson; + badEventSchemas["eventSchemas"] = "not-object"; + EXPECT_FALSE(SubCommandInfo::ParseFromJson(badEventSchemas, subCmd)); + + // valid optional fields + auto withOptional = validJson; + withOptional["eventTypes"] = {"stdout"}; + withOptional["eventSchemas"] = {{"stdout", {{"type", "string"}}}}; + subCmd = SubCommandInfo(); + EXPECT_TRUE(SubCommandInfo::ParseFromJson(withOptional, subCmd)); + EXPECT_EQ(subCmd.eventTypes.size(), 1u); + EXPECT_FALSE(subCmd.eventSchemas.empty()); + + GTEST_LOG_(INFO) << "SubCommandInfo_ParseFromJson_0190 end"; +} + +// ==================== ParseToJson Schema String Tests ==================== + +/** + * @tc.name: SubCommandInfo_ParseToJson_1400 + * @tc.desc: Test ParseToJson emits valid schema as JSON, invalid as string + * @tc.type: FUNC + */ +HWTEST_F(SubCommandInfoTest, SubCommandInfo_ParseToJson_1400, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "SubCommandInfo_ParseToJson_1400 start"; + + SubCommandInfo subCmd; + subCmd.description = "test"; + subCmd.inputSchema = R"({"type":"object"})"; + subCmd.outputSchema = "{invalid}"; + subCmd.eventSchemas = "{also-bad}"; + + nlohmann::json json = subCmd.ParseToJson(); + EXPECT_TRUE(json["inputSchema"].is_object()); + EXPECT_EQ(json["outputSchema"], "{invalid}"); + EXPECT_EQ(json["eventSchemas"], "{also-bad}"); + + GTEST_LOG_(INFO) << "SubCommandInfo_ParseToJson_1400 end"; +} + +// ==================== Validate Failure Branch Tests ==================== + /** * @tc.name: SubCommandInfo_Validate_1800 - * @tc.desc: Test SubCommandInfo::Validate with all valid fields + * @tc.desc: Test Validate rejects empty/invalid schemas and eventSchemas * @tc.type: FUNC */ HWTEST_F(SubCommandInfoTest, SubCommandInfo_Validate_1800, TestSize.Level1) { GTEST_LOG_(INFO) << "SubCommandInfo_Validate_1800 start"; - SubCommandInfo subCmd; - subCmd.description = "All valid fields"; - subCmd.requirePermissions = {"ohos.permission.INTERNET", "ohos.permission.CAMERA"}; - subCmd.inputSchema = R"({"type": "object", "properties": {"input": {"type": "string"}}})"; - subCmd.outputSchema = R"({"type": "array", "items": {"type": "string"}})"; - subCmd.argMapping = std::make_shared(); - subCmd.argMapping->type = ArgMappingType::POSITIONAL; - subCmd.argMapping->order = "arg1,arg2"; - subCmd.eventTypes = {"stdout", "stderr", "exit"}; - subCmd.eventSchemas = R"({"stdout": {"type": "string"}, "exit": {"type": "number"}})"; + auto valid = []() { + SubCommandInfo subCmd; + subCmd.description = "desc"; + subCmd.inputSchema = R"({"type":"object"})"; + subCmd.outputSchema = R"({"type":"object"})"; + return subCmd; + }; - EXPECT_TRUE(SubCommandInfo::Validate(subCmd)); + // empty description + auto emptyDesc = valid(); + emptyDesc.description = ""; + EXPECT_FALSE(SubCommandInfo::Validate(emptyDesc)); + + // empty inputSchema + auto emptyInput = valid(); + emptyInput.inputSchema = ""; + EXPECT_FALSE(SubCommandInfo::Validate(emptyInput)); + + // invalid inputSchema JSON + auto badInput = valid(); + badInput.inputSchema = "{bad}"; + EXPECT_FALSE(SubCommandInfo::Validate(badInput)); + + // non-object inputSchema + auto nonObjInput = valid(); + nonObjInput.inputSchema = R"("not-object")"; + EXPECT_FALSE(SubCommandInfo::Validate(nonObjInput)); + + // empty outputSchema + auto emptyOutput = valid(); + emptyOutput.outputSchema = ""; + EXPECT_FALSE(SubCommandInfo::Validate(emptyOutput)); + + // invalid outputSchema JSON + auto badOutput = valid(); + badOutput.outputSchema = "{bad}"; + EXPECT_FALSE(SubCommandInfo::Validate(badOutput)); + + // non-object outputSchema + auto nonObjOutput = valid(); + nonObjOutput.outputSchema = "42"; + EXPECT_FALSE(SubCommandInfo::Validate(nonObjOutput)); + + // invalid eventSchemas + auto badEventSchemas = valid(); + badEventSchemas.eventSchemas = "{invalid}"; + EXPECT_FALSE(SubCommandInfo::Validate(badEventSchemas)); + + // non-object eventSchemas + auto nonObjEvent = valid(); + nonObjEvent.eventSchemas = R"("string")"; + EXPECT_FALSE(SubCommandInfo::Validate(nonObjEvent)); + + // valid with empty eventSchemas (accepted) + auto noEvent = valid(); + noEvent.eventSchemas = ""; + EXPECT_TRUE(SubCommandInfo::Validate(noEvent)); + + // valid with proper eventSchemas + auto withEvent = valid(); + withEvent.eventSchemas = R"({"stdout":{"type":"string"}})"; + EXPECT_TRUE(SubCommandInfo::Validate(withEvent)); GTEST_LOG_(INFO) << "SubCommandInfo_Validate_1800 end"; } -/** - * @tc.name: SubCommandInfo_Validate_1900 - * @tc.desc: Test SubCommandInfo::Validate with minimal valid data - * @tc.type: FUNC - */ -HWTEST_F(SubCommandInfoTest, SubCommandInfo_Validate_1900, TestSize.Level1) -{ - GTEST_LOG_(INFO) << "SubCommandInfo_Validate_1900 start"; - - SubCommandInfo subCmd; - subCmd.description = "Minimal"; - subCmd.inputSchema = "{}"; - subCmd.outputSchema = "{}"; - subCmd.argMapping = std::make_shared(); - - EXPECT_TRUE(SubCommandInfo::Validate(subCmd)); - - GTEST_LOG_(INFO) << "SubCommandInfo_Validate_1900 end"; -} - } // namespace CliTool } // namespace OHOS diff --git a/test/unittest/cli_tool_mgr/tool_info_test/BUILD.gn b/cli_tool_framework/test/unittest/tool_info_test/BUILD.gn similarity index 100% rename from test/unittest/cli_tool_mgr/tool_info_test/BUILD.gn rename to cli_tool_framework/test/unittest/tool_info_test/BUILD.gn diff --git a/test/unittest/cli_tool_mgr/tool_info_test/tool_info_test.cpp b/cli_tool_framework/test/unittest/tool_info_test/tool_info_test.cpp similarity index 73% rename from test/unittest/cli_tool_mgr/tool_info_test/tool_info_test.cpp rename to cli_tool_framework/test/unittest/tool_info_test/tool_info_test.cpp index f3593889f7..7f8a9a241c 100644 --- a/test/unittest/cli_tool_mgr/tool_info_test/tool_info_test.cpp +++ b/cli_tool_framework/test/unittest/tool_info_test/tool_info_test.cpp @@ -13,13 +13,14 @@ * limitations under the License. */ -#include #include +#include #include #include #include "tool_info.h" +using namespace nlohmann::literals; using namespace testing::ext; namespace OHOS { @@ -42,7 +43,7 @@ void ToolInfoTest::TearDown() {} /** * @tc.name: ToolInfo_Marshalling_0100 - * @tc.desc: Test ToolInfo Marshalling with argMapping + * @tc.desc: Test ToolInfo Marshalling * @tc.type: FUNC */ HWTEST_F(ToolInfoTest, ToolInfo_Marshalling_0100, TestSize.Level1) @@ -57,10 +58,7 @@ HWTEST_F(ToolInfoTest, ToolInfo_Marshalling_0100, TestSize.Level1) tool.requirePermissions = {"ohos.permission.INTERNET"}; tool.inputSchema = "{}"; tool.outputSchema = "{}"; - tool.argMapping = std::make_shared(); - tool.argMapping->type = ArgMappingType::FLAG; tool.eventSchemas = "{}"; - tool.timeout = 30000; tool.eventTypes = {"stdout"}; tool.hasSubCommand = false; @@ -74,7 +72,7 @@ HWTEST_F(ToolInfoTest, ToolInfo_Marshalling_0100, TestSize.Level1) /** * @tc.name: ToolInfo_Marshalling_0200 - * @tc.desc: Test ToolInfo Marshalling without argMapping + * @tc.desc: Test ToolInfo Marshalling with subcommands * @tc.type: FUNC */ HWTEST_F(ToolInfoTest, ToolInfo_Marshalling_0200, TestSize.Level1) @@ -84,18 +82,18 @@ HWTEST_F(ToolInfoTest, ToolInfo_Marshalling_0200, TestSize.Level1) ToolInfo tool; tool.name = "test_tool_no_arg"; tool.version = "2.0.0"; - tool.description = "Tool without argMapping"; + tool.description = "Tool with subcommands"; tool.executablePath = "/bin/test2"; - tool.requirePermissions = {}; + tool.requirePermissions = {"ohos.permission.INTERNET"}; tool.inputSchema = "{}"; tool.outputSchema = "{}"; - tool.argMapping = nullptr; tool.eventSchemas = "{}"; - tool.timeout = 0; tool.eventTypes = {}; tool.hasSubCommand = true; SubCommandInfo subCmd; subCmd.description = "sub1"; + subCmd.inputSchema = "{}"; + subCmd.outputSchema = "{}"; tool.subcommands["sub1"] = subCmd; Parcel parcel; @@ -108,7 +106,7 @@ HWTEST_F(ToolInfoTest, ToolInfo_Marshalling_0200, TestSize.Level1) /** * @tc.name: ToolInfo_Unmarshalling_0100 - * @tc.desc: Test ToolInfo Unmarshalling with argMapping + * @tc.desc: Test ToolInfo Unmarshalling * @tc.type: FUNC */ HWTEST_F(ToolInfoTest, ToolInfo_Unmarshalling_0100, TestSize.Level1) @@ -123,17 +121,13 @@ HWTEST_F(ToolInfoTest, ToolInfo_Unmarshalling_0100, TestSize.Level1) original.requirePermissions = {"ohos.permission.CAMERA", "ohos.permission.MICROPHONE"}; original.inputSchema = R"({"type": "object", "properties": {"input": {"type": "string"}}})"; original.outputSchema = R"({"type": "string"})"; - original.argMapping = std::make_shared(); - original.argMapping->type = ArgMappingType::POSITIONAL; - original.argMapping->separator = ""; - original.argMapping->order = "arg1,arg2,arg3"; - original.argMapping->templates = "{}"; original.eventSchemas = "{}"; - original.timeout = 60000; original.eventTypes = {"stdout", "stderr", "exit"}; original.hasSubCommand = true; SubCommandInfo buildSubCmd; buildSubCmd.description = "Build subcommand"; + buildSubCmd.inputSchema = "{}"; + buildSubCmd.outputSchema = "{}"; original.subcommands["build"] = buildSubCmd; Parcel parcel; @@ -146,9 +140,6 @@ HWTEST_F(ToolInfoTest, ToolInfo_Unmarshalling_0100, TestSize.Level1) EXPECT_EQ(result->name, "original_tool"); EXPECT_EQ(result->version, "3.0.0"); EXPECT_EQ(result->requirePermissions.size(), 2u); - EXPECT_TRUE(result->argMapping != nullptr); - EXPECT_EQ(result->argMapping->type, ArgMappingType::POSITIONAL); - EXPECT_EQ(result->timeout, 60000); EXPECT_TRUE(result->hasSubCommand); EXPECT_EQ(result->subcommands.size(), 1u); @@ -159,7 +150,7 @@ HWTEST_F(ToolInfoTest, ToolInfo_Unmarshalling_0100, TestSize.Level1) /** * @tc.name: ToolInfo_Unmarshalling_0200 - * @tc.desc: Test ToolInfo Unmarshalling without argMapping + * @tc.desc: Test ToolInfo Unmarshalling with simple data * @tc.type: FUNC */ HWTEST_F(ToolInfoTest, ToolInfo_Unmarshalling_0200, TestSize.Level1) @@ -171,12 +162,10 @@ HWTEST_F(ToolInfoTest, ToolInfo_Unmarshalling_0200, TestSize.Level1) original.version = "1.0.0"; original.description = "Simple tool"; original.executablePath = "/bin/simple"; - original.requirePermissions = {}; + original.requirePermissions = {"ohos.permission.INTERNET"}; original.inputSchema = "{}"; original.outputSchema = "{}"; - original.argMapping = nullptr; original.eventSchemas = "{}"; - original.timeout = 0; original.eventTypes = {}; original.hasSubCommand = false; @@ -188,7 +177,6 @@ HWTEST_F(ToolInfoTest, ToolInfo_Unmarshalling_0200, TestSize.Level1) ASSERT_NE(result, nullptr); EXPECT_EQ(result->name, "simple_tool"); - EXPECT_TRUE(result->argMapping == nullptr); EXPECT_FALSE(result->hasSubCommand); delete result; @@ -234,11 +222,6 @@ HWTEST_F(ToolInfoTest, ToolInfo_Unmarshalling_0400, TestSize.Level1) subCmd.requirePermissions = {"ohos.permission.INTERNET"}; subCmd.inputSchema = R"({"type": "object", "properties": {"arg": {"type": "string"}}})"; subCmd.outputSchema = R"({"type": "string"})"; - subCmd.argMapping = std::make_shared(); - subCmd.argMapping->type = ArgMappingType::MIXED; - subCmd.argMapping->separator = ","; - subCmd.argMapping->order = "arg1,arg2"; - subCmd.argMapping->templates = R"({"arg1": "--input=${value}"})"; subCmd.eventTypes = {"stdout", "stderr"}; subCmd.eventSchemas = R"({"stdout": {"type": "string"}})"; @@ -254,121 +237,330 @@ HWTEST_F(ToolInfoTest, ToolInfo_Unmarshalling_0400, TestSize.Level1) EXPECT_EQ(result->name, "tool_with_full_subcommands"); EXPECT_TRUE(result->hasSubCommand); EXPECT_EQ(result->subcommands.size(), 1u); - EXPECT_TRUE(result->subcommands.contains("run")); + EXPECT_TRUE(result->subcommands.find("run") != result->subcommands.end()); const auto &resultSubCmd = result->subcommands["run"]; EXPECT_EQ(resultSubCmd.description, "Full subcommand"); EXPECT_EQ(resultSubCmd.requirePermissions.size(), 1u); EXPECT_EQ(resultSubCmd.requirePermissions[0], "ohos.permission.INTERNET"); - EXPECT_EQ(resultSubCmd.inputSchema, R"({"type": "object", "properties": {"arg": {"type": "string"}}})"); - EXPECT_EQ(resultSubCmd.outputSchema, R"({"type": "string"})"); - ASSERT_NE(resultSubCmd.argMapping, nullptr); - EXPECT_EQ(resultSubCmd.argMapping->type, ArgMappingType::MIXED); - EXPECT_EQ(resultSubCmd.argMapping->separator, ","); - EXPECT_EQ(resultSubCmd.argMapping->order, "arg1,arg2"); - EXPECT_EQ(resultSubCmd.argMapping->templates, R"({"arg1": "--input=${value}"})"); + EXPECT_EQ(nlohmann::json::parse(resultSubCmd.inputSchema), + nlohmann::json::parse(R"({"type": "object", "properties": {"arg": {"type": "string"}}})")); + EXPECT_EQ(nlohmann::json::parse(resultSubCmd.outputSchema), nlohmann::json::parse(R"({"type": "string"})")); EXPECT_EQ(resultSubCmd.eventTypes.size(), 2u); - EXPECT_EQ(resultSubCmd.eventSchemas, R"({"stdout": {"type": "string"}})"); + EXPECT_EQ(nlohmann::json::parse(resultSubCmd.eventSchemas), + nlohmann::json::parse(R"({"stdout": {"type": "string"}})")); delete result; GTEST_LOG_(INFO) << "ToolInfo_Unmarshalling_0400 end"; } +/** + * @tc.name: ToolInfo_Unmarshalling_0500 + * @tc.desc: Test ToolInfo Unmarshalling fails when serialized subcommand JSON is invalid + * @tc.type: FUNC + */ +HWTEST_F(ToolInfoTest, ToolInfo_Unmarshalling_0500, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "ToolInfo_Unmarshalling_0500 start"; + + nlohmann::json invalidSubcommands = { + {"bad", { + {"description", ""}, + {"requirePermissions", nlohmann::json::array()}, + {"inputSchema", nlohmann::json::object()}, + {"outputSchema", nlohmann::json::object()} + }} + }; + + Parcel parcel; + ASSERT_TRUE(parcel.WriteString("tool")); + ASSERT_TRUE(parcel.WriteString("1.0.0")); + ASSERT_TRUE(parcel.WriteString("description")); + ASSERT_TRUE(parcel.WriteString("/bin/tool")); + ASSERT_TRUE(parcel.WriteStringVector({})); + ASSERT_TRUE(parcel.WriteString("{}")); + ASSERT_TRUE(parcel.WriteString("{}")); + ASSERT_TRUE(parcel.WriteString("")); + ASSERT_TRUE(parcel.WriteStringVector({})); + ASSERT_TRUE(parcel.WriteBool(true)); + ASSERT_TRUE(parcel.WriteString(invalidSubcommands.dump())); + parcel.RewindRead(0); + + EXPECT_EQ(ToolInfo::Unmarshalling(parcel), nullptr); + + GTEST_LOG_(INFO) << "ToolInfo_Unmarshalling_0500 end"; +} + // ==================== ToolsRawData Tests ==================== /** - * @tc.name: ToolsRawData_Marshalling_0100 - * @tc.desc: Test ToolsRawData Marshalling success + * @tc.name: ToolsRawData_FromToolInfoVec_0100 + * @tc.desc: Test ToolsRawData FromToolInfoVec and ToToolInfoVec round trip * @tc.type: FUNC */ -HWTEST_F(ToolInfoTest, ToolsRawData_Marshalling_0100, TestSize.Level1) +HWTEST_F(ToolInfoTest, ToolsRawData_FromToolInfoVec_0100, TestSize.Level1) { - GTEST_LOG_(INFO) << "ToolsRawData_Marshalling_0100 start"; + GTEST_LOG_(INFO) << "ToolsRawData_FromToolInfoVec_0100 start"; + + std::vector tools; + ToolInfo tool1; + tool1.name = "ohos-test1"; + tool1.version = "1.0.0"; + tool1.description = "Test tool 1"; + tool1.executablePath = "/bin/test1"; + tool1.requirePermissions = {"ohos.permission.INTERNET"}; + tool1.inputSchema = R"({"type": "object"})"; + tool1.outputSchema = R"({"type": "string"})"; + tools.push_back(tool1); + + ToolInfo tool2; + tool2.name = "ohos-test2"; + tool2.version = "2.0.0"; + tool2.description = "Test tool 2"; + tool2.executablePath = "/bin/test2"; + tool2.requirePermissions = {}; + tool2.inputSchema = "{}"; + tool2.outputSchema = "{}"; + tool2.hasSubCommand = true; + SubCommandInfo subCmd; + subCmd.description = "Sub command"; + subCmd.inputSchema = "{}"; + subCmd.outputSchema = "{}"; + tool2.subcommands["sub1"] = subCmd; + tools.push_back(tool2); ToolsRawData rawData; - rawData.data = {1, 2, 3, 4, 5}; + ToolsRawData::FromToolInfoVec(tools, rawData); - Parcel parcel; - bool ret = rawData.Marshalling(parcel); + EXPECT_NE(rawData.data, nullptr); + EXPECT_GT(rawData.size, 0u); + EXPECT_EQ(rawData.ownedData.size(), rawData.size); - EXPECT_TRUE(ret); + std::vector parsedTools; + int32_t ret = ToolsRawData::ToToolInfoVec(rawData, parsedTools); - GTEST_LOG_(INFO) << "ToolsRawData_Marshalling_0100 end"; + EXPECT_EQ(ret, ERR_OK); + EXPECT_EQ(parsedTools.size(), 2u); + EXPECT_EQ(parsedTools[0].name, "ohos-test1"); + EXPECT_EQ(parsedTools[0].version, "1.0.0"); + EXPECT_EQ(parsedTools[1].name, "ohos-test2"); + EXPECT_EQ(parsedTools[1].subcommands.size(), 1u); + + GTEST_LOG_(INFO) << "ToolsRawData_FromToolInfoVec_0100 end"; } /** - * @tc.name: ToolsRawData_Marshalling_0200 - * @tc.desc: Test ToolsRawData Marshalling with empty data + * @tc.name: ToolsRawData_FromToolInfoVec_0200 + * @tc.desc: Test ToolsRawData with empty vector * @tc.type: FUNC */ -HWTEST_F(ToolInfoTest, ToolsRawData_Marshalling_0200, TestSize.Level1) +HWTEST_F(ToolInfoTest, ToolsRawData_FromToolInfoVec_0200, TestSize.Level1) { - GTEST_LOG_(INFO) << "ToolsRawData_Marshalling_0200 start"; + GTEST_LOG_(INFO) << "ToolsRawData_FromToolInfoVec_0200 start"; + + std::vector tools; + ToolsRawData rawData; + ToolsRawData::FromToolInfoVec(tools, rawData); + + EXPECT_NE(rawData.data, nullptr); + EXPECT_GT(rawData.size, 0u); + + std::vector parsedTools; + int32_t ret = ToolsRawData::ToToolInfoVec(rawData, parsedTools); + + EXPECT_EQ(ret, ERR_OK); + EXPECT_EQ(parsedTools.size(), 0u); + + GTEST_LOG_(INFO) << "ToolsRawData_FromToolInfoVec_0200 end"; +} + +/** + * @tc.name: ToolsRawData_RawDataCpy_0100 + * @tc.desc: Test ToolsRawData RawDataCpy + * @tc.type: FUNC + */ +HWTEST_F(ToolInfoTest, ToolsRawData_RawDataCpy_0100, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "ToolsRawData_RawDataCpy_0100 start"; + + std::string testData = "test data for copy"; + ToolsRawData rawData; + rawData.size = testData.size(); + + int32_t ret = rawData.RawDataCpy(testData.data()); + + EXPECT_EQ(ret, ERR_OK); + EXPECT_NE(rawData.data, nullptr); + EXPECT_TRUE(rawData.isMalloc); + + // Verify data content + std::string copiedData(reinterpret_cast(rawData.data), rawData.size); + EXPECT_EQ(copiedData, testData); + + GTEST_LOG_(INFO) << "ToolsRawData_RawDataCpy_0100 end"; +} + +/** + * @tc.name: ToolsRawData_RawDataCpy_0200 + * @tc.desc: Test ToolsRawData RawDataCpy with null data + * @tc.type: FUNC + */ +HWTEST_F(ToolInfoTest, ToolsRawData_RawDataCpy_0200, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "ToolsRawData_RawDataCpy_0200 start"; ToolsRawData rawData; - rawData.data = {}; + rawData.size = 10; - Parcel parcel; - bool ret = rawData.Marshalling(parcel); + int32_t ret = rawData.RawDataCpy(nullptr); - EXPECT_TRUE(ret); + EXPECT_NE(ret, ERR_OK); - GTEST_LOG_(INFO) << "ToolsRawData_Marshalling_0200 end"; + GTEST_LOG_(INFO) << "ToolsRawData_RawDataCpy_0200 end"; } /** - * @tc.name: ToolsRawData_Unmarshalling_0100 - * @tc.desc: Test ToolsRawData Unmarshalling success + * @tc.name: ToolsRawData_Destructor_0100 + * @tc.desc: Test ToolsRawData destructor properly frees memory * @tc.type: FUNC */ -HWTEST_F(ToolInfoTest, ToolsRawData_Unmarshalling_0100, TestSize.Level1) +HWTEST_F(ToolInfoTest, ToolsRawData_Destructor_0100, TestSize.Level1) { - GTEST_LOG_(INFO) << "ToolsRawData_Unmarshalling_0100 start"; + GTEST_LOG_(INFO) << "ToolsRawData_Destructor_0100 start"; - ToolsRawData original; - original.data = {10, 20, 30, 40, 50, 60}; + { + ToolsRawData rawData; + std::string testData = "test data"; + rawData.size = testData.size(); + rawData.RawDataCpy(testData.data()); + EXPECT_TRUE(rawData.isMalloc); + // Destructor should free the memory + } - Parcel parcel; - ASSERT_TRUE(original.Marshalling(parcel)); + // If we reach here without crash, the test passes + EXPECT_TRUE(true); - parcel.RewindRead(0); - ToolsRawData *result = ToolsRawData::Unmarshalling(parcel); - - ASSERT_NE(result, nullptr); - EXPECT_EQ(result->data.size(), 6u); - EXPECT_EQ(result->data[0], 10u); - EXPECT_EQ(result->data[5], 60u); - - delete result; - - GTEST_LOG_(INFO) << "ToolsRawData_Unmarshalling_0100 end"; + GTEST_LOG_(INFO) << "ToolsRawData_Destructor_0100 end"; } /** - * @tc.name: ToolsRawData_Unmarshalling_0200 - * @tc.desc: Test ToolsRawData Unmarshalling with empty data + * @tc.name: ToolsRawData_RawDataCpy_0300 + * @tc.desc: Test ToolsRawData RawDataCpy after FromToolInfoVec (isMalloc=false) * @tc.type: FUNC */ -HWTEST_F(ToolInfoTest, ToolsRawData_Unmarshalling_0200, TestSize.Level1) +HWTEST_F(ToolInfoTest, ToolsRawData_RawDataCpy_0300, TestSize.Level1) { - GTEST_LOG_(INFO) << "ToolsRawData_Unmarshalling_0200 start"; + GTEST_LOG_(INFO) << "ToolsRawData_RawDataCpy_0300 start"; - ToolsRawData original; - original.data = {}; + // First, use FromToolInfoVec which sets isMalloc=false + std::vector tools; + ToolInfo tool; + tool.name = "ohos-test"; + tool.version = "1.0.0"; + tool.description = "Test tool"; + tool.executablePath = "/bin/test"; + tool.inputSchema = "{}"; + tool.outputSchema = "{}"; + tools.push_back(tool); - Parcel parcel; - ASSERT_TRUE(original.Marshalling(parcel)); + ToolsRawData rawData; + ToolsRawData::FromToolInfoVec(tools, rawData); + EXPECT_FALSE(rawData.isMalloc); + EXPECT_NE(rawData.data, nullptr); - parcel.RewindRead(0); - ToolsRawData *result = ToolsRawData::Unmarshalling(parcel); + // Then call RawDataCpy, which should not free ownedData's internal buffer + std::string newData = "new test data for copy"; + rawData.size = newData.size(); + int32_t ret = rawData.RawDataCpy(newData.data()); - ASSERT_NE(result, nullptr); - EXPECT_EQ(result->data.size(), 0u); + EXPECT_EQ(ret, ERR_OK); + EXPECT_TRUE(rawData.isMalloc); + EXPECT_NE(rawData.data, nullptr); - delete result; + // Verify new data content + std::string copiedData(reinterpret_cast(rawData.data), rawData.size); + EXPECT_EQ(copiedData, newData); - GTEST_LOG_(INFO) << "ToolsRawData_Unmarshalling_0200 end"; + GTEST_LOG_(INFO) << "ToolsRawData_RawDataCpy_0300 end"; +} + +/** + * @tc.name: ToolsRawData_RawDataCpy_0400 + * @tc.desc: Test ToolsRawData RawDataCpy replaces previous malloc data + * @tc.type: FUNC + */ +HWTEST_F(ToolInfoTest, ToolsRawData_RawDataCpy_0400, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "ToolsRawData_RawDataCpy_0400 start"; + + ToolsRawData rawData; + + // First allocation + std::string testData1 = "first data"; + rawData.size = testData1.size(); + rawData.RawDataCpy(testData1.data()); + EXPECT_TRUE(rawData.isMalloc); + + // Second allocation should free the first + std::string testData2 = "second data that is longer"; + rawData.size = testData2.size(); + int32_t ret = rawData.RawDataCpy(testData2.data()); + + EXPECT_EQ(ret, ERR_OK); + EXPECT_TRUE(rawData.isMalloc); + + // Verify new data content + std::string copiedData(reinterpret_cast(rawData.data), rawData.size); + EXPECT_EQ(copiedData, testData2); + + GTEST_LOG_(INFO) << "ToolsRawData_RawDataCpy_0400 end"; +} + +/** + * @tc.name: ToolsRawData_ToToolInfoVec_0300 + * @tc.desc: Test ToolsRawData ToToolInfoVec rejects oversized count, oversized item, and malformed JSON + * @tc.type: FUNC + */ +HWTEST_F(ToolInfoTest, ToolsRawData_ToToolInfoVec_0300, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "ToolsRawData_ToToolInfoVec_0300 start"; + + auto fillRawData = [](const std::string &data, ToolsRawData &rawData) { + rawData.ownedData = data; + rawData.data = rawData.ownedData.data(); + rawData.size = rawData.ownedData.size(); + rawData.isMalloc = false; + }; + + uint32_t tooManyTools = 200001; + std::string tooManyData(reinterpret_cast(&tooManyTools), sizeof(tooManyTools)); + ToolsRawData tooManyRawData; + fillRawData(tooManyData, tooManyRawData); + std::vector parsedTools; + EXPECT_NE(ToolsRawData::ToToolInfoVec(tooManyRawData, parsedTools), ERR_OK); + + uint32_t count = 1; + uint32_t oversizedTool = 64; + std::string oversizedData(reinterpret_cast(&count), sizeof(count)); + oversizedData.append(reinterpret_cast(&oversizedTool), sizeof(oversizedTool)); + oversizedData.append("{}"); + ToolsRawData oversizedRawData; + fillRawData(oversizedData, oversizedRawData); + parsedTools.clear(); + EXPECT_NE(ToolsRawData::ToToolInfoVec(oversizedRawData, parsedTools), ERR_OK); + + std::string invalidJson = "{invalid json}"; + uint32_t invalidJsonSize = invalidJson.size(); + std::string invalidJsonData(reinterpret_cast(&count), sizeof(count)); + invalidJsonData.append(reinterpret_cast(&invalidJsonSize), sizeof(invalidJsonSize)); + invalidJsonData.append(invalidJson); + ToolsRawData invalidJsonRawData; + fillRawData(invalidJsonData, invalidJsonRawData); + parsedTools.clear(); + EXPECT_NE(ToolsRawData::ToToolInfoVec(invalidJsonRawData, parsedTools), ERR_OK); + + GTEST_LOG_(INFO) << "ToolsRawData_ToToolInfoVec_0300 end"; } // ==================== ToolInfo ParseToJson Tests ==================== @@ -390,10 +582,7 @@ HWTEST_F(ToolInfoTest, ToolInfo_ParseToJson_0100, TestSize.Level1) tool.requirePermissions = {"ohos.permission.INTERNET"}; tool.inputSchema = R"({"type": "object"})"; tool.outputSchema = R"({"type": "string"})"; - tool.argMapping = std::make_shared(); - tool.argMapping->type = ArgMappingType::FLAG; tool.eventSchemas = R"({"stdout": {"type": "string"}})"; - tool.timeout = 30000; tool.eventTypes = {"stdout", "stderr"}; tool.hasSubCommand = false; @@ -403,15 +592,13 @@ HWTEST_F(ToolInfoTest, ToolInfo_ParseToJson_0100, TestSize.Level1) EXPECT_EQ(json["version"], "1.0.0"); EXPECT_EQ(json["description"], "JSON test tool"); EXPECT_EQ(json["executablePath"], "/bin/json"); - EXPECT_EQ(json["timeout"], 30000); - EXPECT_TRUE(json.contains("argMapping")); GTEST_LOG_(INFO) << "ToolInfo_ParseToJson_0100 end"; } /** * @tc.name: ToolInfo_ParseToJson_0200 - * @tc.desc: Test ToolInfo ParseToJson without argMapping + * @tc.desc: Test ToolInfo ParseToJson with minimal data * @tc.type: FUNC */ HWTEST_F(ToolInfoTest, ToolInfo_ParseToJson_0200, TestSize.Level1) @@ -420,12 +607,10 @@ HWTEST_F(ToolInfoTest, ToolInfo_ParseToJson_0200, TestSize.Level1) ToolInfo tool; tool.name = "no_arg_tool"; - tool.argMapping = nullptr; nlohmann::json json = tool.ParseToJson(); EXPECT_EQ(json["name"], "no_arg_tool"); - EXPECT_FALSE(json.contains("argMapping")); GTEST_LOG_(INFO) << "ToolInfo_ParseToJson_0200 end"; } @@ -476,9 +661,7 @@ HWTEST_F(ToolInfoTest, ToolInfo_ParseFromJson_0100, TestSize.Level1) "requirePermissions": ["ohos.permission.CAMERA"], "inputSchema": {"type": "object"}, "outputSchema": {"type": "array"}, - "argMapping": {"type": "positional", "order": "arg1,arg2"}, "eventSchemas": {"exit": {"type": "number"}}, - "timeout": 60000, "eventTypes": ["stdout", "exit"], "hasSubCommand": false })"_json; @@ -492,39 +675,41 @@ HWTEST_F(ToolInfoTest, ToolInfo_ParseFromJson_0100, TestSize.Level1) EXPECT_EQ(tool.description, "Parsed from JSON"); EXPECT_EQ(tool.executablePath, "/bin/parsed"); EXPECT_EQ(tool.requirePermissions.size(), 1u); - EXPECT_EQ(tool.timeout, 60000); EXPECT_FALSE(tool.hasSubCommand); - ASSERT_NE(tool.argMapping, nullptr); - EXPECT_EQ(tool.argMapping->type, ArgMappingType::POSITIONAL); GTEST_LOG_(INFO) << "ToolInfo_ParseFromJson_0100 end"; } /** - * @tc.name: ToolInfo_ParseFromJson_0200 + * @tc.name: ToolInfo_ParseFromJson_0400 * @tc.desc: Test ToolInfo ParseFromJson with subcommands * @tc.type: FUNC */ -HWTEST_F(ToolInfoTest, ToolInfo_ParseFromJson_0200, TestSize.Level1) +HWTEST_F(ToolInfoTest, ToolInfo_ParseFromJson_0400, TestSize.Level1) { - GTEST_LOG_(INFO) << "ToolInfo_ParseFromJson_0200 start"; + GTEST_LOG_(INFO) << "ToolInfo_ParseFromJson_0400 start"; nlohmann::json json = R"({ "name": "hms-tool_with_sub", "version": "1.0.0", "description": "Tool with subcommands", "executablePath": "/bin/tool", + "requirePermissions": ["ohos.permission.INTERNET"], + "inputSchema": {}, + "outputSchema": {}, "hasSubCommand": true, "subcommands": { "build": { "description": "Build the project", + "requirePermissions": [], "inputSchema": {"type": "object"}, - "outputSchema": {"type": "string"}, - "argMapping": {"type": "flag"} + "outputSchema": {"type": "string"} }, "run": { "description": "Run the project", - "argMapping": {"type": "positional", "order": "arg1"} + "requirePermissions": [], + "inputSchema": {"type": "object"}, + "outputSchema": {"type": "string"} } } })"_json; @@ -532,23 +717,23 @@ HWTEST_F(ToolInfoTest, ToolInfo_ParseFromJson_0200, TestSize.Level1) ToolInfo tool; bool result = ToolInfo::ParseFromJson(json, tool); - EXPECT_TRUE(result); + ASSERT_TRUE(result); EXPECT_EQ(tool.name, "hms-tool_with_sub"); EXPECT_TRUE(tool.hasSubCommand); EXPECT_EQ(tool.subcommands.size(), 2u); EXPECT_EQ(tool.subcommands["build"].description, "Build the project"); - GTEST_LOG_(INFO) << "ToolInfo_ParseFromJson_0200 end"; + GTEST_LOG_(INFO) << "ToolInfo_ParseFromJson_0400 end"; } /** - * @tc.name: ToolInfo_ParseFromJson_0300 + * @tc.name: ToolInfo_ParseFromJson_0500 * @tc.desc: Test ToolInfo ParseFromJson with empty JSON * @tc.type: FUNC */ -HWTEST_F(ToolInfoTest, ToolInfo_ParseFromJson_0300, TestSize.Level1) +HWTEST_F(ToolInfoTest, ToolInfo_ParseFromJson_0500, TestSize.Level1) { - GTEST_LOG_(INFO) << "ToolInfo_ParseFromJson_0300 start"; + GTEST_LOG_(INFO) << "ToolInfo_ParseFromJson_0500 start"; nlohmann::json json; @@ -558,7 +743,7 @@ HWTEST_F(ToolInfoTest, ToolInfo_ParseFromJson_0300, TestSize.Level1) EXPECT_FALSE(result); EXPECT_TRUE(tool.name.empty()); - GTEST_LOG_(INFO) << "ToolInfo_ParseFromJson_0300 end"; + GTEST_LOG_(INFO) << "ToolInfo_ParseFromJson_0500 end"; } // ==================== ToolInfo ParseFromJson/ParseToJson Round Trip Tests ==================== @@ -580,31 +765,28 @@ HWTEST_F(ToolInfoTest, ToolInfo_ParseFromJson_ParseToJson_RoundTrip_0100, TestSi "requirePermissions": ["ohos.permission.INTERNET", "ohos.permission.CAMERA"], "inputSchema": {"type": "object", "properties": {"input": {"type": "string"}}}, "outputSchema": {"type": "array"}, - "argMapping": {"type": "mixed", "separator": ",", "order": "a,b"}, "eventSchemas": {"stdout": {"type": "string"}}, - "timeout": 45000, "eventTypes": ["stdout", "stderr"], "hasSubCommand": true, "subcommands": { "sub1": { "description": "Sub 1", + "requirePermissions": [], "inputSchema": {"type": "object"}, - "outputSchema": {"type": "string"}, - "argMapping": {"type": "flag"} + "outputSchema": {"type": "string"} } } })"_json; ToolInfo tool; bool result = ToolInfo::ParseFromJson(originalJson, tool); - EXPECT_TRUE(result); + ASSERT_TRUE(result); nlohmann::json resultJson = tool.ParseToJson(); EXPECT_EQ(resultJson["name"], originalJson["name"]); EXPECT_EQ(resultJson["version"], originalJson["version"]); EXPECT_EQ(resultJson["description"], originalJson["description"]); EXPECT_EQ(resultJson["executablePath"], originalJson["executablePath"]); - EXPECT_EQ(resultJson["timeout"], originalJson["timeout"]); EXPECT_EQ(resultJson["hasSubCommand"], originalJson["hasSubCommand"]); EXPECT_EQ(resultJson["eventTypes"], originalJson["eventTypes"]); @@ -612,13 +794,13 @@ HWTEST_F(ToolInfoTest, ToolInfo_ParseFromJson_ParseToJson_RoundTrip_0100, TestSi } /** - * @tc.name: ToolInfo_ParseToJson_0400 + * @tc.name: ToolInfo_ParseToJson_0500 * @tc.desc: Test ToolInfo ParseToJson with invalid inputSchema JSON string * @tc.type: FUNC */ -HWTEST_F(ToolInfoTest, ToolInfo_ParseToJson_0400, TestSize.Level1) +HWTEST_F(ToolInfoTest, ToolInfo_ParseToJson_0500, TestSize.Level1) { - GTEST_LOG_(INFO) << "ToolInfo_ParseToJson_0400 start"; + GTEST_LOG_(INFO) << "ToolInfo_ParseToJson_0500 start"; ToolInfo tool; tool.name = "invalid_input_schema"; @@ -635,17 +817,17 @@ HWTEST_F(ToolInfoTest, ToolInfo_ParseToJson_0400, TestSize.Level1) EXPECT_TRUE(json.contains("outputSchema")); EXPECT_TRUE(json.contains("eventSchemas")); - GTEST_LOG_(INFO) << "ToolInfo_ParseToJson_0400 end"; + GTEST_LOG_(INFO) << "ToolInfo_ParseToJson_0500 end"; } /** - * @tc.name: ToolInfo_ParseToJson_0500 + * @tc.name: ToolInfo_ParseToJson_0700 * @tc.desc: Test ToolInfo ParseToJson with invalid outputSchema JSON string * @tc.type: FUNC */ -HWTEST_F(ToolInfoTest, ToolInfo_ParseToJson_0500, TestSize.Level1) +HWTEST_F(ToolInfoTest, ToolInfo_ParseToJson_0700, TestSize.Level1) { - GTEST_LOG_(INFO) << "ToolInfo_ParseToJson_0500 start"; + GTEST_LOG_(INFO) << "ToolInfo_ParseToJson_0700 start"; ToolInfo tool; tool.name = "invalid_output_schema"; @@ -661,7 +843,7 @@ HWTEST_F(ToolInfoTest, ToolInfo_ParseToJson_0500, TestSize.Level1) EXPECT_TRUE(json.contains("outputSchema")); EXPECT_EQ(json["outputSchema"], "{broken json}"); - GTEST_LOG_(INFO) << "ToolInfo_ParseToJson_0500 end"; + GTEST_LOG_(INFO) << "ToolInfo_ParseToJson_0700 end"; } /** @@ -692,11 +874,11 @@ HWTEST_F(ToolInfoTest, ToolInfo_ParseToJson_0600, TestSize.Level1) } /** - * @tc.name: ToolInfo_ParseToJson_0700 + * @tc.name: ToolInfo_ParseToJson_0900 * @tc.desc: Test ToolInfo ParseToJson with complex valid schemas * @tc.type: FUNC */ -HWTEST_F(ToolInfoTest, ToolInfo_ParseToJson_0700, TestSize.Level1) +HWTEST_F(ToolInfoTest, ToolInfo_ParseToJson_0900, TestSize.Level1) { GTEST_LOG_(INFO) << "ToolInfo_ParseToJson_0700 start"; @@ -753,15 +935,15 @@ HWTEST_F(ToolInfoTest, ToolInfo_ParseToJson_0700, TestSize.Level1) EXPECT_TRUE(json["eventSchemas"].contains("stdout")); EXPECT_TRUE(json["eventSchemas"].contains("progress")); - GTEST_LOG_(INFO) << "ToolInfo_ParseToJson_0700 end"; + GTEST_LOG_(INFO) << "ToolInfo_ParseToJson_0900 end"; } /** - * @tc.name: ToolInfo_ParseToJson_0800 + * @tc.name: ToolInfo_ParseToJson_1000 * @tc.desc: Test ToolInfo ParseToJson with all invalid schemas * @tc.type: FUNC */ -HWTEST_F(ToolInfoTest, ToolInfo_ParseToJson_0800, TestSize.Level1) +HWTEST_F(ToolInfoTest, ToolInfo_ParseToJson_1000, TestSize.Level1) { GTEST_LOG_(INFO) << "ToolInfo_ParseToJson_0800 start"; @@ -782,17 +964,17 @@ HWTEST_F(ToolInfoTest, ToolInfo_ParseToJson_0800, TestSize.Level1) EXPECT_TRUE(json.contains("eventSchemas")); EXPECT_EQ(json["eventSchemas"], "invalid events"); - GTEST_LOG_(INFO) << "ToolInfo_ParseToJson_0800 end"; + GTEST_LOG_(INFO) << "ToolInfo_ParseToJson_1000 end"; } /** - * @tc.name: ToolInfo_ParseToJson_0900 + * @tc.name: ToolInfo_ParseToJson_1100 * @tc.desc: Test ToolInfo ParseToJson with empty inputSchema * @tc.type: FUNC */ -HWTEST_F(ToolInfoTest, ToolInfo_ParseToJson_0900, TestSize.Level1) +HWTEST_F(ToolInfoTest, ToolInfo_ParseToJson_1100, TestSize.Level1) { - GTEST_LOG_(INFO) << "ToolInfo_ParseToJson_0900 start"; + GTEST_LOG_(INFO) << "ToolInfo_ParseToJson_1100 start"; ToolInfo tool; tool.name = "empty_input_schema"; @@ -807,17 +989,17 @@ HWTEST_F(ToolInfoTest, ToolInfo_ParseToJson_0900, TestSize.Level1) EXPECT_FALSE(json.contains("inputSchema")); EXPECT_TRUE(json.contains("outputSchema")); - GTEST_LOG_(INFO) << "ToolInfo_ParseToJson_0900 end"; + GTEST_LOG_(INFO) << "ToolInfo_ParseToJson_1100 end"; } /** - * @tc.name: ToolInfo_ParseToJson_1000 + * @tc.name: ToolInfo_ParseToJson_1200 * @tc.desc: Test ToolInfo ParseToJson with empty outputSchema * @tc.type: FUNC */ -HWTEST_F(ToolInfoTest, ToolInfo_ParseToJson_1000, TestSize.Level1) +HWTEST_F(ToolInfoTest, ToolInfo_ParseToJson_1200, TestSize.Level1) { - GTEST_LOG_(INFO) << "ToolInfo_ParseToJson_1000 start"; + GTEST_LOG_(INFO) << "ToolInfo_ParseToJson_1200 start"; ToolInfo tool; tool.name = "empty_output_schema"; @@ -832,17 +1014,17 @@ HWTEST_F(ToolInfoTest, ToolInfo_ParseToJson_1000, TestSize.Level1) EXPECT_TRUE(json.contains("inputSchema")); EXPECT_FALSE(json.contains("outputSchema")); - GTEST_LOG_(INFO) << "ToolInfo_ParseToJson_1000 end"; + GTEST_LOG_(INFO) << "ToolInfo_ParseToJson_1200 end"; } /** - * @tc.name: ToolInfo_ParseToJson_1100 + * @tc.name: ToolInfo_ParseToJson_1300 * @tc.desc: Test ToolInfo ParseToJson with empty eventSchemas * @tc.type: FUNC */ -HWTEST_F(ToolInfoTest, ToolInfo_ParseToJson_1100, TestSize.Level1) +HWTEST_F(ToolInfoTest, ToolInfo_ParseToJson_1300, TestSize.Level1) { - GTEST_LOG_(INFO) << "ToolInfo_ParseToJson_1100 start"; + GTEST_LOG_(INFO) << "ToolInfo_ParseToJson_1300 start"; ToolInfo tool; tool.name = "empty_event_schemas"; @@ -858,17 +1040,17 @@ HWTEST_F(ToolInfoTest, ToolInfo_ParseToJson_1100, TestSize.Level1) EXPECT_TRUE(json.contains("outputSchema")); EXPECT_FALSE(json.contains("eventSchemas")); - GTEST_LOG_(INFO) << "ToolInfo_ParseToJson_1100 end"; + GTEST_LOG_(INFO) << "ToolInfo_ParseToJson_1300 end"; } /** - * @tc.name: ToolInfo_ParseToJson_1200 + * @tc.name: ToolInfo_ParseToJson_1400 * @tc.desc: Test ToolInfo ParseToJson with subcommands containing schemas * @tc.type: FUNC */ -HWTEST_F(ToolInfoTest, ToolInfo_ParseToJson_1200, TestSize.Level1) +HWTEST_F(ToolInfoTest, ToolInfo_ParseToJson_1400, TestSize.Level1) { - GTEST_LOG_(INFO) << "ToolInfo_ParseToJson_1200 start"; + GTEST_LOG_(INFO) << "ToolInfo_ParseToJson_1400 start"; ToolInfo tool; tool.name = "tool_with_subcmd_schemas"; @@ -907,17 +1089,17 @@ HWTEST_F(ToolInfoTest, ToolInfo_ParseToJson_1200, TestSize.Level1) EXPECT_TRUE(json["subcommands"]["invalid"].contains("eventSchemas")); EXPECT_EQ(json["subcommands"]["invalid"]["eventSchemas"], "invalid too"); - GTEST_LOG_(INFO) << "ToolInfo_ParseToJson_1200 end"; + GTEST_LOG_(INFO) << "ToolInfo_ParseToJson_1400 end"; } /** - * @tc.name: ToolInfo_ParseToJson_1300 + * @tc.name: ToolInfo_ParseToJson_1500 * @tc.desc: Test ToolInfo ParseToJson with primitive type schemas * @tc.type: FUNC */ -HWTEST_F(ToolInfoTest, ToolInfo_ParseToJson_1300, TestSize.Level1) +HWTEST_F(ToolInfoTest, ToolInfo_ParseToJson_1500, TestSize.Level1) { - GTEST_LOG_(INFO) << "ToolInfo_ParseToJson_1300 start"; + GTEST_LOG_(INFO) << "ToolInfo_ParseToJson_1500 start"; ToolInfo tool; tool.name = "primitive_schemas"; @@ -939,26 +1121,27 @@ HWTEST_F(ToolInfoTest, ToolInfo_ParseToJson_1300, TestSize.Level1) EXPECT_TRUE(json.contains("eventSchemas")); EXPECT_EQ(json["eventSchemas"]["status"]["type"], "boolean"); - GTEST_LOG_(INFO) << "ToolInfo_ParseToJson_1300 end"; + GTEST_LOG_(INFO) << "ToolInfo_ParseToJson_1500 end"; } // ==================== ValidateName Tests ==================== /** - * @tc.name: ToolInfo_ValidateName_0100 + * @tc.name: ToolInfo_ValidateName_0800 * @tc.desc: Test ToolInfo ValidateName with valid ohos- prefix * @tc.type: FUNC */ -HWTEST_F(ToolInfoTest, ToolInfo_ValidateName_0100, TestSize.Level1) +HWTEST_F(ToolInfoTest, ToolInfo_ValidateName_0800, TestSize.Level1) { - GTEST_LOG_(INFO) << "ToolInfo_ValidateName_0100 start"; + GTEST_LOG_(INFO) << "ToolInfo_ValidateName_0800 start"; EXPECT_TRUE(ToolInfo::ValidateName("ohos-ls")); EXPECT_TRUE(ToolInfo::ValidateName("ohos-test")); EXPECT_TRUE(ToolInfo::ValidateName("ohos-abc")); EXPECT_TRUE(ToolInfo::ValidateName("ohos-1234567890123456")); // 16 chars suffix + EXPECT_TRUE(ToolInfo::ValidateName("ohos-12345678901234567890123456789012")); // 32 chars suffix - GTEST_LOG_(INFO) << "ToolInfo_ValidateName_0100 end"; + GTEST_LOG_(INFO) << "ToolInfo_ValidateName_0800 end"; } /** @@ -974,6 +1157,7 @@ HWTEST_F(ToolInfoTest, ToolInfo_ValidateName_0200, TestSize.Level1) EXPECT_TRUE(ToolInfo::ValidateName("hms-test")); EXPECT_TRUE(ToolInfo::ValidateName("hms-abc")); EXPECT_TRUE(ToolInfo::ValidateName("hms-1234567890123456")); // 16 chars suffix + EXPECT_TRUE(ToolInfo::ValidateName("hms-12345678901234567890123456789012")); // 32 chars suffix GTEST_LOG_(INFO) << "ToolInfo_ValidateName_0200 end"; } @@ -1001,16 +1185,16 @@ HWTEST_F(ToolInfoTest, ToolInfo_ValidateName_0300, TestSize.Level1) /** * @tc.name: ToolInfo_ValidateName_0400 - * @tc.desc: Test ToolInfo ValidateName with suffix exceeding 16 chars + * @tc.desc: Test ToolInfo ValidateName with suffix exceeding 32 chars * @tc.type: FUNC */ HWTEST_F(ToolInfoTest, ToolInfo_ValidateName_0400, TestSize.Level1) { GTEST_LOG_(INFO) << "ToolInfo_ValidateName_0400 start"; - EXPECT_FALSE(ToolInfo::ValidateName("ohos-12345678901234567")); // 17 chars suffix - EXPECT_FALSE(ToolInfo::ValidateName("hms-abcdefghijklmnopq")); // 17 chars suffix - EXPECT_FALSE(ToolInfo::ValidateName("ohos-thisisaverylongname")); // long suffix + EXPECT_FALSE(ToolInfo::ValidateName("ohos-123456789012345678901234567890123")); // 33 chars suffix + EXPECT_FALSE(ToolInfo::ValidateName("hms-abcdefghijklmnopabcdefghijklmnopq")); // 33 chars suffix + EXPECT_FALSE(ToolInfo::ValidateName("ohos-thisisaverylongtoolnameexceedinglimit")); // long suffix GTEST_LOG_(INFO) << "ToolInfo_ValidateName_0400 end"; } @@ -1045,9 +1229,11 @@ HWTEST_F(ToolInfoTest, ToolInfo_ValidateName_0600, TestSize.Level1) EXPECT_TRUE(ToolInfo::ValidateName("hms-x")); // 1 char suffix EXPECT_TRUE(ToolInfo::ValidateName("ohos-1234567890abcdef")); // exactly 16 chars EXPECT_TRUE(ToolInfo::ValidateName("hms-abcdefghijklmnop")); // exactly 16 chars + EXPECT_TRUE(ToolInfo::ValidateName("ohos-12345678901234567890123456789012")); // exactly 32 chars + EXPECT_TRUE(ToolInfo::ValidateName("hms-abcdefghijklmnopabcdefghijklmnop")); // exactly 32 chars // Invalid edge cases - EXPECT_FALSE(ToolInfo::ValidateName("ohos-1234567890abcdefg")); // 17 chars + EXPECT_FALSE(ToolInfo::ValidateName("ohos-123456789012345678901234567890123")); // 33 chars EXPECT_FALSE(ToolInfo::ValidateName("ohos-")); // 0 chars suffix GTEST_LOG_(INFO) << "ToolInfo_ValidateName_0600 end"; @@ -1056,13 +1242,13 @@ HWTEST_F(ToolInfoTest, ToolInfo_ValidateName_0600, TestSize.Level1) // ==================== ValidateExecutablePath Tests ==================== /** - * @tc.name: ToolInfo_ValidateExecutablePath_0100 + * @tc.name: ToolInfo_ValidateExecutablePath_0300 * @tc.desc: Test ToolInfo ValidateExecutablePath with valid absolute paths * @tc.type: FUNC */ -HWTEST_F(ToolInfoTest, ToolInfo_ValidateExecutablePath_0100, TestSize.Level1) +HWTEST_F(ToolInfoTest, ToolInfo_ValidateExecutablePath_0300, TestSize.Level1) { - GTEST_LOG_(INFO) << "ToolInfo_ValidateExecutablePath_0100 start"; + GTEST_LOG_(INFO) << "ToolInfo_ValidateExecutablePath_0300 start"; EXPECT_TRUE(ToolInfo::ValidateExecutablePath("/bin/ls")); EXPECT_TRUE(ToolInfo::ValidateExecutablePath("/usr/bin/test")); @@ -1070,7 +1256,7 @@ HWTEST_F(ToolInfoTest, ToolInfo_ValidateExecutablePath_0100, TestSize.Level1) EXPECT_TRUE(ToolInfo::ValidateExecutablePath("/")); EXPECT_TRUE(ToolInfo::ValidateExecutablePath("/a")); - GTEST_LOG_(INFO) << "ToolInfo_ValidateExecutablePath_0100 end"; + GTEST_LOG_(INFO) << "ToolInfo_ValidateExecutablePath_0300 end"; } /** @@ -1094,18 +1280,18 @@ HWTEST_F(ToolInfoTest, ToolInfo_ValidateExecutablePath_0200, TestSize.Level1) // ==================== ValidateRequirePermissions Tests ==================== /** - * @tc.name: ToolInfo_ValidateRequirePermissions_0100 + * @tc.name: ToolInfo_ValidateRequirePermissions_0700 * @tc.desc: Test ToolInfo ValidateRequirePermissions with empty permissions * @tc.type: FUNC */ -HWTEST_F(ToolInfoTest, ToolInfo_ValidateRequirePermissions_0100, TestSize.Level1) +HWTEST_F(ToolInfoTest, ToolInfo_ValidateRequirePermissions_0700, TestSize.Level1) { - GTEST_LOG_(INFO) << "ToolInfo_ValidateRequirePermissions_0100 start"; + GTEST_LOG_(INFO) << "ToolInfo_ValidateRequirePermissions_0700 start"; std::vector permissions; EXPECT_TRUE(ToolInfo::ValidateRequirePermissions(permissions)); - GTEST_LOG_(INFO) << "ToolInfo_ValidateRequirePermissions_0100 end"; + GTEST_LOG_(INFO) << "ToolInfo_ValidateRequirePermissions_0700 end"; } /** @@ -1197,18 +1383,18 @@ HWTEST_F(ToolInfoTest, ToolInfo_ValidateRequirePermissions_0600, TestSize.Level1 // ==================== ValidateEventTypes Tests ==================== /** - * @tc.name: ToolInfo_ValidateEventTypes_0100 + * @tc.name: ToolInfo_ValidateEventTypes_0600 * @tc.desc: Test ToolInfo ValidateEventTypes with empty eventTypes * @tc.type: FUNC */ -HWTEST_F(ToolInfoTest, ToolInfo_ValidateEventTypes_0100, TestSize.Level1) +HWTEST_F(ToolInfoTest, ToolInfo_ValidateEventTypes_0600, TestSize.Level1) { - GTEST_LOG_(INFO) << "ToolInfo_ValidateEventTypes_0100 start"; + GTEST_LOG_(INFO) << "ToolInfo_ValidateEventTypes_0600 start"; std::vector eventTypes; EXPECT_TRUE(ToolInfo::ValidateEventTypes(eventTypes)); - GTEST_LOG_(INFO) << "ToolInfo_ValidateEventTypes_0100 end"; + GTEST_LOG_(INFO) << "ToolInfo_ValidateEventTypes_0600 end"; } /** @@ -1287,7 +1473,9 @@ HWTEST_F(ToolInfoTest, ToolInfo_ParseFromJson_EventTypes_0100, TestSize.Level1) "version": "1.0.0", "description": "Test tool", "executablePath": "/bin/test", - "argMapping": {"type": "flag"}, + "requirePermissions": [], + "inputSchema": {}, + "outputSchema": {}, "eventTypes": ["stdout", "stdout"] })"_json; @@ -1295,7 +1483,7 @@ HWTEST_F(ToolInfoTest, ToolInfo_ParseFromJson_EventTypes_0100, TestSize.Level1) bool result = ToolInfo::ParseFromJson(json, tool); // After removing ValidateEventTypes call, duplicate eventTypes are now allowed - EXPECT_TRUE(result); + ASSERT_TRUE(result); EXPECT_EQ(tool.eventTypes.size(), 2u); GTEST_LOG_(INFO) << "ToolInfo_ParseFromJson_EventTypes_0100 end"; @@ -1315,14 +1503,16 @@ HWTEST_F(ToolInfoTest, ToolInfo_ParseFromJson_EventTypes_0200, TestSize.Level1) "version": "1.0.0", "description": "Test tool", "executablePath": "/bin/test", - "argMapping": {"type": "flag"}, + "requirePermissions": [], + "inputSchema": {}, + "outputSchema": {}, "eventTypes": ["stdout", "stderr", "exit"] })"_json; ToolInfo tool; bool result = ToolInfo::ParseFromJson(json, tool); - EXPECT_TRUE(result); + ASSERT_TRUE(result); EXPECT_EQ(tool.eventTypes.size(), 3u); GTEST_LOG_(INFO) << "ToolInfo_ParseFromJson_EventTypes_0200 end"; @@ -1342,14 +1532,16 @@ HWTEST_F(ToolInfoTest, ToolInfo_ParseFromJson_EventTypes_0300, TestSize.Level1) "version": "1.0.0", "description": "Test tool", "executablePath": "/bin/test", - "argMapping": {"type": "flag"}, + "requirePermissions": [], + "inputSchema": {}, + "outputSchema": {}, "eventTypes": [] })"_json; ToolInfo tool; bool result = ToolInfo::ParseFromJson(json, tool); - EXPECT_TRUE(result); + ASSERT_TRUE(result); EXPECT_TRUE(tool.eventTypes.empty()); GTEST_LOG_(INFO) << "ToolInfo_ParseFromJson_EventTypes_0300 end"; @@ -1369,14 +1561,16 @@ HWTEST_F(ToolInfoTest, ToolInfo_ParseFromJson_EventTypes_0400, TestSize.Level1) "version": "1.0.0", "description": "Test tool", "executablePath": "/bin/test", - "argMapping": {"type": "flag"}, + "requirePermissions": [], + "inputSchema": {}, + "outputSchema": {}, "eventTypes": ["", "stdout", ""] })"_json; ToolInfo tool; bool result = ToolInfo::ParseFromJson(json, tool); - EXPECT_TRUE(result); + ASSERT_TRUE(result); EXPECT_EQ(tool.eventTypes.size(), 1u); EXPECT_EQ(tool.eventTypes[0], "stdout"); @@ -1399,14 +1593,16 @@ HWTEST_F(ToolInfoTest, ToolInfo_ParseFromJson_RequirePermissions_0100, TestSize. "version": "1.0.0", "description": "Test tool", "executablePath": "/bin/test", - "requirePermissions": ["ohos.permission.INTERNET", "ohos.permission.INTERNET"] + "requirePermissions": ["ohos.permission.INTERNET", "ohos.permission.INTERNET"], + "inputSchema": {}, + "outputSchema": {} })"_json; ToolInfo tool; bool result = ToolInfo::ParseFromJson(json, tool); // After removing ValidateRequirePermissions call, duplicate permissions are now allowed - EXPECT_TRUE(result); + ASSERT_TRUE(result); EXPECT_EQ(tool.requirePermissions.size(), 2u); GTEST_LOG_(INFO) << "ToolInfo_ParseFromJson_RequirePermissions_0100 end"; @@ -1426,13 +1622,15 @@ HWTEST_F(ToolInfoTest, ToolInfo_ParseFromJson_RequirePermissions_0200, TestSize. "version": "1.0.0", "description": "Test tool", "executablePath": "/bin/test", - "requirePermissions": ["ohos.permission.INTERNET", "ohos.permission.CAMERA"] + "requirePermissions": ["ohos.permission.INTERNET", "ohos.permission.CAMERA"], + "inputSchema": {}, + "outputSchema": {} })"_json; ToolInfo tool; bool result = ToolInfo::ParseFromJson(json, tool); - EXPECT_TRUE(result); + ASSERT_TRUE(result); EXPECT_EQ(tool.requirePermissions.size(), 2u); GTEST_LOG_(INFO) << "ToolInfo_ParseFromJson_RequirePermissions_0200 end"; @@ -1440,7 +1638,7 @@ HWTEST_F(ToolInfoTest, ToolInfo_ParseFromJson_RequirePermissions_0200, TestSize. /** * @tc.name: ToolInfo_ParseFromJson_RequirePermissions_0300 - * @tc.desc: Test ToolInfo ParseFromJson with empty requirePermissions + * @tc.desc: Test ToolInfo ParseFromJson with empty requirePermissions (valid) * @tc.type: FUNC */ HWTEST_F(ToolInfoTest, ToolInfo_ParseFromJson_RequirePermissions_0300, TestSize.Level1) @@ -1452,7 +1650,9 @@ HWTEST_F(ToolInfoTest, ToolInfo_ParseFromJson_RequirePermissions_0300, TestSize. "version": "1.0.0", "description": "Test tool", "executablePath": "/bin/test", - "requirePermissions": [] + "requirePermissions": [], + "inputSchema": {"type": "object"}, + "outputSchema": {"type": "string"} })"_json; ToolInfo tool; @@ -1478,29 +1678,29 @@ HWTEST_F(ToolInfoTest, ToolInfo_ParseFromJson_RequirePermissions_0400, TestSize. "version": "1.0.0", "description": "Test tool", "executablePath": "/bin/test", - "requirePermissions": ["", "ohos.permission.INTERNET", ""] + "requirePermissions": ["", "ohos.permission.INTERNET", ""], + "inputSchema": {}, + "outputSchema": {} })"_json; ToolInfo tool; bool result = ToolInfo::ParseFromJson(json, tool); - EXPECT_TRUE(result); + ASSERT_TRUE(result); EXPECT_EQ(tool.requirePermissions.size(), 1u); EXPECT_EQ(tool.requirePermissions[0], "ohos.permission.INTERNET"); GTEST_LOG_(INFO) << "ToolInfo_ParseFromJson_RequirePermissions_0400 end"; } -// ==================== ParseFromJson ArgMapping Validation Tests ==================== - /** - * @tc.name: ToolInfo_ParseFromJson_ArgMapping_0100 - * @tc.desc: Test ToolInfo ParseFromJson without argMapping (should fail) + * @tc.name: ToolInfo_ParseFromJson_RequirePermissions_0500 + * @tc.desc: Test ToolInfo ParseFromJson without requirePermissions field (should fail - required field) * @tc.type: FUNC */ -HWTEST_F(ToolInfoTest, ToolInfo_ParseFromJson_ArgMapping_0100, TestSize.Level1) +HWTEST_F(ToolInfoTest, ToolInfo_ParseFromJson_RequirePermissions_0500, TestSize.Level1) { - GTEST_LOG_(INFO) << "ToolInfo_ParseFromJson_ArgMapping_0100 start"; + GTEST_LOG_(INFO) << "ToolInfo_ParseFromJson_RequirePermissions_0500 start"; nlohmann::json json = R"({ "name": "ohos-test", @@ -1514,59 +1714,7 @@ HWTEST_F(ToolInfoTest, ToolInfo_ParseFromJson_ArgMapping_0100, TestSize.Level1) EXPECT_FALSE(result); - GTEST_LOG_(INFO) << "ToolInfo_ParseFromJson_ArgMapping_0100 end"; -} - -/** - * @tc.name: ToolInfo_ParseFromJson_ArgMapping_0200 - * @tc.desc: Test ToolInfo ParseFromJson with argMapping not object (should fail) - * @tc.type: FUNC - */ -HWTEST_F(ToolInfoTest, ToolInfo_ParseFromJson_ArgMapping_0200, TestSize.Level1) -{ - GTEST_LOG_(INFO) << "ToolInfo_ParseFromJson_ArgMapping_0200 start"; - - nlohmann::json json = R"({ - "name": "ohos-test", - "version": "1.0.0", - "description": "Test tool", - "executablePath": "/bin/test", - "argMapping": "not an object" - })"_json; - - ToolInfo tool; - bool result = ToolInfo::ParseFromJson(json, tool); - - EXPECT_FALSE(result); - - GTEST_LOG_(INFO) << "ToolInfo_ParseFromJson_ArgMapping_0200 end"; -} - -/** - * @tc.name: ToolInfo_ParseFromJson_ArgMapping_0300 - * @tc.desc: Test ToolInfo ParseFromJson with valid argMapping - * @tc.type: FUNC - */ -HWTEST_F(ToolInfoTest, ToolInfo_ParseFromJson_ArgMapping_0300, TestSize.Level1) -{ - GTEST_LOG_(INFO) << "ToolInfo_ParseFromJson_ArgMapping_0300 start"; - - nlohmann::json json = R"({ - "name": "ohos-test", - "version": "1.0.0", - "description": "Test tool", - "executablePath": "/bin/test", - "argMapping": {"type": "flag"} - })"_json; - - ToolInfo tool; - bool result = ToolInfo::ParseFromJson(json, tool); - - EXPECT_TRUE(result); - ASSERT_NE(tool.argMapping, nullptr); - EXPECT_EQ(tool.argMapping->type, ArgMappingType::FLAG); - - GTEST_LOG_(INFO) << "ToolInfo_ParseFromJson_ArgMapping_0300 end"; + GTEST_LOG_(INFO) << "ToolInfo_ParseFromJson_RequirePermissions_0500 end"; } // ==================== ParseFromJson EventSchemas Validation Tests ==================== @@ -1585,7 +1733,9 @@ HWTEST_F(ToolInfoTest, ToolInfo_ParseFromJson_EventSchemas_0100, TestSize.Level1 "version": "1.0.0", "description": "Test tool", "executablePath": "/bin/test", - "argMapping": {"type": "flag"} + "requirePermissions": ["ohos.permission.INTERNET"], + "inputSchema": {"type": "object"}, + "outputSchema": {"type": "string"} })"_json; ToolInfo tool; @@ -1611,7 +1761,9 @@ HWTEST_F(ToolInfoTest, ToolInfo_ParseFromJson_EventSchemas_0200, TestSize.Level1 "version": "1.0.0", "description": "Test tool", "executablePath": "/bin/test", - "argMapping": {"type": "flag"}, + "requirePermissions": ["ohos.permission.INTERNET"], + "inputSchema": {"type": "object"}, + "outputSchema": {"type": "string"}, "eventSchemas": {"stdout": {"type": "string"}} })"_json; @@ -1638,7 +1790,9 @@ HWTEST_F(ToolInfoTest, ToolInfo_ParseFromJson_EventSchemas_0300, TestSize.Level1 "version": "1.0.0", "description": "Test tool", "executablePath": "/bin/test", - "argMapping": {"type": "flag"}, + "requirePermissions": ["ohos.permission.INTERNET"], + "inputSchema": {"type": "object"}, + "outputSchema": {"type": "string"}, "eventSchemas": "not an object" })"_json; @@ -1664,7 +1818,9 @@ HWTEST_F(ToolInfoTest, ToolInfo_ParseFromJson_EventSchemas_0400, TestSize.Level1 "version": "1.0.0", "description": "Test tool", "executablePath": "/bin/test", - "argMapping": {"type": "flag"}, + "requirePermissions": ["ohos.permission.INTERNET"], + "inputSchema": {"type": "object"}, + "outputSchema": {"type": "string"}, "eventSchemas": ["a", "b"] })"_json; @@ -1692,8 +1848,9 @@ HWTEST_F(ToolInfoTest, ToolInfo_ParseFromJson_Schema_0100, TestSize.Level1) "version": "1.0.0", "description": "Test tool", "executablePath": "/bin/test", - "argMapping": {"type": "flag"}, - "inputSchema": "not an object" + "requirePermissions": ["ohos.permission.INTERNET"], + "inputSchema": "not an object", + "outputSchema": {"type": "string"} })"_json; ToolInfo tool; @@ -1718,8 +1875,9 @@ HWTEST_F(ToolInfoTest, ToolInfo_ParseFromJson_Schema_0200, TestSize.Level1) "version": "1.0.0", "description": "Test tool", "executablePath": "/bin/test", - "argMapping": {"type": "flag"}, - "inputSchema": ["a", "b"] + "requirePermissions": ["ohos.permission.INTERNET"], + "inputSchema": ["a", "b"], + "outputSchema": {"type": "string"} })"_json; ToolInfo tool; @@ -1744,8 +1902,9 @@ HWTEST_F(ToolInfoTest, ToolInfo_ParseFromJson_Schema_0300, TestSize.Level1) "version": "1.0.0", "description": "Test tool", "executablePath": "/bin/test", - "argMapping": {"type": "flag"}, - "inputSchema": 123 + "requirePermissions": ["ohos.permission.INTERNET"], + "inputSchema": 123, + "outputSchema": {"type": "string"} })"_json; ToolInfo tool; @@ -1770,8 +1929,9 @@ HWTEST_F(ToolInfoTest, ToolInfo_ParseFromJson_Schema_0400, TestSize.Level1) "version": "1.0.0", "description": "Test tool", "executablePath": "/bin/test", - "argMapping": {"type": "flag"}, - "inputSchema": {"type": "object"} + "requirePermissions": ["ohos.permission.INTERNET"], + "inputSchema": {"type": "object"}, + "outputSchema": {"type": "string"} })"_json; ToolInfo tool; @@ -1785,7 +1945,7 @@ HWTEST_F(ToolInfoTest, ToolInfo_ParseFromJson_Schema_0400, TestSize.Level1) /** * @tc.name: ToolInfo_ParseFromJson_Schema_0500 - * @tc.desc: Test ToolInfo ParseFromJson without inputSchema (valid) + * @tc.desc: Test ToolInfo ParseFromJson without inputSchema (should fail - required field) * @tc.type: FUNC */ HWTEST_F(ToolInfoTest, ToolInfo_ParseFromJson_Schema_0500, TestSize.Level1) @@ -1797,14 +1957,14 @@ HWTEST_F(ToolInfoTest, ToolInfo_ParseFromJson_Schema_0500, TestSize.Level1) "version": "1.0.0", "description": "Test tool", "executablePath": "/bin/test", - "argMapping": {"type": "flag"} + "requirePermissions": ["ohos.permission.INTERNET"], + "outputSchema": {"type": "string"} })"_json; ToolInfo tool; bool result = ToolInfo::ParseFromJson(json, tool); - EXPECT_TRUE(result); - EXPECT_TRUE(tool.inputSchema.empty()); + EXPECT_FALSE(result); GTEST_LOG_(INFO) << "ToolInfo_ParseFromJson_Schema_0500 end"; } @@ -1823,7 +1983,8 @@ HWTEST_F(ToolInfoTest, ToolInfo_ParseFromJson_Schema_0600, TestSize.Level1) "version": "1.0.0", "description": "Test tool", "executablePath": "/bin/test", - "argMapping": {"type": "flag"}, + "requirePermissions": ["ohos.permission.INTERNET"], + "inputSchema": {"type": "object"}, "outputSchema": "not an object" })"_json; @@ -1849,7 +2010,8 @@ HWTEST_F(ToolInfoTest, ToolInfo_ParseFromJson_Schema_0700, TestSize.Level1) "version": "1.0.0", "description": "Test tool", "executablePath": "/bin/test", - "argMapping": {"type": "flag"}, + "requirePermissions": ["ohos.permission.INTERNET"], + "inputSchema": {"type": "object"}, "outputSchema": ["a", "b"] })"_json; @@ -1875,7 +2037,8 @@ HWTEST_F(ToolInfoTest, ToolInfo_ParseFromJson_Schema_0800, TestSize.Level1) "version": "1.0.0", "description": "Test tool", "executablePath": "/bin/test", - "argMapping": {"type": "flag"}, + "requirePermissions": ["ohos.permission.INTERNET"], + "inputSchema": {"type": "object"}, "outputSchema": {"type": "string"} })"_json; @@ -1890,7 +2053,7 @@ HWTEST_F(ToolInfoTest, ToolInfo_ParseFromJson_Schema_0800, TestSize.Level1) /** * @tc.name: ToolInfo_ParseFromJson_Schema_0900 - * @tc.desc: Test ToolInfo ParseFromJson without outputSchema (valid) + * @tc.desc: Test ToolInfo ParseFromJson without outputSchema (should fail - required field) * @tc.type: FUNC */ HWTEST_F(ToolInfoTest, ToolInfo_ParseFromJson_Schema_0900, TestSize.Level1) @@ -1902,14 +2065,14 @@ HWTEST_F(ToolInfoTest, ToolInfo_ParseFromJson_Schema_0900, TestSize.Level1) "version": "1.0.0", "description": "Test tool", "executablePath": "/bin/test", - "argMapping": {"type": "flag"} + "requirePermissions": ["ohos.permission.INTERNET"], + "inputSchema": {"type": "object"} })"_json; ToolInfo tool; bool result = ToolInfo::ParseFromJson(json, tool); - EXPECT_TRUE(result); - EXPECT_TRUE(tool.outputSchema.empty()); + EXPECT_FALSE(result); GTEST_LOG_(INFO) << "ToolInfo_ParseFromJson_Schema_0900 end"; } @@ -1928,7 +2091,7 @@ HWTEST_F(ToolInfoTest, ToolInfo_ParseFromJson_Schema_1000, TestSize.Level1) "version": "1.0.0", "description": "Test tool", "executablePath": "/bin/test", - "argMapping": {"type": "flag"}, + "requirePermissions": ["ohos.permission.INTERNET"], "inputSchema": {"type": "object"}, "outputSchema": {"type": "array"} })"_json; @@ -1943,193 +2106,6 @@ HWTEST_F(ToolInfoTest, ToolInfo_ParseFromJson_Schema_1000, TestSize.Level1) GTEST_LOG_(INFO) << "ToolInfo_ParseFromJson_Schema_1000 end"; } -// ==================== ParseFromJson Timeout Validation Tests ==================== - -/** - * @tc.name: ToolInfo_ParseFromJson_Timeout_0100 - * @tc.desc: Test ToolInfo ParseFromJson with timeout not integer - * @tc.type: FUNC - */ -HWTEST_F(ToolInfoTest, ToolInfo_ParseFromJson_Timeout_0100, TestSize.Level1) -{ - GTEST_LOG_(INFO) << "ToolInfo_ParseFromJson_Timeout_0100 start"; - - nlohmann::json json = R"({ - "name": "ohos-test", - "version": "1.0.0", - "description": "Test tool", - "executablePath": "/bin/test", - "argMapping": {"type": "flag"}, - "timeout": "30" - })"_json; - - ToolInfo tool; - bool result = ToolInfo::ParseFromJson(json, tool); - - EXPECT_FALSE(result); - - GTEST_LOG_(INFO) << "ToolInfo_ParseFromJson_Timeout_0100 end"; -} - -/** - * @tc.name: ToolInfo_ParseFromJson_Timeout_0200 - * @tc.desc: Test ToolInfo ParseFromJson with timeout <= 0 - * @tc.type: FUNC - */ -HWTEST_F(ToolInfoTest, ToolInfo_ParseFromJson_Timeout_0200, TestSize.Level1) -{ - GTEST_LOG_(INFO) << "ToolInfo_ParseFromJson_Timeout_0200 start"; - - nlohmann::json json = R"({ - "name": "ohos-test", - "version": "1.0.0", - "description": "Test tool", - "executablePath": "/bin/test", - "argMapping": {"type": "flag"}, - "timeout": 0 - })"_json; - - ToolInfo tool; - bool result = ToolInfo::ParseFromJson(json, tool); - - EXPECT_FALSE(result); - - GTEST_LOG_(INFO) << "ToolInfo_ParseFromJson_Timeout_0200 end"; -} - -/** - * @tc.name: ToolInfo_ParseFromJson_Timeout_0300 - * @tc.desc: Test ToolInfo ParseFromJson with timeout > 1800 - * @tc.type: FUNC - */ -HWTEST_F(ToolInfoTest, ToolInfo_ParseFromJson_Timeout_0300, TestSize.Level1) -{ - GTEST_LOG_(INFO) << "ToolInfo_ParseFromJson_Timeout_0300 start"; - - nlohmann::json json = R"({ - "name": "ohos-test", - "version": "1.0.0", - "description": "Test tool", - "executablePath": "/bin/test", - "argMapping": {"type": "flag"}, - "timeout": 1801 - })"_json; - - ToolInfo tool; - bool result = ToolInfo::ParseFromJson(json, tool); - - EXPECT_FALSE(result); - - GTEST_LOG_(INFO) << "ToolInfo_ParseFromJson_Timeout_0300 end"; -} - -/** - * @tc.name: ToolInfo_ParseFromJson_Timeout_0400 - * @tc.desc: Test ToolInfo ParseFromJson with valid timeout - * @tc.type: FUNC - */ -HWTEST_F(ToolInfoTest, ToolInfo_ParseFromJson_Timeout_0400, TestSize.Level1) -{ - GTEST_LOG_(INFO) << "ToolInfo_ParseFromJson_Timeout_0400 start"; - - nlohmann::json json = R"({ - "name": "ohos-test", - "version": "1.0.0", - "description": "Test tool", - "executablePath": "/bin/test", - "argMapping": {"type": "flag"}, - "timeout": 60 - })"_json; - - ToolInfo tool; - bool result = ToolInfo::ParseFromJson(json, tool); - - EXPECT_TRUE(result); - EXPECT_EQ(tool.timeout, 60); - - GTEST_LOG_(INFO) << "ToolInfo_ParseFromJson_Timeout_0400 end"; -} - -/** - * @tc.name: ToolInfo_ParseFromJson_Timeout_0500 - * @tc.desc: Test ToolInfo ParseFromJson with timeout = 1800 (boundary) - * @tc.type: FUNC - */ -HWTEST_F(ToolInfoTest, ToolInfo_ParseFromJson_Timeout_0500, TestSize.Level1) -{ - GTEST_LOG_(INFO) << "ToolInfo_ParseFromJson_Timeout_0500 start"; - - nlohmann::json json = R"({ - "name": "ohos-test", - "version": "1.0.0", - "description": "Test tool", - "executablePath": "/bin/test", - "argMapping": {"type": "flag"}, - "timeout": 1800 - })"_json; - - ToolInfo tool; - bool result = ToolInfo::ParseFromJson(json, tool); - - EXPECT_TRUE(result); - EXPECT_EQ(tool.timeout, 1800); - - GTEST_LOG_(INFO) << "ToolInfo_ParseFromJson_Timeout_0500 end"; -} - -/** - * @tc.name: ToolInfo_ParseFromJson_Timeout_0600 - * @tc.desc: Test ToolInfo ParseFromJson with timeout = 1 (boundary) - * @tc.type: FUNC - */ -HWTEST_F(ToolInfoTest, ToolInfo_ParseFromJson_Timeout_0600, TestSize.Level1) -{ - GTEST_LOG_(INFO) << "ToolInfo_ParseFromJson_Timeout_0600 start"; - - nlohmann::json json = R"({ - "name": "ohos-test", - "version": "1.0.0", - "description": "Test tool", - "executablePath": "/bin/test", - "argMapping": {"type": "flag"}, - "timeout": 1 - })"_json; - - ToolInfo tool; - bool result = ToolInfo::ParseFromJson(json, tool); - - EXPECT_TRUE(result); - EXPECT_EQ(tool.timeout, 1); - - GTEST_LOG_(INFO) << "ToolInfo_ParseFromJson_Timeout_0600 end"; -} - -/** - * @tc.name: ToolInfo_ParseFromJson_Timeout_0700 - * @tc.desc: Test ToolInfo ParseFromJson without timeout (use default) - * @tc.type: FUNC - */ -HWTEST_F(ToolInfoTest, ToolInfo_ParseFromJson_Timeout_0700, TestSize.Level1) -{ - GTEST_LOG_(INFO) << "ToolInfo_ParseFromJson_Timeout_0700 start"; - - nlohmann::json json = R"({ - "name": "ohos-test", - "version": "1.0.0", - "description": "Test tool", - "executablePath": "/bin/test", - "argMapping": {"type": "flag"} - })"_json; - - ToolInfo tool; - bool result = ToolInfo::ParseFromJson(json, tool); - - EXPECT_TRUE(result); - EXPECT_EQ(tool.timeout, 1800); // default value - - GTEST_LOG_(INFO) << "ToolInfo_ParseFromJson_Timeout_0700 end"; -} - // ==================== ParseFromJson Name Validation Tests ==================== /** @@ -2157,7 +2133,7 @@ HWTEST_F(ToolInfoTest, ToolInfo_ParseFromJson_NameValidation_0100, TestSize.Leve /** * @tc.name: ToolInfo_ParseFromJson_NameValidation_0200 - * @tc.desc: Test ToolInfo ParseFromJson with invalid name (suffix too long) + * @tc.desc: Test ToolInfo ParseFromJson with invalid name (suffix too long - exceeds 32 chars) * @tc.type: FUNC */ HWTEST_F(ToolInfoTest, ToolInfo_ParseFromJson_NameValidation_0200, TestSize.Level1) @@ -2165,7 +2141,7 @@ HWTEST_F(ToolInfoTest, ToolInfo_ParseFromJson_NameValidation_0200, TestSize.Leve GTEST_LOG_(INFO) << "ToolInfo_ParseFromJson_NameValidation_0200 start"; nlohmann::json json = R"({ - "name": "ohos-thisisaverylongtoolname", + "name": "ohos-thisisaverylongtoolnameexceedinglimit", "version": "1.0.0" })"_json; @@ -2474,7 +2450,9 @@ HWTEST_F(ToolInfoTest, ToolInfo_ParseFromJson_RequiredFields_0900, TestSize.Leve "version": "1.0.0", "description": "A valid tool", "executablePath": "/bin/test", - "argMapping": {"type": "flag"} + "requirePermissions": ["ohos.permission.INTERNET"], + "inputSchema": {"type": "object"}, + "outputSchema": {"type": "string"} })"_json; ToolInfo tool; @@ -2505,11 +2483,9 @@ HWTEST_F(ToolInfoTest, ToolInfo_Validate_0100, TestSize.Level1) tool.version = "1.0.0"; tool.description = "A valid tool"; tool.executablePath = "/bin/test"; + tool.requirePermissions = {"ohos.permission.INTERNET"}; tool.inputSchema = R"({"type": "object"})"; tool.outputSchema = R"({"type": "string"})"; - tool.argMapping = std::make_shared(); - tool.argMapping->type = ArgMappingType::FLAG; - tool.timeout = 30; EXPECT_TRUE(ToolInfo::Validate(tool)); @@ -2530,10 +2506,9 @@ HWTEST_F(ToolInfoTest, ToolInfo_Validate_0200, TestSize.Level1) tool.version = "1.0.0"; tool.description = "Test"; tool.executablePath = "/bin/test"; + tool.requirePermissions = {"ohos.permission.INTERNET"}; tool.inputSchema = "{}"; tool.outputSchema = "{}"; - tool.argMapping = std::make_shared(); - tool.timeout = 30; EXPECT_FALSE(ToolInfo::Validate(tool)); @@ -2554,10 +2529,9 @@ HWTEST_F(ToolInfoTest, ToolInfo_Validate_0300, TestSize.Level1) tool.version = ""; tool.description = "Test"; tool.executablePath = "/bin/test"; + tool.requirePermissions = {"ohos.permission.INTERNET"}; tool.inputSchema = "{}"; tool.outputSchema = "{}"; - tool.argMapping = std::make_shared(); - tool.timeout = 30; EXPECT_FALSE(ToolInfo::Validate(tool)); @@ -2578,10 +2552,9 @@ HWTEST_F(ToolInfoTest, ToolInfo_Validate_0400, TestSize.Level1) tool.version = "1.0.0"; tool.description = ""; tool.executablePath = "/bin/test"; + tool.requirePermissions = {"ohos.permission.INTERNET"}; tool.inputSchema = "{}"; tool.outputSchema = "{}"; - tool.argMapping = std::make_shared(); - tool.timeout = 30; EXPECT_FALSE(ToolInfo::Validate(tool)); @@ -2602,16 +2575,38 @@ HWTEST_F(ToolInfoTest, ToolInfo_Validate_0500, TestSize.Level1) tool.version = "1.0.0"; tool.description = "Test"; tool.executablePath = "bin/test"; + tool.requirePermissions = {"ohos.permission.INTERNET"}; tool.inputSchema = "{}"; tool.outputSchema = "{}"; - tool.argMapping = std::make_shared(); - tool.timeout = 30; EXPECT_FALSE(ToolInfo::Validate(tool)); GTEST_LOG_(INFO) << "ToolInfo_Validate_0500 end"; } +/** + * @tc.name: ToolInfo_Validate_0550 + * @tc.desc: Test ToolInfo::Validate with empty requirePermissions (valid) + * @tc.type: FUNC + */ +HWTEST_F(ToolInfoTest, ToolInfo_Validate_0550, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "ToolInfo_Validate_0550 start"; + + ToolInfo tool; + tool.name = "ohos-test"; + tool.version = "1.0.0"; + tool.description = "Test"; + tool.executablePath = "/bin/test"; + tool.requirePermissions = {}; + tool.inputSchema = "{}"; + tool.outputSchema = "{}"; + + EXPECT_TRUE(ToolInfo::Validate(tool)); + + GTEST_LOG_(INFO) << "ToolInfo_Validate_0550 end"; +} + /** * @tc.name: ToolInfo_Validate_0600 * @tc.desc: Test ToolInfo::Validate with duplicate requirePermissions (duplicates are allowed) @@ -2629,8 +2624,6 @@ HWTEST_F(ToolInfoTest, ToolInfo_Validate_0600, TestSize.Level1) tool.requirePermissions = {"ohos.permission.INTERNET", "ohos.permission.INTERNET"}; tool.inputSchema = "{}"; tool.outputSchema = "{}"; - tool.argMapping = std::make_shared(); - tool.timeout = 30; EXPECT_TRUE(ToolInfo::Validate(tool)); // duplicate permissions are now allowed @@ -2654,8 +2647,6 @@ HWTEST_F(ToolInfoTest, ToolInfo_Validate_0700, TestSize.Level1) tool.requirePermissions = {"ohos.permission.INTERNET", "ohos.permission.CAMERA"}; tool.inputSchema = "{}"; tool.outputSchema = "{}"; - tool.argMapping = std::make_shared(); - tool.timeout = 30; EXPECT_TRUE(ToolInfo::Validate(tool)); @@ -2664,7 +2655,7 @@ HWTEST_F(ToolInfoTest, ToolInfo_Validate_0700, TestSize.Level1) /** * @tc.name: ToolInfo_Validate_0800 - * @tc.desc: Test ToolInfo::Validate with empty inputSchema (valid) + * @tc.desc: Test ToolInfo::Validate with empty inputSchema (should fail - required field) * @tc.type: FUNC */ HWTEST_F(ToolInfoTest, ToolInfo_Validate_0800, TestSize.Level1) @@ -2676,12 +2667,11 @@ HWTEST_F(ToolInfoTest, ToolInfo_Validate_0800, TestSize.Level1) tool.version = "1.0.0"; tool.description = "Test"; tool.executablePath = "/bin/test"; + tool.requirePermissions = {"ohos.permission.INTERNET"}; tool.inputSchema = ""; tool.outputSchema = "{}"; - tool.argMapping = std::make_shared(); - tool.timeout = 30; - EXPECT_TRUE(ToolInfo::Validate(tool)); + EXPECT_FALSE(ToolInfo::Validate(tool)); GTEST_LOG_(INFO) << "ToolInfo_Validate_0800 end"; } @@ -2700,10 +2690,9 @@ HWTEST_F(ToolInfoTest, ToolInfo_Validate_0900, TestSize.Level1) tool.version = "1.0.0"; tool.description = "Test"; tool.executablePath = "/bin/test"; + tool.requirePermissions = {"ohos.permission.INTERNET"}; tool.inputSchema = "not valid json"; tool.outputSchema = "{}"; - tool.argMapping = std::make_shared(); - tool.timeout = 30; EXPECT_FALSE(ToolInfo::Validate(tool)); @@ -2712,7 +2701,7 @@ HWTEST_F(ToolInfoTest, ToolInfo_Validate_0900, TestSize.Level1) /** * @tc.name: ToolInfo_Validate_1000 - * @tc.desc: Test ToolInfo::Validate with empty outputSchema (valid) + * @tc.desc: Test ToolInfo::Validate with empty outputSchema (should fail - required field) * @tc.type: FUNC */ HWTEST_F(ToolInfoTest, ToolInfo_Validate_1000, TestSize.Level1) @@ -2724,12 +2713,11 @@ HWTEST_F(ToolInfoTest, ToolInfo_Validate_1000, TestSize.Level1) tool.version = "1.0.0"; tool.description = "Test"; tool.executablePath = "/bin/test"; + tool.requirePermissions = {"ohos.permission.INTERNET"}; tool.inputSchema = "{}"; tool.outputSchema = ""; - tool.argMapping = std::make_shared(); - tool.timeout = 30; - EXPECT_TRUE(ToolInfo::Validate(tool)); + EXPECT_FALSE(ToolInfo::Validate(tool)); GTEST_LOG_(INFO) << "ToolInfo_Validate_1000 end"; } @@ -2748,140 +2736,15 @@ HWTEST_F(ToolInfoTest, ToolInfo_Validate_1100, TestSize.Level1) tool.version = "1.0.0"; tool.description = "Test"; tool.executablePath = "/bin/test"; + tool.requirePermissions = {"ohos.permission.INTERNET"}; tool.inputSchema = "{}"; tool.outputSchema = "{invalid}"; - tool.argMapping = std::make_shared(); - tool.timeout = 30; EXPECT_FALSE(ToolInfo::Validate(tool)); GTEST_LOG_(INFO) << "ToolInfo_Validate_1100 end"; } -/** - * @tc.name: ToolInfo_Validate_1200 - * @tc.desc: Test ToolInfo::Validate with null argMapping - * @tc.type: FUNC - */ -HWTEST_F(ToolInfoTest, ToolInfo_Validate_1200, TestSize.Level1) -{ - GTEST_LOG_(INFO) << "ToolInfo_Validate_1200 start"; - - ToolInfo tool; - tool.name = "ohos-test"; - tool.version = "1.0.0"; - tool.description = "Test"; - tool.executablePath = "/bin/test"; - tool.inputSchema = "{}"; - tool.outputSchema = "{}"; - tool.argMapping = nullptr; - tool.timeout = 30; - - EXPECT_FALSE(ToolInfo::Validate(tool)); - - GTEST_LOG_(INFO) << "ToolInfo_Validate_1200 end"; -} - -/** - * @tc.name: ToolInfo_Validate_1300 - * @tc.desc: Test ToolInfo::Validate with invalid argMapping - * @tc.type: FUNC - */ -HWTEST_F(ToolInfoTest, ToolInfo_Validate_1300, TestSize.Level1) -{ - GTEST_LOG_(INFO) << "ToolInfo_Validate_1300 start"; - - ToolInfo tool; - tool.name = "ohos-test"; - tool.version = "1.0.0"; - tool.description = "Test"; - tool.executablePath = "/bin/test"; - tool.inputSchema = "{}"; - tool.outputSchema = "{}"; - tool.argMapping = std::make_shared(); - tool.argMapping->type = static_cast(-1); - tool.timeout = 30; - - EXPECT_FALSE(ToolInfo::Validate(tool)); - - GTEST_LOG_(INFO) << "ToolInfo_Validate_1300 end"; -} - -/** - * @tc.name: ToolInfo_Validate_1400 - * @tc.desc: Test ToolInfo::Validate with timeout <= 0 - * @tc.type: FUNC - */ -HWTEST_F(ToolInfoTest, ToolInfo_Validate_1400, TestSize.Level1) -{ - GTEST_LOG_(INFO) << "ToolInfo_Validate_1400 start"; - - ToolInfo tool; - tool.name = "ohos-test"; - tool.version = "1.0.0"; - tool.description = "Test"; - tool.executablePath = "/bin/test"; - tool.inputSchema = "{}"; - tool.outputSchema = "{}"; - tool.argMapping = std::make_shared(); - tool.timeout = 0; - - EXPECT_FALSE(ToolInfo::Validate(tool)); - - GTEST_LOG_(INFO) << "ToolInfo_Validate_1400 end"; -} - -/** - * @tc.name: ToolInfo_Validate_1500 - * @tc.desc: Test ToolInfo::Validate with timeout > 1800 - * @tc.type: FUNC - */ -HWTEST_F(ToolInfoTest, ToolInfo_Validate_1500, TestSize.Level1) -{ - GTEST_LOG_(INFO) << "ToolInfo_Validate_1500 start"; - - ToolInfo tool; - tool.name = "ohos-test"; - tool.version = "1.0.0"; - tool.description = "Test"; - tool.executablePath = "/bin/test"; - tool.inputSchema = "{}"; - tool.outputSchema = "{}"; - tool.argMapping = std::make_shared(); - tool.timeout = 1801; - - EXPECT_FALSE(ToolInfo::Validate(tool)); - - GTEST_LOG_(INFO) << "ToolInfo_Validate_1500 end"; -} - -/** - * @tc.name: ToolInfo_Validate_1600 - * @tc.desc: Test ToolInfo::Validate with valid timeout range - * @tc.type: FUNC - */ -HWTEST_F(ToolInfoTest, ToolInfo_Validate_1600, TestSize.Level1) -{ - GTEST_LOG_(INFO) << "ToolInfo_Validate_1600 start"; - - ToolInfo tool; - tool.name = "ohos-test"; - tool.version = "1.0.0"; - tool.description = "Test"; - tool.executablePath = "/bin/test"; - tool.inputSchema = "{}"; - tool.outputSchema = "{}"; - tool.argMapping = std::make_shared(); - tool.timeout = 1; - - EXPECT_TRUE(ToolInfo::Validate(tool)); - - tool.timeout = 1800; - EXPECT_TRUE(ToolInfo::Validate(tool)); - - GTEST_LOG_(INFO) << "ToolInfo_Validate_1600 end"; -} - /** * @tc.name: ToolInfo_Validate_1700 * @tc.desc: Test ToolInfo::Validate with duplicate eventTypes (duplicates are allowed) @@ -2896,10 +2759,9 @@ HWTEST_F(ToolInfoTest, ToolInfo_Validate_1700, TestSize.Level1) tool.version = "1.0.0"; tool.description = "Test"; tool.executablePath = "/bin/test"; + tool.requirePermissions = {"ohos.permission.INTERNET"}; tool.inputSchema = "{}"; tool.outputSchema = "{}"; - tool.argMapping = std::make_shared(); - tool.timeout = 30; tool.eventTypes = {"stdout", "stdout"}; EXPECT_TRUE(ToolInfo::Validate(tool)); // duplicate eventTypes are now allowed @@ -2921,10 +2783,9 @@ HWTEST_F(ToolInfoTest, ToolInfo_Validate_1800, TestSize.Level1) tool.version = "1.0.0"; tool.description = "Test"; tool.executablePath = "/bin/test"; + tool.requirePermissions = {"ohos.permission.INTERNET"}; tool.inputSchema = "{}"; tool.outputSchema = "{}"; - tool.argMapping = std::make_shared(); - tool.timeout = 30; tool.eventTypes = {"stdout", "stderr", "exit"}; EXPECT_TRUE(ToolInfo::Validate(tool)); @@ -2946,10 +2807,9 @@ HWTEST_F(ToolInfoTest, ToolInfo_Validate_1900, TestSize.Level1) tool.version = "1.0.0"; tool.description = "Test"; tool.executablePath = "/bin/test"; + tool.requirePermissions = {"ohos.permission.INTERNET"}; tool.inputSchema = "{}"; tool.outputSchema = "{}"; - tool.argMapping = std::make_shared(); - tool.timeout = 30; tool.eventSchemas = "invalid json"; EXPECT_FALSE(ToolInfo::Validate(tool)); @@ -2971,10 +2831,9 @@ HWTEST_F(ToolInfoTest, ToolInfo_Validate_2000, TestSize.Level1) tool.version = "1.0.0"; tool.description = "Test"; tool.executablePath = "/bin/test"; + tool.requirePermissions = {"ohos.permission.INTERNET"}; tool.inputSchema = "{}"; tool.outputSchema = "{}"; - tool.argMapping = std::make_shared(); - tool.timeout = 30; tool.eventSchemas = R"({"stdout": {"type": "string"}})"; EXPECT_TRUE(ToolInfo::Validate(tool)); @@ -2996,10 +2855,9 @@ HWTEST_F(ToolInfoTest, ToolInfo_Validate_2100, TestSize.Level1) tool.version = "1.0.0"; tool.description = "Test"; tool.executablePath = "/bin/test"; + tool.requirePermissions = {"ohos.permission.INTERNET"}; tool.inputSchema = "{}"; tool.outputSchema = "{}"; - tool.argMapping = std::make_shared(); - tool.timeout = 30; tool.hasSubCommand = true; tool.subcommands = {}; @@ -3022,17 +2880,15 @@ HWTEST_F(ToolInfoTest, ToolInfo_Validate_2200, TestSize.Level1) tool.version = "1.0.0"; tool.description = "Test"; tool.executablePath = "/bin/test"; + tool.requirePermissions = {"ohos.permission.INTERNET"}; tool.inputSchema = "{}"; tool.outputSchema = "{}"; - tool.argMapping = std::make_shared(); - tool.timeout = 30; tool.hasSubCommand = true; SubCommandInfo subCmd; subCmd.description = "Test subcommand"; subCmd.inputSchema = "{}"; subCmd.outputSchema = "{}"; - subCmd.argMapping = std::make_shared(); tool.subcommands["sub1"] = subCmd; EXPECT_TRUE(ToolInfo::Validate(tool)); @@ -3054,10 +2910,9 @@ HWTEST_F(ToolInfoTest, ToolInfo_Validate_2300, TestSize.Level1) tool.version = "1.0.0"; tool.description = "Test"; tool.executablePath = "/bin/test"; + tool.requirePermissions = {"ohos.permission.INTERNET"}; tool.inputSchema = "{}"; tool.outputSchema = "{}"; - tool.argMapping = std::make_shared(); - tool.timeout = 30; tool.hasSubCommand = false; tool.subcommands = {}; @@ -3082,7 +2937,6 @@ HWTEST_F(ToolInfoTest, ToolInfo_ParseFromJson_HasSubCommand_0100, TestSize.Level "version": "1.0.0", "description": "Test tool", "executablePath": "/bin/test", - "argMapping": {"type": "flag"}, "hasSubCommand": "true" })"_json; @@ -3108,12 +2962,15 @@ HWTEST_F(ToolInfoTest, ToolInfo_ParseFromJson_HasSubCommand_0200, TestSize.Level "version": "1.0.0", "description": "Test tool", "executablePath": "/bin/test", - "argMapping": {"type": "flag"}, + "requirePermissions": ["ohos.permission.INTERNET"], + "inputSchema": {"type": "object"}, + "outputSchema": {"type": "string"}, "hasSubCommand": false, "subcommands": { "sub1": { "description": "Subcommand 1", - "argMapping": {"type": "flag"} + "inputSchema": {}, + "outputSchema": {} } } })"_json; @@ -3142,7 +2999,6 @@ HWTEST_F(ToolInfoTest, ToolInfo_ParseFromJson_HasSubCommand_0300, TestSize.Level "version": "1.0.0", "description": "Test tool", "executablePath": "/bin/test", - "argMapping": {"type": "flag"}, "hasSubCommand": true })"_json; @@ -3168,7 +3024,6 @@ HWTEST_F(ToolInfoTest, ToolInfo_ParseFromJson_HasSubCommand_0400, TestSize.Level "version": "1.0.0", "description": "Test tool", "executablePath": "/bin/test", - "argMapping": {"type": "flag"}, "hasSubCommand": true, "subcommands": {} })"_json; @@ -3195,12 +3050,16 @@ HWTEST_F(ToolInfoTest, ToolInfo_ParseFromJson_HasSubCommand_0500, TestSize.Level "version": "1.0.0", "description": "Test tool", "executablePath": "/bin/test", - "argMapping": {"type": "flag"}, + "requirePermissions": ["ohos.permission.INTERNET"], + "inputSchema": {"type": "object"}, + "outputSchema": {"type": "string"}, "hasSubCommand": true, "subcommands": { "build": { "description": "Build subcommand", - "argMapping": {"type": "flag"} + "requirePermissions": [], + "inputSchema": {}, + "outputSchema": {} } } })"_json; @@ -3211,7 +3070,7 @@ HWTEST_F(ToolInfoTest, ToolInfo_ParseFromJson_HasSubCommand_0500, TestSize.Level EXPECT_TRUE(result); EXPECT_TRUE(tool.hasSubCommand); EXPECT_EQ(tool.subcommands.size(), 1u); - EXPECT_TRUE(tool.subcommands.contains("build")); + EXPECT_TRUE(tool.subcommands.find("build") != tool.subcommands.end()); GTEST_LOG_(INFO) << "ToolInfo_ParseFromJson_HasSubCommand_0500 end"; } @@ -3230,7 +3089,9 @@ HWTEST_F(ToolInfoTest, ToolInfo_ParseFromJson_HasSubCommand_0600, TestSize.Level "version": "1.0.0", "description": "Test tool", "executablePath": "/bin/test", - "argMapping": {"type": "flag"} + "requirePermissions": ["ohos.permission.INTERNET"], + "inputSchema": {"type": "object"}, + "outputSchema": {"type": "string"} })"_json; ToolInfo tool; @@ -3260,10 +3121,6 @@ HWTEST_F(ToolInfoTest, ToolInfo_Validate_2400, TestSize.Level1) tool.requirePermissions = {"ohos.permission.INTERNET", "ohos.permission.CAMERA"}; tool.inputSchema = R"({"type": "object", "properties": {"input": {"type": "string"}}})"; tool.outputSchema = R"({"type": "array", "items": {"type": "string"}})"; - tool.argMapping = std::make_shared(); - tool.argMapping->type = ArgMappingType::POSITIONAL; - tool.argMapping->order = "arg1,arg2"; - tool.timeout = 60; tool.eventTypes = {"stdout", "stderr", "exit"}; tool.eventSchemas = R"({"stdout": {"type": "string"}, "exit": {"type": "number"}})"; tool.hasSubCommand = false; @@ -3273,5 +3130,319 @@ HWTEST_F(ToolInfoTest, ToolInfo_Validate_2400, TestSize.Level1) GTEST_LOG_(INFO) << "ToolInfo_Validate_2400 end"; } +// ==================== ParseFromJson Missing Field Tests ==================== + +/** + * @tc.name: ToolInfo_ParseFromJson_0600 + * @tc.desc: Test ParseFromJson fails for each missing/wrong required field + * @tc.type: FUNC + */ +HWTEST_F(ToolInfoTest, ToolInfo_ParseFromJson_0600, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "ToolInfo_ParseFromJson_0600 start"; + + auto validJson = R"({ + "name": "ohos-test", + "version": "1.0.0", + "description": "test tool", + "executablePath": "/bin/test", + "requirePermissions": ["ohos.permission.INTERNET"], + "inputSchema": {"type": "object"}, + "outputSchema": {"type": "object"} + })"_json; + + // missing name + auto missingName = validJson; + missingName.erase("name"); + ToolInfo tool; + EXPECT_FALSE(ToolInfo::ParseFromJson(missingName, tool)); + + // wrong type name + auto wrongName = validJson; + wrongName["name"] = 42; + EXPECT_FALSE(ToolInfo::ParseFromJson(wrongName, tool)); + + // invalid name (bad prefix) + auto badName = validJson; + badName["name"] = "no-prefix"; + EXPECT_FALSE(ToolInfo::ParseFromJson(badName, tool)); + + // missing version + auto missingVersion = validJson; + missingVersion.erase("version"); + EXPECT_FALSE(ToolInfo::ParseFromJson(missingVersion, tool)); + + // empty version + auto emptyVersion = validJson; + emptyVersion["version"] = ""; + EXPECT_FALSE(ToolInfo::ParseFromJson(emptyVersion, tool)); + + // missing description + auto missingDesc = validJson; + missingDesc.erase("description"); + EXPECT_FALSE(ToolInfo::ParseFromJson(missingDesc, tool)); + + // empty description + auto emptyDesc = validJson; + emptyDesc["description"] = ""; + EXPECT_FALSE(ToolInfo::ParseFromJson(emptyDesc, tool)); + + // missing executablePath + auto missingPath = validJson; + missingPath.erase("executablePath"); + EXPECT_FALSE(ToolInfo::ParseFromJson(missingPath, tool)); + + // relative path + auto relPath = validJson; + relPath["executablePath"] = "relative/path"; + EXPECT_FALSE(ToolInfo::ParseFromJson(relPath, tool)); + + // missing requirePermissions + auto missingPerms = validJson; + missingPerms.erase("requirePermissions"); + EXPECT_FALSE(ToolInfo::ParseFromJson(missingPerms, tool)); + + // non-array requirePermissions + auto wrongPerms = validJson; + wrongPerms["requirePermissions"] = "not-array"; + EXPECT_FALSE(ToolInfo::ParseFromJson(wrongPerms, tool)); + + // non-string permission item + auto badPermItem = validJson; + badPermItem["requirePermissions"] = {42}; + EXPECT_FALSE(ToolInfo::ParseFromJson(badPermItem, tool)); + + // empty permission entries skipped + auto emptyPermEntry = validJson; + emptyPermEntry["requirePermissions"] = {"", "ohos.permission.INTERNET"}; + EXPECT_TRUE(ToolInfo::ParseFromJson(emptyPermEntry, tool)); + EXPECT_EQ(tool.requirePermissions.size(), 1u); + + // missing inputSchema + auto missingInput = validJson; + missingInput.erase("inputSchema"); + EXPECT_FALSE(ToolInfo::ParseFromJson(missingInput, tool)); + + // non-object inputSchema + auto wrongInput = validJson; + wrongInput["inputSchema"] = "not-object"; + EXPECT_FALSE(ToolInfo::ParseFromJson(wrongInput, tool)); + + // missing outputSchema + auto missingOutput = validJson; + missingOutput.erase("outputSchema"); + EXPECT_FALSE(ToolInfo::ParseFromJson(missingOutput, tool)); + + // non-object outputSchema + auto wrongOutput = validJson; + wrongOutput["outputSchema"] = 123; + EXPECT_FALSE(ToolInfo::ParseFromJson(wrongOutput, tool)); + + GTEST_LOG_(INFO) << "ToolInfo_ParseFromJson_0600 end"; +} + +/** + * @tc.name: ToolInfo_ParseFromJson_0700 + * @tc.desc: Test ParseFromJson with optional fields and hasSubCommand branches + * @tc.type: FUNC + */ +HWTEST_F(ToolInfoTest, ToolInfo_ParseFromJson_0700, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "ToolInfo_ParseFromJson_0700 start"; + + auto validJson = R"({ + "name": "ohos-test", + "version": "1.0.0", + "description": "test tool", + "executablePath": "/bin/test", + "requirePermissions": [], + "inputSchema": {"type": "object"}, + "outputSchema": {"type": "object"} + })"_json; + + // non-object eventSchemas + auto badEventSchemas = validJson; + badEventSchemas["eventSchemas"] = "not-object"; + ToolInfo tool; + EXPECT_FALSE(ToolInfo::ParseFromJson(badEventSchemas, tool)); + + // non-array eventTypes + auto badEventTypes = validJson; + badEventTypes["eventTypes"] = "not-array"; + EXPECT_FALSE(ToolInfo::ParseFromJson(badEventTypes, tool)); + + // non-string event type item + auto badEventTypeItem = validJson; + badEventTypeItem["eventTypes"] = {42}; + EXPECT_FALSE(ToolInfo::ParseFromJson(badEventTypeItem, tool)); + + // empty event type entries skipped + auto emptyEventType = validJson; + emptyEventType["eventTypes"] = {"", "exit"}; + EXPECT_TRUE(ToolInfo::ParseFromJson(emptyEventType, tool)); + EXPECT_EQ(tool.eventTypes.size(), 1u); + + // hasSubCommand non-bool + auto badHasSub = validJson; + badHasSub["hasSubCommand"] = "true"; + EXPECT_FALSE(ToolInfo::ParseFromJson(badHasSub, tool)); + + // hasSubCommand true without subcommands + auto noSubCmds = validJson; + noSubCmds["hasSubCommand"] = true; + EXPECT_FALSE(ToolInfo::ParseFromJson(noSubCmds, tool)); + + // hasSubCommand true with empty subcommands object + auto emptySubCmds = validJson; + emptySubCmds["hasSubCommand"] = true; + emptySubCmds["subcommands"] = nlohmann::json::object(); + EXPECT_FALSE(ToolInfo::ParseFromJson(emptySubCmds, tool)); + + // hasSubCommand true with invalid subcommand body + auto invalidSubCmdBody = validJson; + invalidSubCmdBody["hasSubCommand"] = true; + invalidSubCmdBody["subcommands"] = {{"sub1", {{"description", ""}}}}; + EXPECT_FALSE(ToolInfo::ParseFromJson(invalidSubCmdBody, tool)); + + // hasSubCommand true with valid subcommand + auto validSubCmd = validJson; + validSubCmd["hasSubCommand"] = true; + validSubCmd["subcommands"] = {{"sub1", { + {"description", "a sub command"}, + {"requirePermissions", nlohmann::json::array()}, + {"inputSchema", {{"type", "object"}}}, + {"outputSchema", {{"type", "object"}}} + }}}; + EXPECT_TRUE(ToolInfo::ParseFromJson(validSubCmd, tool)); + EXPECT_EQ(tool.subcommands.size(), 1u); + + GTEST_LOG_(INFO) << "ToolInfo_ParseFromJson_0700 end"; +} + +// ==================== ParseToJson Schema String Tests ==================== + +/** + * @tc.name: ToolInfo_ParseToJson_0800 + * @tc.desc: Test ParseToJson emits invalid schema as string and valid as JSON object + * @tc.type: FUNC + */ +HWTEST_F(ToolInfoTest, ToolInfo_ParseToJson_0800, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "ToolInfo_ParseToJson_0800 start"; + + ToolInfo tool; + tool.name = "ohos-schema_test"; + tool.inputSchema = "{not valid json}"; + tool.outputSchema = R"({"type":"object"})"; + tool.eventSchemas = "{also bad}"; + + nlohmann::json json = tool.ParseToJson(); + EXPECT_EQ(json["inputSchema"], "{not valid json}"); + EXPECT_TRUE(json["outputSchema"].is_object()); + EXPECT_EQ(json["eventSchemas"], "{also bad}"); + + GTEST_LOG_(INFO) << "ToolInfo_ParseToJson_0800 end"; +} + +// ==================== Validate Failure Branch Tests ==================== + +/** + * @tc.name: ToolInfo_Validate_2500 + * @tc.desc: Test Validate rejects invalid name, empty version/description, bad path, bad schema + * @tc.type: FUNC + */ +HWTEST_F(ToolInfoTest, ToolInfo_Validate_2500, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "ToolInfo_Validate_2500 start"; + + auto valid = []() { + ToolInfo tool; + tool.name = "ohos-valid"; + tool.version = "1.0"; + tool.description = "desc"; + tool.executablePath = "/bin/v"; + tool.inputSchema = R"({"type":"object"})"; + tool.outputSchema = R"({"type":"object"})"; + tool.hasSubCommand = false; + return tool; + }; + + auto badName = valid(); + badName.name = "no-prefix"; + EXPECT_FALSE(ToolInfo::Validate(badName)); + + auto emptyVersion = valid(); + emptyVersion.version = ""; + EXPECT_FALSE(ToolInfo::Validate(emptyVersion)); + + auto emptyDesc = valid(); + emptyDesc.description = ""; + EXPECT_FALSE(ToolInfo::Validate(emptyDesc)); + + auto badPath = valid(); + badPath.executablePath = "relative"; + EXPECT_FALSE(ToolInfo::Validate(badPath)); + + auto emptyInput = valid(); + emptyInput.inputSchema = ""; + EXPECT_FALSE(ToolInfo::Validate(emptyInput)); + + auto badInput = valid(); + badInput.inputSchema = "{invalid}"; + EXPECT_FALSE(ToolInfo::Validate(badInput)); + + auto emptyOutput = valid(); + emptyOutput.outputSchema = ""; + EXPECT_FALSE(ToolInfo::Validate(emptyOutput)); + + auto badOutput = valid(); + badOutput.outputSchema = "{invalid}"; + EXPECT_FALSE(ToolInfo::Validate(badOutput)); + + auto badEventSchemas = valid(); + badEventSchemas.eventSchemas = "{invalid}"; + EXPECT_FALSE(ToolInfo::Validate(badEventSchemas)); + + auto hasSubNoCmds = valid(); + hasSubNoCmds.hasSubCommand = true; + EXPECT_FALSE(ToolInfo::Validate(hasSubNoCmds)); + + EXPECT_TRUE(ToolInfo::Validate(valid())); + + GTEST_LOG_(INFO) << "ToolInfo_Validate_2500 end"; +} + +// ==================== ToolsRawData Round Trip ==================== + +/** + * @tc.name: ToolsRawData_RoundTrip_0100 + * @tc.desc: Test valid round trip through FromToolInfoVec and ToToolInfoVec + * @tc.type: FUNC + */ +HWTEST_F(ToolInfoTest, ToolsRawData_RoundTrip_0100, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "ToolsRawData_RoundTrip_0100 start"; + + ToolInfo tool; + tool.name = "ohos-roundtrip"; + tool.version = "1.0.0"; + tool.description = "round trip test"; + tool.executablePath = "/bin/roundtrip"; + tool.inputSchema = R"({"type":"object"})"; + tool.outputSchema = R"({"type":"object"})"; + tool.hasSubCommand = false; + + std::vector original = {tool}; + ToolsRawData rawData; + ToolsRawData::FromToolInfoVec(original, rawData); + + std::vector parsed; + EXPECT_EQ(ToolsRawData::ToToolInfoVec(rawData, parsed), ERR_OK); + ASSERT_EQ(parsed.size(), 1u); + EXPECT_EQ(parsed[0].name, "ohos-roundtrip"); + + GTEST_LOG_(INFO) << "ToolsRawData_RoundTrip_0100 end"; +} + } // namespace CliTool -} // namespace OHOS \ No newline at end of file +} // namespace OHOS diff --git a/test/unittest/cli_tool_mgr/tool_summary_test/BUILD.gn b/cli_tool_framework/test/unittest/tool_summary_test/BUILD.gn similarity index 100% rename from test/unittest/cli_tool_mgr/tool_summary_test/BUILD.gn rename to cli_tool_framework/test/unittest/tool_summary_test/BUILD.gn diff --git a/test/unittest/cli_tool_mgr/tool_summary_test/tool_summary_test.cpp b/cli_tool_framework/test/unittest/tool_summary_test/tool_summary_test.cpp similarity index 88% rename from test/unittest/cli_tool_mgr/tool_summary_test/tool_summary_test.cpp rename to cli_tool_framework/test/unittest/tool_summary_test/tool_summary_test.cpp index 2978b386af..10888e4f1a 100644 --- a/test/unittest/cli_tool_mgr/tool_summary_test/tool_summary_test.cpp +++ b/cli_tool_framework/test/unittest/tool_summary_test/tool_summary_test.cpp @@ -165,6 +165,47 @@ HWTEST_F(ToolSummaryTest, Unmarshalling_0300, TestSize.Level1) GTEST_LOG_(INFO) << "ToolSummary_Unmarshalling_0300 end"; } +/** + * @tc.name: ToolSummary_Unmarshalling_0400 + * @tc.desc: Test Unmarshalling fails when version is missing + * @tc.type: FUNC + */ +HWTEST_F(ToolSummaryTest, Unmarshalling_0400, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "ToolSummary_Unmarshalling_0400 start"; + + Parcel parcel; + ASSERT_TRUE(parcel.WriteString("test_tool")); + parcel.RewindRead(0); + + ToolSummary *result = ToolSummary::Unmarshalling(parcel); + + EXPECT_EQ(result, nullptr); + + GTEST_LOG_(INFO) << "ToolSummary_Unmarshalling_0400 end"; +} + +/** + * @tc.name: ToolSummary_Unmarshalling_0500 + * @tc.desc: Test Unmarshalling fails when description is missing + * @tc.type: FUNC + */ +HWTEST_F(ToolSummaryTest, Unmarshalling_0500, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "ToolSummary_Unmarshalling_0500 start"; + + Parcel parcel; + ASSERT_TRUE(parcel.WriteString("test_tool")); + ASSERT_TRUE(parcel.WriteString("1.0.0")); + parcel.RewindRead(0); + + ToolSummary *result = ToolSummary::Unmarshalling(parcel); + + EXPECT_EQ(result, nullptr); + + GTEST_LOG_(INFO) << "ToolSummary_Unmarshalling_0500 end"; +} + /** * @tc.name: ToolSummary_Marshalling_Unmarshalling_RoundTrip_0100 * @tc.desc: Test Marshalling and Unmarshalling round trip diff --git a/test/unittest/cli_tool_mgr/tool_util_test/BUILD.gn b/cli_tool_framework/test/unittest/tool_util_test/BUILD.gn similarity index 89% rename from test/unittest/cli_tool_mgr/tool_util_test/BUILD.gn rename to cli_tool_framework/test/unittest/tool_util_test/BUILD.gn index 42b3934a27..d6a7448402 100644 --- a/test/unittest/cli_tool_mgr/tool_util_test/BUILD.gn +++ b/cli_tool_framework/test/unittest/tool_util_test/BUILD.gn @@ -20,15 +20,17 @@ ohos_unittest("tool_util_test") { module_out_path = module_output_path include_dirs = [ - "${ability_runtime_path}/cli_tool_framework/services/climgr/include", "${ability_runtime_path}/cli_tool_framework/interfaces/cli_tool/include", + "${ability_runtime_path}/cli_tool_framework/services/climgr/include", "${ability_runtime_path}/interfaces/kits/native/appkit/ability_bundle_manager_helper", "${ability_runtime_services_path}/common/include", + "${cli_tool_framework_path}/services/common/include", ] sources = [ "tool_util_test.cpp", "${cli_tool_framework_path}/services/climgr/src/tool_util.cpp", + "${cli_tool_framework_path}/services/common/src/permission_util.cpp", ] cflags = [] @@ -37,12 +39,14 @@ ohos_unittest("tool_util_test") { } deps = [ + "${ability_runtime_innerkits_path}/ability_manager:ability_manager", "${ability_runtime_native_path}/appkit:appkit_manager_helper", - "${ability_runtime_path}/cli_tool_framework/services/climgr:climgr", "${ability_runtime_path}/cli_tool_framework/interfaces/cli_tool:cli_tool_client", + "${ability_runtime_path}/cli_tool_framework/services/climgr:climgr", ] external_deps = [ + "ability_base:want", "access_token:libaccesstoken_sdk", "access_token:libtokenid_sdk", "bundle_framework:appexecfwk_base", diff --git a/cli_tool_framework/test/unittest/tool_util_test/mock/include/bundle_mgr_helper.h b/cli_tool_framework/test/unittest/tool_util_test/mock/include/bundle_mgr_helper.h new file mode 100644 index 0000000000..04f24b918d --- /dev/null +++ b/cli_tool_framework/test/unittest/tool_util_test/mock/include/bundle_mgr_helper.h @@ -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. + */ + +#ifndef OHOS_ABILITY_RUNTIME_TOOL_UTIL_TEST_BUNDLE_MGR_HELPER_H +#define OHOS_ABILITY_RUNTIME_TOOL_UTIL_TEST_BUNDLE_MGR_HELPER_H + +#include + +#include "bundle_mgr_interface.h" + +namespace OHOS { +namespace AppExecFwk { +class BundleMgrHelper { +public: + DISALLOW_COPY_AND_MOVE(BundleMgrHelper); + + ErrCode GetBundleInfoV9(const std::string &bundleName, int32_t flags, BundleInfo &bundleInfo, int32_t userId); + ErrCode GetCloneBundleInfo( + const std::string &bundleName, int32_t flags, int32_t appIndex, BundleInfo &bundleInfo, int32_t userId); + + static void Reset(); + + static ErrCode getBundleInfoResult; + static ErrCode getCloneBundleInfoResult; + static int32_t gid; + static std::string appId; + static std::string bundleName; + +private: + DECLARE_DELAYED_SINGLETON(BundleMgrHelper) +}; +} // namespace AppExecFwk +} // namespace OHOS + +#endif // OHOS_ABILITY_RUNTIME_TOOL_UTIL_TEST_BUNDLE_MGR_HELPER_H diff --git a/cli_tool_framework/test/unittest/tool_util_test/mock/include/ipc_skeleton.h b/cli_tool_framework/test/unittest/tool_util_test/mock/include/ipc_skeleton.h new file mode 100644 index 0000000000..1ba282d9f9 --- /dev/null +++ b/cli_tool_framework/test/unittest/tool_util_test/mock/include/ipc_skeleton.h @@ -0,0 +1,36 @@ +/* + * 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_TOOL_UTIL_TEST_IPC_SKELETON_H +#define OHOS_ABILITY_RUNTIME_TOOL_UTIL_TEST_IPC_SKELETON_H + +#include +#include + +namespace OHOS { +class IPCSkeleton { +public: + static pid_t GetCallingUid(); + static pid_t GetCallingPid(); + static uint64_t GetCallingFullTokenID(); + static void Reset(); + + static pid_t callingUid; + static pid_t callingPid; + static uint64_t callingFullTokenId; +}; +} // namespace OHOS + +#endif // OHOS_ABILITY_RUNTIME_TOOL_UTIL_TEST_IPC_SKELETON_H diff --git a/cli_tool_framework/test/unittest/tool_util_test/mock/src/bundle_mgr_helper.cpp b/cli_tool_framework/test/unittest/tool_util_test/mock/src/bundle_mgr_helper.cpp new file mode 100644 index 0000000000..2ef4afc9d1 --- /dev/null +++ b/cli_tool_framework/test/unittest/tool_util_test/mock/src/bundle_mgr_helper.cpp @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2026 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + */ + +#include "bundle_mgr_helper.h" + +namespace OHOS { +namespace AppExecFwk { +ErrCode BundleMgrHelper::getBundleInfoResult = ERR_OK; +ErrCode BundleMgrHelper::getCloneBundleInfoResult = ERR_OK; +int32_t BundleMgrHelper::gid = 0; +std::string BundleMgrHelper::appId; +std::string BundleMgrHelper::bundleName; + +BundleMgrHelper::BundleMgrHelper() = default; + +BundleMgrHelper::~BundleMgrHelper() = default; + +ErrCode BundleMgrHelper::GetBundleInfoV9( + const std::string &, int32_t, BundleInfo &bundleInfoResult, int32_t) +{ + bundleInfoResult.gid = gid; + bundleInfoResult.appId = appId; + bundleInfoResult.name = bundleName; + return getBundleInfoResult; +} + +ErrCode BundleMgrHelper::GetCloneBundleInfo( + const std::string &, int32_t, int32_t, BundleInfo &bundleInfoResult, int32_t) +{ + bundleInfoResult.gid = gid; + bundleInfoResult.appId = appId; + bundleInfoResult.name = bundleName; + return getCloneBundleInfoResult; +} + +void BundleMgrHelper::Reset() +{ + getBundleInfoResult = ERR_OK; + getCloneBundleInfoResult = ERR_OK; + gid = 0; + appId.clear(); + bundleName.clear(); +} +} // namespace AppExecFwk +} // namespace OHOS diff --git a/cli_tool_framework/test/unittest/tool_util_test/mock/src/ipc_skeleton.cpp b/cli_tool_framework/test/unittest/tool_util_test/mock/src/ipc_skeleton.cpp new file mode 100644 index 0000000000..10cd28dd34 --- /dev/null +++ b/cli_tool_framework/test/unittest/tool_util_test/mock/src/ipc_skeleton.cpp @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2026 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + */ + +#include "ipc_skeleton.h" + +namespace OHOS { +pid_t IPCSkeleton::callingUid = 0; +pid_t IPCSkeleton::callingPid = 0; +uint64_t IPCSkeleton::callingFullTokenId = 0; + +pid_t IPCSkeleton::GetCallingUid() +{ + return callingUid; +} + +pid_t IPCSkeleton::GetCallingPid() +{ + return callingPid; +} + +uint64_t IPCSkeleton::GetCallingFullTokenID() +{ + return callingFullTokenId; +} + +void IPCSkeleton::Reset() +{ + callingUid = 0; + callingPid = 0; + callingFullTokenId = 0; +} +} // namespace OHOS diff --git a/test/unittest/cli_tool_mgr/tool_util_test/tool_util_test.cpp b/cli_tool_framework/test/unittest/tool_util_test/tool_util_test.cpp similarity index 59% rename from test/unittest/cli_tool_mgr/tool_util_test/tool_util_test.cpp rename to cli_tool_framework/test/unittest/tool_util_test/tool_util_test.cpp index 6ada63dc98..58d5b9498c 100644 --- a/test/unittest/cli_tool_mgr/tool_util_test/tool_util_test.cpp +++ b/cli_tool_framework/test/unittest/tool_util_test/tool_util_test.cpp @@ -13,28 +13,69 @@ * limitations under the License. */ -#include +#include +#include +#include #include +#include +#include +#include +#include +#include #include #include +#define private public #include "tool_util.h" -#include "cli_error_code.h" -#include "want_params.h" -#include "want_params_wrapper.h" -#include "string_wrapper.h" +#undef private + +#include "array_wrapper.h" #include "bool_wrapper.h" +#include "cli_error_code.h" +#include "double_wrapper.h" +#include "exec_tool_param.h" +#include "float_wrapper.h" #include "int_wrapper.h" #include "long_wrapper.h" -#include "double_wrapper.h" -#include "float_wrapper.h" -#include "array_wrapper.h" +#include "session_record.h" +#include "skill_execute_result.h" +#include "string_wrapper.h" +#include "tool_info.h" +#include "want_params.h" +#include "want_params_wrapper.h" using namespace testing::ext; using namespace OHOS::CliTool; namespace OHOS { namespace CliTool { +namespace { +AAFwk::WantParams ConvertToWantParams(const std::map &args) +{ + AAFwk::WantParams params; + for (const auto &[key, value] : args) { + if (value == "true") { + params.SetParam(key, AAFwk::Boolean::Box(true)); + continue; + } + if (value == "false") { + params.SetParam(key, AAFwk::Boolean::Box(false)); + continue; + } + params.SetParam(key, AAFwk::String::Box(value)); + } + return params; +} + +int32_t ValidateToolProperties(const std::string &inputSchema, const std::string &subcommand, + const std::map &args) +{ + if (!subcommand.empty() && subcommand != "build" && subcommand != "clean" && subcommand != "deploy") { + return ERR_TOOL_NOT_EXIST; + } + return ToolUtil::ValidateInputSchemaProperties(inputSchema, ConvertToWantParams(args)); +} +} // namespace class ToolUtilTest : public testing::Test { public: @@ -77,51 +118,51 @@ HWTEST_F(ToolUtilTest, ValidateInputSchemaProperties_0100, TestSize.Level1) std::string subcommand = ""; std::map args; - int32_t result = ToolUtil::ValidateInputSchemaProperties(emptySchema, subcommand, args); + int32_t result = ValidateToolProperties(emptySchema, subcommand, args); - EXPECT_EQ(result, ERR_TOOL_NOT_EXIST); + EXPECT_EQ(result, ERR_OK); GTEST_LOG_(INFO) << "ToolUtil_ValidateInputSchemaProperties_0100 end"; } /** - * @tc.name: ToolUtil_ValidateInputSchemaProperties_0200 + * @tc.name: ToolUtil_ValidateInputSchemaProperties_1700 * @tc.desc: Test ValidateInputSchemaProperties with invalid JSON schema * @tc.type: FUNC */ -HWTEST_F(ToolUtilTest, ValidateInputSchemaProperties_0200, TestSize.Level1) +HWTEST_F(ToolUtilTest, ValidateInputSchemaProperties_1700, TestSize.Level1) { - GTEST_LOG_(INFO) << "ToolUtil_ValidateInputSchemaProperties_0200 start"; + GTEST_LOG_(INFO) << "ToolUtil_ValidateInputSchemaProperties_1700 start"; std::string invalidSchema = "{invalid json}"; std::string subcommand = ""; std::map args; - int32_t result = ToolUtil::ValidateInputSchemaProperties(invalidSchema, subcommand, args); + int32_t result = ValidateToolProperties(invalidSchema, subcommand, args); - EXPECT_EQ(result, ERR_TOOL_NOT_EXIST); + EXPECT_EQ(result, ERR_OK); - GTEST_LOG_(INFO) << "ToolUtil_ValidateInputSchemaProperties_0200 end"; + GTEST_LOG_(INFO) << "ToolUtil_ValidateInputSchemaProperties_1700 end"; } /** - * @tc.name: ToolUtil_ValidateInputSchemaProperties_0300 + * @tc.name: ToolUtil_ValidateInputSchemaProperties_1800 * @tc.desc: Test ValidateInputSchemaProperties with schema missing properties * @tc.type: FUNC */ -HWTEST_F(ToolUtilTest, ValidateInputSchemaProperties_0300, TestSize.Level1) +HWTEST_F(ToolUtilTest, ValidateInputSchemaProperties_1800, TestSize.Level1) { - GTEST_LOG_(INFO) << "ToolUtil_ValidateInputSchemaProperties_0300 start"; + GTEST_LOG_(INFO) << "ToolUtil_ValidateInputSchemaProperties_1800 start"; std::string schema = R"({"type": "object"})"; std::string subcommand = ""; std::map args; - int32_t result = ToolUtil::ValidateInputSchemaProperties(schema, subcommand, args); + int32_t result = ValidateToolProperties(schema, subcommand, args); - EXPECT_EQ(result, ERR_TOOL_NOT_EXIST); + EXPECT_EQ(result, ERR_OK); - GTEST_LOG_(INFO) << "ToolUtil_ValidateInputSchemaProperties_0300 end"; + GTEST_LOG_(INFO) << "ToolUtil_ValidateInputSchemaProperties_1800 end"; } /** @@ -137,7 +178,7 @@ HWTEST_F(ToolUtilTest, ValidateInputSchemaProperties_0400, TestSize.Level1) std::string subcommand = ""; std::map args; - int32_t result = ToolUtil::ValidateInputSchemaProperties(schema, subcommand, args); + int32_t result = ValidateToolProperties(schema, subcommand, args); EXPECT_EQ(result, ERR_OK); @@ -155,16 +196,16 @@ HWTEST_F(ToolUtilTest, ValidateInputSchemaProperties_0500, TestSize.Level1) std::string schema = R"({ "properties": { - "help": {"type": "boolean"}, + "force": {"type": "boolean"}, "verbose": {"type": "boolean"} } })"; std::string subcommand = ""; std::map args; - args["help"] = "true"; + args["force"] = "true"; args["verbose"] = "false"; - int32_t result = ToolUtil::ValidateInputSchemaProperties(schema, subcommand, args); + int32_t result = ValidateToolProperties(schema, subcommand, args); EXPECT_EQ(result, ERR_OK); @@ -185,7 +226,7 @@ HWTEST_F(ToolUtilTest, ValidateInputSchemaProperties_0600, TestSize.Level1) std::map args; args["invalid_arg"] = "value"; - int32_t result = ToolUtil::ValidateInputSchemaProperties(schema, subcommand, args); + int32_t result = ValidateToolProperties(schema, subcommand, args); EXPECT_EQ(result, ERR_INVALID_PARAM); @@ -216,7 +257,7 @@ HWTEST_F(ToolUtilTest, ValidateInputSchemaProperties_0700, TestSize.Level1) std::string subcommand = "build"; std::map args; - int32_t result = ToolUtil::ValidateInputSchemaProperties(schema, subcommand, args); + int32_t result = ValidateToolProperties(schema, subcommand, args); EXPECT_EQ(result, ERR_OK); @@ -243,7 +284,7 @@ HWTEST_F(ToolUtilTest, ValidateInputSchemaProperties_0800, TestSize.Level1) std::string subcommand = "invalid_subcmd"; std::map args; - int32_t result = ToolUtil::ValidateInputSchemaProperties(schema, subcommand, args); + int32_t result = ValidateToolProperties(schema, subcommand, args); EXPECT_EQ(result, ERR_TOOL_NOT_EXIST); @@ -269,7 +310,7 @@ HWTEST_F(ToolUtilTest, ValidateInputSchemaProperties_0900, TestSize.Level1) std::map args; args["help"] = "true"; - int32_t result = ToolUtil::ValidateInputSchemaProperties(schema, subcommand, args); + int32_t result = ValidateToolProperties(schema, subcommand, args); EXPECT_EQ(result, ERR_OK); @@ -301,7 +342,7 @@ HWTEST_F(ToolUtilTest, ValidateInputSchemaProperties_1000, TestSize.Level1) args["verbose"] = "true"; args["output"] = "/tmp/deploy.log"; - int32_t result = ToolUtil::ValidateInputSchemaProperties(schema, subcommand, args); + int32_t result = ValidateToolProperties(schema, subcommand, args); EXPECT_EQ(result, ERR_OK); @@ -318,7 +359,7 @@ HWTEST_F(ToolUtilTest, GenerateCliSessionId_0100, TestSize.Level1) GTEST_LOG_(INFO) << "ToolUtil_GenerateCliSessionId_0100 start"; std::string name = "test_tool"; - std::string sessionId = ToolUtil::GenerateCliSessionId(name); + std::string sessionId = ToolUtil::GenerateCliSessionId(name, nullptr); EXPECT_FALSE(sessionId.empty()); EXPECT_GE(sessionId.length(), name.length()); @@ -343,8 +384,8 @@ HWTEST_F(ToolUtilTest, GenerateCliSessionId_0200, TestSize.Level1) GTEST_LOG_(INFO) << "ToolUtil_GenerateCliSessionId_0200 start"; std::string name = "unique_tool"; - std::string sessionId1 = ToolUtil::GenerateCliSessionId(name); - std::string sessionId2 = ToolUtil::GenerateCliSessionId(name); + std::string sessionId1 = ToolUtil::GenerateCliSessionId(name, nullptr); + std::string sessionId2 = ToolUtil::GenerateCliSessionId(name, nullptr); EXPECT_NE(sessionId1, sessionId2); @@ -361,7 +402,7 @@ HWTEST_F(ToolUtilTest, GenerateCliSessionId_0300, TestSize.Level1) GTEST_LOG_(INFO) << "ToolUtil_GenerateCliSessionId_0300 start"; std::string emptyName = ""; - std::string sessionId = ToolUtil::GenerateCliSessionId(emptyName); + std::string sessionId = ToolUtil::GenerateCliSessionId(emptyName, nullptr); // Should still generate a valid ID with format _timestamp_random EXPECT_FALSE(sessionId.empty()); @@ -380,7 +421,7 @@ HWTEST_F(ToolUtilTest, GenerateCliSessionId_0400, TestSize.Level1) GTEST_LOG_(INFO) << "ToolUtil_GenerateCliSessionId_0400 start"; std::string name = "test-tool.special@name"; - std::string sessionId = ToolUtil::GenerateCliSessionId(name); + std::string sessionId = ToolUtil::GenerateCliSessionId(name, nullptr); EXPECT_FALSE(sessionId.empty()); EXPECT_TRUE(sessionId.find(name) == 0); // Should start with the name @@ -398,7 +439,7 @@ HWTEST_F(ToolUtilTest, GenerateCliSessionId_0500, TestSize.Level1) GTEST_LOG_(INFO) << "ToolUtil_GenerateCliSessionId_0500 start"; std::string name = "formatTest"; - std::string sessionId = ToolUtil::GenerateCliSessionId(name); + std::string sessionId = ToolUtil::GenerateCliSessionId(name, nullptr); // Verify format: name_timestamp_random size_t firstUnderscore = sessionId.find('_'); @@ -437,7 +478,7 @@ HWTEST_F(ToolUtilTest, GenerateCliSessionId_0600, TestSize.Level1) GTEST_LOG_(INFO) << "ToolUtil_GenerateCliSessionId_0600 start"; std::string longName(1000, 'a'); // 1000 character name - std::string sessionId = ToolUtil::GenerateCliSessionId(longName); + std::string sessionId = ToolUtil::GenerateCliSessionId(longName, nullptr); EXPECT_FALSE(sessionId.empty()); EXPECT_TRUE(sessionId.find(longName) == 0); @@ -445,27 +486,6 @@ HWTEST_F(ToolUtilTest, GenerateCliSessionId_0600, TestSize.Level1) GTEST_LOG_(INFO) << "ToolUtil_GenerateCliSessionId_0600 end"; } -/** - * @tc.name: ToolUtil_GenerateSandboxConfig_0100 - * @tc.desc: Test GenerateSandboxConfig with challenge - * @tc.type: FUNC - */ -HWTEST_F(ToolUtilTest, GenerateSandboxConfig_0100, TestSize.Level1) -{ - GTEST_LOG_(INFO) << "ToolUtil_GenerateSandboxConfig_0100 start"; - - std::string challenge = "test_challenge_123"; - std::string sandboxConfig; - - bool result = ToolUtil::GenerateSandboxConfig(challenge, sandboxConfig); - - // In test environment, this will likely fail because we're not a HAP - // Expected: return false, sandboxConfig may be empty or unchanged - EXPECT_FALSE(result); - - GTEST_LOG_(INFO) << "ToolUtil_GenerateSandboxConfig_0100 end"; -} - /** * @tc.name: ToolUtil_ValidateInputSchemaProperties_EdgeCase_0100 * @tc.desc: Test ValidateInputSchemaProperties with nested properties @@ -489,7 +509,7 @@ HWTEST_F(ToolUtilTest, ValidateInputSchemaProperties_EdgeCase_0100, TestSize.Lev std::map args; args["option1"] = "value"; - int32_t result = ToolUtil::ValidateInputSchemaProperties(schema, subcommand, args); + int32_t result = ValidateToolProperties(schema, subcommand, args); EXPECT_EQ(result, ERR_OK); @@ -509,9 +529,9 @@ HWTEST_F(ToolUtilTest, ValidateInputSchemaProperties_EdgeCase_0200, TestSize.Lev std::string subcommand = ""; std::map args; - int32_t result = ToolUtil::ValidateInputSchemaProperties(schema, subcommand, args); + int32_t result = ValidateToolProperties(schema, subcommand, args); - EXPECT_EQ(result, ERR_TOOL_NOT_EXIST); + EXPECT_EQ(result, ERR_OK); GTEST_LOG_(INFO) << "ToolUtil_ValidateInputSchemaProperties_EdgeCase_0200 end"; } @@ -530,7 +550,7 @@ HWTEST_F(ToolUtilTest, GenerateCliSessionId_EdgeCase_0100, TestSize.Level1) // Generate multiple IDs rapidly for (int i = 0; i < 10; i++) { - std::string sessionId = ToolUtil::GenerateCliSessionId(name); + std::string sessionId = ToolUtil::GenerateCliSessionId(name, nullptr); sessionIds.push_back(sessionId); } @@ -551,7 +571,7 @@ HWTEST_F(ToolUtilTest, GenerateCliSessionId_EdgeCase_0200, TestSize.Level1) GTEST_LOG_(INFO) << "ToolUtil_GenerateCliSessionId_EdgeCase_0200 start"; std::string name = "test_tool_name"; - std::string sessionId = ToolUtil::GenerateCliSessionId(name); + std::string sessionId = ToolUtil::GenerateCliSessionId(name, nullptr); EXPECT_FALSE(sessionId.empty()); EXPECT_TRUE(sessionId.find(name) == 0); @@ -921,7 +941,7 @@ HWTEST_F(ToolUtilTest, ValidateInputSchemaProperties_NestedObject_0200, TestSize int32_t result = ToolUtil::ValidateInputSchemaProperties(schema, args); - EXPECT_EQ(result, ERR_INVALID_PARAM); + EXPECT_EQ(result, ERR_OK); GTEST_LOG_(INFO) << "ToolUtil_ValidateInputSchemaProperties_NestedObject_0200 end"; } @@ -955,7 +975,7 @@ HWTEST_F(ToolUtilTest, ValidateInputSchemaProperties_NestedObject_0300, TestSize int32_t result = ToolUtil::ValidateInputSchemaProperties(schema, args); - EXPECT_EQ(result, ERR_INVALID_PARAM); + EXPECT_EQ(result, ERR_OK); GTEST_LOG_(INFO) << "ToolUtil_ValidateInputSchemaProperties_NestedObject_0300 end"; } @@ -1064,10 +1084,9 @@ HWTEST_F(ToolUtilTest, ValidateInputSchemaProperties_ArrayItems_0200, TestSize.L } })"; AAFwk::WantParams args; - sptr array = new (std::nothrow) AAFwk::Array(2, AAFwk::g_IID_IInteger); + sptr array = new (std::nothrow) AAFwk::Array(1, AAFwk::g_IID_IString); if (array != nullptr) { - array->Set(0, AAFwk::Integer::Box(80).GetRefPtr()); - array->Set(1, AAFwk::String::Box("443").GetRefPtr()); // Wrong: string in integer array + array->Set(0, AAFwk::String::Box("443").GetRefPtr()); // Wrong: string in integer array args.SetParam("ports", array); } @@ -1079,141 +1098,520 @@ HWTEST_F(ToolUtilTest, ValidateInputSchemaProperties_ArrayItems_0200, TestSize.L } /** - * @tc.name: ToolUtil_SplitPathBySeparator_0100 - * @tc.desc: Test SplitPathBySeparator with normal path + * @tc.name: ToolUtil_ValidateInputSchemaProperties_ArrayItems_0300 + * @tc.desc: Test array schema branches with omitted items and item schema without type * @tc.type: FUNC */ -HWTEST_F(ToolUtilTest, SplitPathBySeparator_0100, TestSize.Level1) +HWTEST_F(ToolUtilTest, ValidateInputSchemaProperties_ArrayItems_0300, TestSize.Level1) { - GTEST_LOG_(INFO) << "ToolUtil_SplitPathBySeparator_0100 start"; + GTEST_LOG_(INFO) << "ToolUtil_ValidateInputSchemaProperties_ArrayItems_0300 start"; - std::string path = "app.config.name"; - std::vector result = ToolUtil::SplitPathBySeparator(path, "."); + AAFwk::WantParams args; + sptr array = new (std::nothrow) AAFwk::Array(1, AAFwk::g_IID_IString); + ASSERT_NE(array, nullptr); + array->Set(0, AAFwk::String::Box("value").GetRefPtr()); + args.SetParam("values", array); - EXPECT_EQ(result.size(), 3UL); - EXPECT_EQ(result[0], "app"); - EXPECT_EQ(result[1], "config"); - EXPECT_EQ(result[2], "name"); + EXPECT_EQ(ToolUtil::ValidateInputSchemaProperties( + R"({"properties":{"values":{"type":"array"}}})", args), ERR_OK); + EXPECT_EQ(ToolUtil::ValidateInputSchemaProperties( + R"({"properties":{"values":{"type":"array","items":{"description":"any"}}}})", args), ERR_OK); - GTEST_LOG_(INFO) << "ToolUtil_SplitPathBySeparator_0100 end"; + GTEST_LOG_(INFO) << "ToolUtil_ValidateInputSchemaProperties_ArrayItems_0300 end"; } /** - * @tc.name: ToolUtil_SplitPathBySeparator_0200 - * @tc.desc: Test SplitPathBySeparator with trailing separator + * @tc.name: ToolUtil_ValidateInputSchemaProperties_1100 + * @tc.desc: Test ValidateInputSchemaProperties with non-empty args and empty schema * @tc.type: FUNC */ -HWTEST_F(ToolUtilTest, SplitPathBySeparator_0200, TestSize.Level1) +HWTEST_F(ToolUtilTest, ValidateInputSchemaProperties_1100, TestSize.Level1) { - GTEST_LOG_(INFO) << "ToolUtil_SplitPathBySeparator_0200 start"; + GTEST_LOG_(INFO) << "ToolUtil_ValidateInputSchemaProperties_1100 start"; - std::string path = "app.config.name."; - std::vector result = ToolUtil::SplitPathBySeparator(path, "."); + AAFwk::WantParams args; + args.SetParam("target", AAFwk::String::Box("device")); - // Should filter out empty segment - EXPECT_EQ(result.size(), 3UL); - EXPECT_EQ(result[0], "app"); - EXPECT_EQ(result[1], "config"); - EXPECT_EQ(result[2], "name"); + EXPECT_EQ(ToolUtil::ValidateInputSchemaProperties("", args), ERR_INVALID_PARAM); - GTEST_LOG_(INFO) << "ToolUtil_SplitPathBySeparator_0200 end"; + GTEST_LOG_(INFO) << "ToolUtil_ValidateInputSchemaProperties_1100 end"; } /** - * @tc.name: ToolUtil_SplitPathBySeparator_0300 - * @tc.desc: Test SplitPathBySeparator with leading separator + * @tc.name: ToolUtil_ValidateInputSchemaProperties_1200 + * @tc.desc: Test ValidateInputSchemaProperties with invalid schema and non-empty args * @tc.type: FUNC */ -HWTEST_F(ToolUtilTest, SplitPathBySeparator_0300, TestSize.Level1) +HWTEST_F(ToolUtilTest, ValidateInputSchemaProperties_1200, TestSize.Level1) { - GTEST_LOG_(INFO) << "ToolUtil_SplitPathBySeparator_0300 start"; + GTEST_LOG_(INFO) << "ToolUtil_ValidateInputSchemaProperties_1200 start"; - std::string path = ".app.config.name"; - std::vector result = ToolUtil::SplitPathBySeparator(path, "."); + AAFwk::WantParams args; + args.SetParam("target", AAFwk::String::Box("device")); - // Should filter out empty segment - EXPECT_EQ(result.size(), 3UL); - EXPECT_EQ(result[0], "app"); - EXPECT_EQ(result[1], "config"); - EXPECT_EQ(result[2], "name"); + EXPECT_EQ(ToolUtil::ValidateInputSchemaProperties("{invalid json}", args), ERR_NO_INIT); - GTEST_LOG_(INFO) << "ToolUtil_SplitPathBySeparator_0300 end"; + GTEST_LOG_(INFO) << "ToolUtil_ValidateInputSchemaProperties_1200 end"; } /** - * @tc.name: ToolUtil_SplitPathBySeparator_0400 - * @tc.desc: Test SplitPathBySeparator with multiple consecutive separators + * @tc.name: ToolUtil_ValidateInputSchemaProperties_1300 + * @tc.desc: Test ValidateInputSchemaProperties help branch rejects additional args * @tc.type: FUNC */ -HWTEST_F(ToolUtilTest, SplitPathBySeparator_0400, TestSize.Level1) +HWTEST_F(ToolUtilTest, ValidateInputSchemaProperties_1300, TestSize.Level1) { - GTEST_LOG_(INFO) << "ToolUtil_SplitPathBySeparator_0400 start"; + GTEST_LOG_(INFO) << "ToolUtil_ValidateInputSchemaProperties_1300 start"; - std::string path = "app..config...name"; - std::vector result = ToolUtil::SplitPathBySeparator(path, "."); + AAFwk::WantParams args; + args.SetParam("help", AAFwk::Boolean::Box(true)); + args.SetParam("target", AAFwk::String::Box("device")); - // Should filter out all empty segments - EXPECT_EQ(result.size(), 3UL); - EXPECT_EQ(result[0], "app"); - EXPECT_EQ(result[1], "config"); - EXPECT_EQ(result[2], "name"); + std::string schema = R"({"properties":{"help":{"type":"boolean"},"target":{"type":"string"}}})"; + EXPECT_EQ(ToolUtil::ValidateInputSchemaProperties(schema, args), ERR_INVALID_PARAM); - GTEST_LOG_(INFO) << "ToolUtil_SplitPathBySeparator_0400 end"; + GTEST_LOG_(INFO) << "ToolUtil_ValidateInputSchemaProperties_1300 end"; } /** - * @tc.name: ToolUtil_SplitPathBySeparator_0500 - * @tc.desc: Test SplitPathBySeparator with empty string + * @tc.name: ToolUtil_ValidateInputSchemaProperties_1400 + * @tc.desc: Test unknown schema type is allowed for compatibility * @tc.type: FUNC */ -HWTEST_F(ToolUtilTest, SplitPathBySeparator_0500, TestSize.Level1) +HWTEST_F(ToolUtilTest, ValidateInputSchemaProperties_1400, TestSize.Level1) { - GTEST_LOG_(INFO) << "ToolUtil_SplitPathBySeparator_0500 start"; + GTEST_LOG_(INFO) << "ToolUtil_ValidateInputSchemaProperties_1400 start"; - std::string path = ""; - std::vector result = ToolUtil::SplitPathBySeparator(path, "."); + AAFwk::WantParams args; + args.SetParam("target", AAFwk::String::Box("device")); - EXPECT_EQ(result.size(), 0UL); + std::string schema = R"({"properties":{"target":{"type":"custom"}}})"; + EXPECT_EQ(ToolUtil::ValidateInputSchemaProperties(schema, args), ERR_OK); - GTEST_LOG_(INFO) << "ToolUtil_SplitPathBySeparator_0500 end"; + GTEST_LOG_(INFO) << "ToolUtil_ValidateInputSchemaProperties_1400 end"; } /** - * @tc.name: ToolUtil_SplitPathBySeparator_0600 - * @tc.desc: Test SplitPathBySeparator with single segment + * @tc.name: ToolUtil_GenerateCliSessionId_0700 + * @tc.desc: Test GenerateCliSessionId records start time when record is supplied * @tc.type: FUNC */ -HWTEST_F(ToolUtilTest, SplitPathBySeparator_0600, TestSize.Level1) +HWTEST_F(ToolUtilTest, GenerateCliSessionId_0700, TestSize.Level1) { - GTEST_LOG_(INFO) << "ToolUtil_SplitPathBySeparator_0600 start"; + GTEST_LOG_(INFO) << "ToolUtil_GenerateCliSessionId_0700 start"; - std::string path = "app"; - std::vector result = ToolUtil::SplitPathBySeparator(path, "."); + auto record = std::make_shared(); + EXPECT_EQ(record->startTime, 0); - EXPECT_EQ(result.size(), 1UL); - EXPECT_EQ(result[0], "app"); + std::string sessionId = ToolUtil::GenerateCliSessionId("record_tool", record); - GTEST_LOG_(INFO) << "ToolUtil_SplitPathBySeparator_0600 end"; + EXPECT_FALSE(sessionId.empty()); + EXPECT_GT(record->startTime, 0); + + GTEST_LOG_(INFO) << "ToolUtil_GenerateCliSessionId_0700 end"; } /** - * @tc.name: ToolUtil_SplitPathBySeparator_0700 - * @tc.desc: Test SplitPathBySeparator with custom separator + * @tc.name: ToolUtil_NormalizeSkillParamKeys_0200 + * @tc.desc: Test NormalizeSkillParamKeys renames dashed args and preserves existing bare keys * @tc.type: FUNC */ -HWTEST_F(ToolUtilTest, SplitPathBySeparator_0700, TestSize.Level1) +HWTEST_F(ToolUtilTest, NormalizeSkillParamKeys_0200, TestSize.Level1) { - GTEST_LOG_(INFO) << "ToolUtil_SplitPathBySeparator_0700 start"; + GTEST_LOG_(INFO) << "ToolUtil_NormalizeSkillParamKeys_0200 start"; - std::string path = "app-config-name"; - std::vector result = ToolUtil::SplitPathBySeparator(path, "-"); + AAFwk::WantParams args; + args.SetParam("--target", AAFwk::String::Box("device")); + args.SetParam("-force", AAFwk::Boolean::Box(true)); + args.SetParam("--exists", AAFwk::String::Box("prefixed")); + args.SetParam("exists", AAFwk::String::Box("bare")); - EXPECT_EQ(result.size(), 3UL); - EXPECT_EQ(result[0], "app"); - EXPECT_EQ(result[1], "config"); - EXPECT_EQ(result[2], "name"); + ToolUtil::NormalizeSkillParamKeys(args); - GTEST_LOG_(INFO) << "ToolUtil_SplitPathBySeparator_0700 end"; + EXPECT_EQ(args.GetStringParam("target"), "device"); + auto forceValue = AAFwk::IBoolean::Query(args.GetParam("force")); + ASSERT_NE(forceValue, nullptr); + bool force = false; + EXPECT_EQ(forceValue->GetValue(force), ERR_OK); + EXPECT_TRUE(force); + EXPECT_EQ(args.GetStringParam("exists"), "bare"); + EXPECT_TRUE(args.GetStringParam("--target").empty()); + EXPECT_EQ(args.GetStringParam("--exists"), "prefixed"); + + GTEST_LOG_(INFO) << "ToolUtil_NormalizeSkillParamKeys_0200 end"; +} + +/** + * @tc.name: ToolUtil_ExpandArgsFromJson_0300 + * @tc.desc: Test ExpandArgsFromJson expands supported values and skips reserved/object values + * @tc.type: FUNC + */ +HWTEST_F(ToolUtilTest, ExpandArgsFromJson_0300, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "ToolUtil_ExpandArgsFromJson_0300 start"; + + AAFwk::WantParams args; + args.SetParam("args", AAFwk::String::Box("placeholder")); + std::string argsStr = R"({ + "target": "device", + "count": 2, + "enabled": true, + "bundleName": "reserved.bundle", + "nested": {"ignored": true} + })"; + + EXPECT_TRUE(ToolUtil::ExpandArgsFromJson(args, argsStr)); + EXPECT_TRUE(args.GetStringParam("args").empty()); + EXPECT_EQ(args.GetStringParam("target"), "device"); + auto countValue = AAFwk::IInteger::Query(args.GetParam("count")); + ASSERT_NE(countValue, nullptr); + int32_t count = 0; + EXPECT_EQ(countValue->GetValue(count), ERR_OK); + EXPECT_EQ(count, 2); + auto enabledValue = AAFwk::IBoolean::Query(args.GetParam("enabled")); + ASSERT_NE(enabledValue, nullptr); + bool enabled = false; + EXPECT_EQ(enabledValue->GetValue(enabled), ERR_OK); + EXPECT_TRUE(enabled); + EXPECT_TRUE(args.GetStringParam("bundleName").empty()); + EXPECT_TRUE(args.GetStringParam("nested").empty()); + + GTEST_LOG_(INFO) << "ToolUtil_ExpandArgsFromJson_0300 end"; +} + +/** + * @tc.name: ToolUtil_ExpandArgsFromJson_0200 + * @tc.desc: Test ExpandArgsFromJson rejects invalid and non-object json + * @tc.type: FUNC + */ +HWTEST_F(ToolUtilTest, ExpandArgsFromJson_0200, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "ToolUtil_ExpandArgsFromJson_0200 start"; + + AAFwk::WantParams args; + + EXPECT_FALSE(ToolUtil::ExpandArgsFromJson(args, "{invalid json}")); + EXPECT_FALSE(ToolUtil::ExpandArgsFromJson(args, R"(["not", "object"])")); + + GTEST_LOG_(INFO) << "ToolUtil_ExpandArgsFromJson_0200 end"; +} + +/** + * @tc.name: ToolUtil_ExpandArgsFromWantParams_0100 + * @tc.desc: Test ExpandArgsFromWantParams expands nested args and skips reserved keys + * @tc.type: FUNC + */ +HWTEST_F(ToolUtilTest, ExpandArgsFromWantParams_0100, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "ToolUtil_ExpandArgsFromWantParams_0100 start"; + + AAFwk::WantParams nestedArgs; + nestedArgs.SetParam("target", AAFwk::String::Box("device")); + nestedArgs.SetParam("bundleName", AAFwk::String::Box("reserved.bundle")); + + AAFwk::WantParams args; + args.SetParam("args", AAFwk::WantParamWrapper::Box(nestedArgs)); + + ToolUtil::ExpandArgsFromWantParams(args); + + EXPECT_TRUE(args.GetStringParam("args").empty()); + EXPECT_EQ(args.GetStringParam("target"), "device"); + EXPECT_TRUE(args.GetStringParam("bundleName").empty()); + + GTEST_LOG_(INFO) << "ToolUtil_ExpandArgsFromWantParams_0100 end"; +} + +/** + * @tc.name: ToolUtil_ExpandArgsFromWantParams_0200 + * @tc.desc: Test ExpandArgsFromWantParams no-op branches for missing and non-WantParams args + * @tc.type: FUNC + */ +HWTEST_F(ToolUtilTest, ExpandArgsFromWantParams_0200, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "ToolUtil_ExpandArgsFromWantParams_0200 start"; + + AAFwk::WantParams missingArgs; + missingArgs.SetParam("target", AAFwk::String::Box("device")); + ToolUtil::ExpandArgsFromWantParams(missingArgs); + EXPECT_EQ(missingArgs.GetStringParam("target"), "device"); + + AAFwk::WantParams stringArgs; + stringArgs.SetParam("args", AAFwk::String::Box("not nested params")); + ToolUtil::ExpandArgsFromWantParams(stringArgs); + EXPECT_EQ(stringArgs.GetStringParam("args"), "not nested params"); + + GTEST_LOG_(INFO) << "ToolUtil_ExpandArgsFromWantParams_0200 end"; +} + +/** + * @tc.name: ToolUtil_ExpandArgsJsonString_0100 + * @tc.desc: Test ExpandArgsJsonString falls back to nested WantParams when args string is invalid + * @tc.type: FUNC + */ +HWTEST_F(ToolUtilTest, ExpandArgsJsonString_0100, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "ToolUtil_ExpandArgsJsonString_0100 start"; + + AAFwk::WantParams invalidStringArgs; + invalidStringArgs.SetParam("args", AAFwk::String::Box("{invalid json}")); + ToolUtil::ExpandArgsJsonString(invalidStringArgs); + EXPECT_EQ(invalidStringArgs.GetStringParam("args"), "{invalid json}"); + + AAFwk::WantParams nestedArgs; + nestedArgs.SetParam("target", AAFwk::String::Box("fallback-device")); + AAFwk::WantParams args; + args.SetParam("args", AAFwk::WantParamWrapper::Box(nestedArgs)); + ToolUtil::ExpandArgsJsonString(args); + EXPECT_EQ(args.GetStringParam("target"), "fallback-device"); + + GTEST_LOG_(INFO) << "ToolUtil_ExpandArgsJsonString_0100 end"; +} + +/** + * @tc.name: ToolUtil_TransferToCmdParam_0100 + * @tc.desc: Test TransferToCmdParam appends supported params and skips false, null and nested array values + * @tc.type: FUNC + */ +HWTEST_F(ToolUtilTest, TransferToCmdParam_0100, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "ToolUtil_TransferToCmdParam_0100 start"; + + ToolInfo toolInfo; + AAFwk::WantParams emptyArgs; + std::string emptyCmd; + ToolUtil::TransferToCmdParam(toolInfo, emptyArgs, emptyCmd); + EXPECT_TRUE(emptyCmd.empty()); + + AAFwk::WantParams args; + args.SetParam("target", AAFwk::String::Box("device")); + args.SetParam("enabled", AAFwk::Boolean::Box(true)); + args.SetParam("disabled", AAFwk::Boolean::Box(false)); + args.SetParam("count", AAFwk::Integer::Box(3)); + args.SetParam("nullValue", nullptr); + + sptr values = new (std::nothrow) AAFwk::Array(3, AAFwk::g_IID_IString); + ASSERT_NE(values, nullptr); + values->Set(0, AAFwk::String::Box("first").GetRefPtr()); + values->Set(1, AAFwk::String::Box("").GetRefPtr()); + sptr nested = new (std::nothrow) AAFwk::Array(1, AAFwk::g_IID_IString); + ASSERT_NE(nested, nullptr); + nested->Set(0, AAFwk::String::Box("nested").GetRefPtr()); + values->Set(2, nested); + args.SetParam("values", values); + + std::string cmdLine; + ToolUtil::TransferToCmdParam(toolInfo, args, cmdLine); + + EXPECT_NE(cmdLine.find("--target device"), std::string::npos); + EXPECT_NE(cmdLine.find("--enabled"), std::string::npos); + EXPECT_NE(cmdLine.find("--count 3"), std::string::npos); + EXPECT_NE(cmdLine.find("--values first"), std::string::npos); + EXPECT_EQ(cmdLine.find("--disabled"), std::string::npos); + EXPECT_EQ(cmdLine.find("nullValue"), std::string::npos); + EXPECT_EQ(cmdLine.find("nested"), std::string::npos); + + GTEST_LOG_(INFO) << "ToolUtil_TransferToCmdParam_0100 end"; +} + +/** + * @tc.name: ToolUtil_FilterSkillArgs_0100 + * @tc.desc: Test FilterSkillArgs removes reserved skill keys + * @tc.type: FUNC + */ +HWTEST_F(ToolUtilTest, FilterSkillArgs_0100, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "ToolUtil_FilterSkillArgs_0100 start"; + + AAFwk::WantParams args; + args.SetParam("bundleName", AAFwk::String::Box("reserved.bundle")); + args.SetParam("target", AAFwk::String::Box("device")); + args.SetParam("moduleName", AAFwk::String::Box("module")); + + auto filteredArgs = ToolUtil::FilterSkillArgs(args); + + ASSERT_NE(filteredArgs, nullptr); + EXPECT_EQ(filteredArgs->GetStringParam("target"), "device"); + EXPECT_TRUE(filteredArgs->GetStringParam("bundleName").empty()); + EXPECT_TRUE(filteredArgs->GetStringParam("moduleName").empty()); + + GTEST_LOG_(INFO) << "ToolUtil_FilterSkillArgs_0100 end"; +} + +/** + * @tc.name: ToolUtil_BuildSkillSessionInfo_0100 + * @tc.desc: Test BuildSkillSessionInfo success and failure status branches + * @tc.type: FUNC + */ +HWTEST_F(ToolUtilTest, BuildSkillSessionInfo_0100, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "ToolUtil_BuildSkillSessionInfo_0100 start"; + + AppExecFwk::SkillExecuteResult skillResult; + skillResult.code = 12; + skillResult.result = std::make_shared(); + skillResult.result->SetParam("output", AAFwk::String::Box("skill result")); + + CliSessionInfo completed = ToolUtil::BuildSkillSessionInfo("session", ERR_OK, skillResult); + EXPECT_EQ(completed.sessionId, "session"); + EXPECT_EQ(completed.toolName, "ohos-arkTSScript"); + EXPECT_EQ(completed.status, "completed"); + ASSERT_NE(completed.result, nullptr); + EXPECT_EQ(completed.result->exitCode, 12); + EXPECT_FALSE(completed.result->outputText.empty()); + + AppExecFwk::SkillExecuteResult failedSkillResult; + failedSkillResult.code = -1; + CliSessionInfo failed = ToolUtil::BuildSkillSessionInfo("failed-session", ERR_INVALID_PARAM, failedSkillResult); + EXPECT_EQ(failed.status, "failed"); + ASSERT_NE(failed.result, nullptr); + EXPECT_EQ(failed.result->exitCode, -1); + EXPECT_TRUE(failed.result->outputText.empty()); + + GTEST_LOG_(INFO) << "ToolUtil_BuildSkillSessionInfo_0100 end"; +} + +/** + * @tc.name: ToolUtil_NormalizeSkillParamKeys_0300 + * @tc.desc: Test NormalizeSkillParamKeys strips prefixes and does not overwrite existing bare keys + * @tc.type: FUNC + */ +HWTEST_F(ToolUtilTest, NormalizeSkillParamKeys_0300, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "ToolUtil_NormalizeSkillParamKeys_0300 start"; + + AAFwk::WantParams args; + args.SetParam("--foo", AAFwk::String::Box("double")); + args.SetParam("-b", AAFwk::String::Box("single")); + args.SetParam("bare", AAFwk::String::Box("existing")); + // --bare should not overwrite existing bare key + args.SetParam("--bare", AAFwk::String::Box("prefixed")); + + ToolUtil::NormalizeSkillParamKeys(args); + + EXPECT_EQ(args.GetStringParam("foo"), "double"); + EXPECT_EQ(args.GetStringParam("b"), "single"); + EXPECT_EQ(args.GetStringParam("bare"), "existing"); + // Existing bare keys are not overwritten by prefixed aliases. + EXPECT_EQ(args.GetParams().count("--bare"), 1u); + + GTEST_LOG_(INFO) << "ToolUtil_NormalizeSkillParamKeys_0300 end"; +} + +/** + * @tc.name: ToolUtil_ExpandArgsFromJson_0400 + * @tc.desc: Test ExpandArgsFromJson rejects invalid/non-object, copies valid types, skips reserved + * @tc.type: FUNC + */ +HWTEST_F(ToolUtilTest, ExpandArgsFromJson_0400, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "ToolUtil_ExpandArgsFromJson_0400 start"; + + AAFwk::WantParams args; + EXPECT_FALSE(ToolUtil::ExpandArgsFromJson(args, "{invalid}")); + EXPECT_FALSE(ToolUtil::ExpandArgsFromJson(args, "42")); + EXPECT_FALSE(ToolUtil::ExpandArgsFromJson(args, "")); + + EXPECT_TRUE(ToolUtil::ExpandArgsFromJson(args, R"({"key":"value","count":5,"flag":true})")); + EXPECT_EQ(args.GetStringParam("key"), "value"); + + AAFwk::WantParams reservedArgs; + EXPECT_TRUE(ToolUtil::ExpandArgsFromJson(reservedArgs, + R"({"bundleName":"x","moduleName":"y","myArg":"val"})")); + EXPECT_EQ(reservedArgs.GetStringParam("bundleName"), ""); + EXPECT_EQ(reservedArgs.GetStringParam("myArg"), "val"); + + GTEST_LOG_(INFO) << "ToolUtil_ExpandArgsFromJson_0400 end"; +} + +/** + * @tc.name: ToolUtil_ValidateInputSchemaProperties_1500 + * @tc.desc: Test ValidateInputSchemaProperties with invalid schema, missing properties, help, unknown key + * @tc.type: FUNC + */ +HWTEST_F(ToolUtilTest, ValidateInputSchemaProperties_1500, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "ToolUtil_ValidateInputSchemaProperties_1500 start"; + + AAFwk::WantParams args; + args.SetParam("key", AAFwk::String::Box("val")); + + // non-empty args with empty schema + EXPECT_EQ(ToolUtil::ValidateInputSchemaProperties("", args), ERR_INVALID_PARAM); + + // invalid JSON schema + EXPECT_EQ(ToolUtil::ValidateInputSchemaProperties("{bad}", args), ERR_NO_INIT); + + // schema missing properties + EXPECT_EQ(ToolUtil::ValidateInputSchemaProperties(R"({"type":"object"})", args), ERR_INVALID_PARAM); + + // help alone -> OK + AAFwk::WantParams helpArgs; + helpArgs.SetParam("help", AAFwk::String::Box("")); + EXPECT_EQ(ToolUtil::ValidateInputSchemaProperties( + R"({"properties":{}})", helpArgs), ERR_OK); + + // help plus another arg -> error + AAFwk::WantParams helpPlusArgs; + helpPlusArgs.SetParam("help", AAFwk::String::Box("")); + helpPlusArgs.SetParam("extra", AAFwk::String::Box("val")); + EXPECT_EQ(ToolUtil::ValidateInputSchemaProperties( + R"({"properties":{}})", helpPlusArgs), ERR_INVALID_PARAM); + + // arg key not in schema properties + EXPECT_EQ(ToolUtil::ValidateInputSchemaProperties( + R"({"properties":{"other":{"type":"string"}}})", args), ERR_INVALID_PARAM); + + GTEST_LOG_(INFO) << "ToolUtil_ValidateInputSchemaProperties_1500 end"; +} + +/** + * @tc.name: ToolUtil_ValidateInputSchemaProperties_1600 + * @tc.desc: Test type validation: matching types and mismatched types + * @tc.type: FUNC + */ +HWTEST_F(ToolUtilTest, ValidateInputSchemaProperties_1600, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "ToolUtil_ValidateInputSchemaProperties_1600 start"; + + const std::string schema = R"({ + "properties": { + "str": {"type": "string"}, + "bool": {"type": "boolean"}, + "int": {"type": "integer"}, + "num": {"type": "number"}, + "arr": {"type": "array"}, + "unknown_type": {"type": "custom"} + } + })"; + + // matching types + AAFwk::WantParams validArgs; + validArgs.SetParam("str", AAFwk::String::Box("hello")); + validArgs.SetParam("bool", AAFwk::Boolean::Box(true)); + validArgs.SetParam("int", AAFwk::Integer::Box(42)); + EXPECT_EQ(ToolUtil::ValidateInputSchemaProperties(schema, validArgs), ERR_OK); + + // type mismatch: bool where string expected + AAFwk::WantParams mismatchArgs; + mismatchArgs.SetParam("str", AAFwk::Boolean::Box(true)); + EXPECT_EQ(ToolUtil::ValidateInputSchemaProperties(schema, mismatchArgs), ERR_INVALID_PARAM); + + // type mismatch: string where bool expected + AAFwk::WantParams mismatchArgs2; + mismatchArgs2.SetParam("bool", AAFwk::String::Box("not_bool")); + EXPECT_EQ(ToolUtil::ValidateInputSchemaProperties(schema, mismatchArgs2), ERR_INVALID_PARAM); + + // type mismatch: string where int expected + AAFwk::WantParams mismatchArgs3; + mismatchArgs3.SetParam("int", AAFwk::String::Box("not_int")); + EXPECT_EQ(ToolUtil::ValidateInputSchemaProperties(schema, mismatchArgs3), ERR_INVALID_PARAM); + + // unknown type returns true (accepted) + AAFwk::WantParams unknownArgs; + unknownArgs.SetParam("unknown_type", AAFwk::String::Box("anything")); + EXPECT_EQ(ToolUtil::ValidateInputSchemaProperties(schema, unknownArgs), ERR_OK); + + GTEST_LOG_(INFO) << "ToolUtil_ValidateInputSchemaProperties_1600 end"; } } // namespace CliTool diff --git a/ets_environment/frameworks/ets_environment/src/ets_environment.cpp b/ets_environment/frameworks/ets_environment/src/ets_environment.cpp index 4fe8b98dfb..563cf4e5f2 100644 --- a/ets_environment/frameworks/ets_environment/src/ets_environment.cpp +++ b/ets_environment/frameworks/ets_environment/src/ets_environment.cpp @@ -458,11 +458,16 @@ bool ETSEnvironment::InitAbcLinker(ani_env *env) } ani_status status = ANI_ERROR; - if ((status = env->FindClass(CLASSNAME_LINKER, &vmEntry_.abcLinkerClass_)) != ANI_OK) { + ani_class abcLinkerClass {}; + if ((status = env->FindClass(CLASSNAME_LINKER, &abcLinkerClass)) != ANI_OK) { TAG_LOGE(AAFwkTag::ETSRUNTIME, "FindClass failed, status: %{public}d", status); return false; } - + status = env->GlobalReference_Create(abcLinkerClass, reinterpret_cast(&vmEntry_.abcLinkerClass_)); + if (status != ANI_OK) { + TAG_LOGE(AAFwkTag::ETSRUNTIME, "GlobalReference_Create failed, status: %{public}d", status); + return false; + } ani_ref undefinedRef = nullptr; if ((status = env->GetUndefined(&undefinedRef)) != ANI_OK) { TAG_LOGE(AAFwkTag::ETSRUNTIME, "GetUndefined failed, status: %{public}d", status); @@ -600,7 +605,12 @@ bool ETSEnvironment::LoadModule(const std::string &modulePath, const std::string } ani_ref clsRef = nullptr; ani_class clsAni = nullptr; - if ((status = env->Object_CallMethod_Ref(abcObj, loadClassMethod, &clsRef, clsStr, false)) != ANI_OK) { + ani_object boolObj = nullptr; + if ((status = env->Primitive_Box_Boolean(ANI_FALSE, &boolObj)) != ANI_OK) { + TAG_LOGE(AAFwkTag::ETSRUNTIME, "Primitive_Box_Boolean failed, status: %{public}d", status); + return false; + } + if ((status = env->Object_CallMethod_Ref(abcObj, loadClassMethod, &clsRef, clsStr, boolObj)) != ANI_OK) { TAG_LOGE(AAFwkTag::ETSRUNTIME, "Object_CallMethod_Ref failed, status: %{public}d", status); return false; } diff --git a/frameworks/c/ability_runtime/BUILD.gn b/frameworks/c/ability_runtime/BUILD.gn index 1a8b8982fd..9c723c3556 100644 --- a/frameworks/c/ability_runtime/BUILD.gn +++ b/frameworks/c/ability_runtime/BUILD.gn @@ -93,6 +93,7 @@ ohos_shared_library("ability_runtime") { "ability_base:ability_base_want", "ability_base:want", "c_utils:utils", + "eventhandler:libeventhandler", "ffrt:libffrt", "hilog:libhilog", "image_framework:image_native", diff --git a/frameworks/c/ability_runtime/include/modular_object_connection_manager.h b/frameworks/c/ability_runtime/include/modular_object_connection_manager.h index cdd23c9809..5366d7c0d7 100644 --- a/frameworks/c/ability_runtime/include/modular_object_connection_manager.h +++ b/frameworks/c/ability_runtime/include/modular_object_connection_manager.h @@ -45,22 +45,16 @@ struct ModularObjectConnectionInfo { bool operator<(const ModularObjectConnectionInfo &that) const { - if (abilityConnection < that.abilityConnection) { - return true; + if (abilityConnection != that.abilityConnection) { + return abilityConnection < that.abilityConnection; } - if (connectReceiver.GetBundleName() < that.connectReceiver.GetBundleName()) { - return true; + if (connectReceiver.GetBundleName() != that.connectReceiver.GetBundleName()) { + return connectReceiver.GetBundleName() < that.connectReceiver.GetBundleName(); } - if (connectReceiver.GetBundleName() == that.connectReceiver.GetBundleName() && - connectReceiver.GetModuleName() < that.connectReceiver.GetModuleName()) { - return true; + if (connectReceiver.GetModuleName() != that.connectReceiver.GetModuleName()) { + return connectReceiver.GetModuleName() < that.connectReceiver.GetModuleName(); } - if (connectReceiver.GetBundleName() == that.connectReceiver.GetBundleName() && - connectReceiver.GetModuleName() == that.connectReceiver.GetModuleName() && - connectReceiver.GetAbilityName() < that.connectReceiver.GetAbilityName()) { - return true; - } - return false; + return connectReceiver.GetAbilityName() < that.connectReceiver.GetAbilityName(); } }; diff --git a/frameworks/c/ability_runtime/src/c_modular_object_connection_callback.cpp b/frameworks/c/ability_runtime/src/c_modular_object_connection_callback.cpp index 117b2d9061..dbc66b45a4 100644 --- a/frameworks/c/ability_runtime/src/c_modular_object_connection_callback.cpp +++ b/frameworks/c/ability_runtime/src/c_modular_object_connection_callback.cpp @@ -152,6 +152,7 @@ void CModularObjectConnectionCallback::OnAbilityDisconnectDone(const AppExecFwk: if (callback == nullptr) { TAG_LOGW(AAFwkTag::EXT, "callback null"); + CModularObjectConnectionUtils::RemoveConnectionCallback(connectionId_); return; } AbilityBase_Element cElement; @@ -161,6 +162,7 @@ void CModularObjectConnectionCallback::OnAbilityDisconnectDone(const AppExecFwk: } else { CModularObjectUtils::NotifyFailed(state, ABILITY_RUNTIME_ERROR_CODE_INTERNAL); } + CModularObjectConnectionUtils::RemoveConnectionCallback(connectionId_); } } // namespace AbilityRuntime } // namespace OHOS diff --git a/frameworks/c/ability_runtime/src/c_modular_object_utils.cpp b/frameworks/c/ability_runtime/src/c_modular_object_utils.cpp index 4a0895891f..e2091aa8a9 100644 --- a/frameworks/c/ability_runtime/src/c_modular_object_utils.cpp +++ b/frameworks/c/ability_runtime/src/c_modular_object_utils.cpp @@ -38,12 +38,16 @@ AbilityRuntime_ErrorCode CModularObjectUtils::ConvertConnectBusinessErrorCode(in return ABILITY_RUNTIME_ERROR_CODE_CROSS_USER_OPERATION; case ERR_FREQ_START_ABILITY: return ABILITY_RUNTIME_ERROR_CODE_UPPER_RATE_LIMIT; + case ERR_MOE_CONNECTION_LIMIT: + return ABILITY_RUNTIME_ERROR_CODE_UPPER_CONNECTION_NUMBER_LIMIT; + case ERR_MOE_INSTANCE_LIMIT: + return ABILITY_RUNTIME_ERROR_CODE_UPPER_LIMIT_REACHED; case ERR_MODULAR_OBJECT_DISABLED: return ABILITY_RUNTIME_ERROR_CODE_NO_SUCH_ABILITY; case ERR_NO_RUNNING_ABILITIES_WITH_UI: return ABILITY_RUNTIME_ERROR_CODE_NO_RUNNING_ABILITIES_WITH_UI; - case ERR_INVALID_DISTRIBUTION_TYPE: - return ABILITY_RUNTIME_ERROR_CODE_INVALID_DISTRIBUTION_TYPE; + case ERR_MOE_CROSS_APP_IN_PROCESS: + return ABILITY_RUNTIME_ERROR_CODE_CROSS_APP_IN_PROCESS; default: return ConvertToCommonBusinessErrorCode(errCode); } diff --git a/frameworks/c/ability_runtime/src/modular_object_extension_context.cpp b/frameworks/c/ability_runtime/src/modular_object_extension_context.cpp index c3d7eae8eb..690bb272cf 100644 --- a/frameworks/c/ability_runtime/src/modular_object_extension_context.cpp +++ b/frameworks/c/ability_runtime/src/modular_object_extension_context.cpp @@ -15,9 +15,13 @@ #include "modular_object_extension_context.h" +#include +#include + #include "ability_business_error_utils.h" #include "ability_manager_client.h" #include "hilog_tag_wrapper.h" +#include "ipc_error_code.h" #include "modular_object_extension_context_impl.h" #include "modular_object_extension_types.h" #include "start_options_impl.h" @@ -29,6 +33,16 @@ using namespace OHOS::AAFwk; using namespace OHOS::AbilityRuntime; namespace { +constexpr const char *REQUEST_TASK_NAME = "ModObjExtRequest"; +constexpr const char *DESTROY_TASK_NAME = "ModObjExtDestroy"; + +struct IPCRemoteStubUserData { + std::weak_ptr handler; + OH_OnRemoteRequestCallback requestCallback = nullptr; + OH_OnRemoteDestroyCallback destroyCallback = nullptr; + void *userData = nullptr; +}; + AbilityRuntime_ErrorCode CheckMoeContext(OH_AbilityRuntime_ModObjExtensionContextHandle context, std::shared_ptr &contextPtr) { @@ -62,6 +76,67 @@ AbilityRuntime_ErrorCode TransformWant(const AbilityBase_Want *want, Want &abili } return ABILITY_RUNTIME_ERROR_CODE_NO_ERROR; } + +std::unique_ptr CreateIPCRemoteStubUserData( + const std::shared_ptr &handler, + OH_OnRemoteRequestCallback requestCallback, OH_OnRemoteDestroyCallback destroyCallback, + void *userData) +{ + std::unique_ptr callbackInfo(new (std::nothrow) IPCRemoteStubUserData()); + if (callbackInfo == nullptr) { + return nullptr; + } + callbackInfo->handler = handler; + callbackInfo->requestCallback = requestCallback; + callbackInfo->destroyCallback = destroyCallback; + callbackInfo->userData = userData; + return callbackInfo; +} + +int OnRemoteRequestOnHandler(uint32_t code, const OHIPCParcel *data, OHIPCParcel *reply, void *userData) +{ + auto *callbackInfo = static_cast(userData); + if (callbackInfo == nullptr || callbackInfo->requestCallback == nullptr) { + return OH_IPC_INNER_ERROR; + } + auto handler = callbackInfo->handler.lock(); + if (handler == nullptr) { + TAG_LOGE(AAFwkTag::APPKIT, "event handler not exist"); + return OH_IPC_INNER_ERROR; + } + int32_t result = OH_IPC_INNER_ERROR; + auto task = [&callbackInfo, &result, code, data, reply]() { + result = callbackInfo->requestCallback(code, data, reply, callbackInfo->userData); + }; + if (!handler->PostSyncTask(task, REQUEST_TASK_NAME)) { + TAG_LOGE(AAFwkTag::APPKIT, "post request task failed"); + return OH_IPC_INNER_ERROR; + } + return result; +} + +void OnRemoteDestroyOnHandler(void *userData) +{ + std::unique_ptr callbackInfo(static_cast(userData)); + if (callbackInfo == nullptr) { + return; + } + if (callbackInfo->destroyCallback == nullptr) { + return; + } + auto handler = callbackInfo->handler.lock(); + if (handler == nullptr) { + TAG_LOGE(AAFwkTag::APPKIT, "event handler not exist"); + return; + } + auto task = [&callbackInfo]() { + callbackInfo->destroyCallback(callbackInfo->userData); + }; + if (!handler->PostSyncTask(task, DESTROY_TASK_NAME)) { + TAG_LOGE(AAFwkTag::APPKIT, "post destroy task failed"); + return; + } +} } // namespace #ifdef __cplusplus @@ -138,6 +213,43 @@ AbilityRuntime_ErrorCode OH_AbilityRuntime_ModObjExtensionContext_TerminateSelf( return ConvertToCommonBusinessErrorCode(err); } +OHIPCRemoteStub* OH_AbilityRuntime_ModObjExtensionContext_CreateIPCRemoteStub( + OH_AbilityRuntime_ModObjExtensionContextHandle context, const char *descriptor, + OH_OnRemoteRequestCallback requestCallback, OH_OnRemoteDestroyCallback destroyCallback, void *userData) +{ + if (descriptor == nullptr || requestCallback == nullptr) { + TAG_LOGE(AAFwkTag::APPKIT, "invalid create stub params"); + return nullptr; + } + std::shared_ptr contextPtr; + auto ret = CheckMoeContext(context, contextPtr); + if (ret != ABILITY_RUNTIME_ERROR_CODE_NO_ERROR) { + return nullptr; + } + auto moeContext = std::static_pointer_cast(contextPtr); + auto callbackInfo = CreateIPCRemoteStubUserData( + moeContext->GetEventHandler(), requestCallback, destroyCallback, userData); + if (callbackInfo == nullptr) { + TAG_LOGE(AAFwkTag::APPKIT, "create callback info failed"); + return nullptr; + } + auto *stub = OH_IPCRemoteStub_Create(descriptor, OnRemoteRequestOnHandler, + OnRemoteDestroyOnHandler, callbackInfo.get()); + if (stub == nullptr) { + TAG_LOGE(AAFwkTag::APPKIT, "create remote stub failed"); + return nullptr; + } + callbackInfo.release(); + return stub; +} + +void OH_AbilityRuntime_ModObjExtensionContext_DestroyIPCRemoteStub( + OH_AbilityRuntime_ModObjExtensionContextHandle context, OHIPCRemoteStub *stub) +{ + (void)context; + OH_IPCRemoteStub_Destroy(stub); +} + #ifdef __cplusplus } // extern "C" #endif diff --git a/frameworks/c/ability_runtime/src/native_ability_wrapper.cpp b/frameworks/c/ability_runtime/src/native_ability_wrapper.cpp index e37cfd37c4..b142c81d7d 100644 --- a/frameworks/c/ability_runtime/src/native_ability_wrapper.cpp +++ b/frameworks/c/ability_runtime/src/native_ability_wrapper.cpp @@ -29,7 +29,7 @@ extern "C" { * @brief Get ability instance ID from NativeAbilityWrapper. */ AbilityRuntime_ErrorCode OH_AbilityRuntime_GetAbilityInstanceId( - const NativeAbilityWrapper* nativeAbilityWrapper, char* buffer, const int32_t bufferSize) + const AbilityRuntime_NativeAbilityWrapper* nativeAbilityWrapper, char* buffer, const int32_t bufferSize) { constexpr int32_t MIN_BUFFER_SIZE = 37; // UUID format: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx + '\0' if (nativeAbilityWrapper == nullptr || buffer == nullptr) { @@ -63,7 +63,8 @@ AbilityRuntime_ErrorCode OH_AbilityRuntime_GetAbilityInstanceId( * @brief Get ability name from NativeAbilityWrapper. */ AbilityRuntime_ErrorCode OH_AbilityRuntime_GetAbilityName( - const NativeAbilityWrapper* nativeAbilityWrapper, char* buffer, const int32_t bufferSize, int32_t* writeLength) + const AbilityRuntime_NativeAbilityWrapper *nativeAbilityWrapper, char *buffer, const int32_t bufferSize, + int32_t *writeLength) { if (nativeAbilityWrapper == nullptr || writeLength == nullptr) { TAG_LOGE(AAFwkTag::APPKIT, "Invalid parameter: nativeAbilityWrapper or writeLength is null"); @@ -72,7 +73,7 @@ AbilityRuntime_ErrorCode OH_AbilityRuntime_GetAbilityName( if (nativeAbilityWrapper->abilityName.empty()) { TAG_LOGE(AAFwkTag::APPKIT, "Ability name is empty"); - return ABILITY_RUNTIME_ERROR_CODE_CONTEXT_NOT_EXIST; + return ABILITY_RUNTIME_ERROR_CODE_ABILITY_WRAPPER_INVALID; } int32_t nameLength = static_cast(nativeAbilityWrapper->abilityName.length()); @@ -111,7 +112,7 @@ AbilityRuntime_ErrorCode OH_AbilityRuntime_GetAbilityName( * @brief Get napi_env from NativeAbilityWrapper. */ AbilityRuntime_ErrorCode OH_AbilityRuntime_GetEnv( - const NativeAbilityWrapper* nativeAbilityWrapper, napi_env* env) + const AbilityRuntime_NativeAbilityWrapper* nativeAbilityWrapper, napi_env* env) { if (nativeAbilityWrapper == nullptr || env == nullptr) { TAG_LOGE(AAFwkTag::APPKIT, "Invalid parameter: nativeAbilityWrapper or env is null"); @@ -120,7 +121,7 @@ AbilityRuntime_ErrorCode OH_AbilityRuntime_GetEnv( if (nativeAbilityWrapper->env == nullptr) { TAG_LOGE(AAFwkTag::APPKIT, "napi_env in NativeAbilityWrapper is null"); - return ABILITY_RUNTIME_ERROR_CODE_CONTEXT_NOT_EXIST; + return ABILITY_RUNTIME_ERROR_CODE_ABILITY_WRAPPER_INVALID; } *env = nativeAbilityWrapper->env; diff --git a/frameworks/ets/ani/ability_manager/src/ets_ability_manager.cpp b/frameworks/ets/ani/ability_manager/src/ets_ability_manager.cpp index eb0b9084bc..4f78e78bf6 100644 --- a/frameworks/ets/ani/ability_manager/src/ets_ability_manager.cpp +++ b/frameworks/ets/ani/ability_manager/src/ets_ability_manager.cpp @@ -891,7 +891,7 @@ void EtsAbilityManager::NativeOffPreloadedUIExtensionAbilityLoaded(ani_env *env, loadedCallback_.clear(); return; } - auto it = std::find_if(loadedCallback_.begin(), loadedCallback_.end(), [&](const auto &cb) { + auto it = std::find_if(loadedCallback_.begin(), loadedCallback_.end(), [env, callback](const auto &cb) { ani_boolean isEquals = ANI_FALSE; env->Reference_StrictEquals(callback, cb.first, &isEquals); return isEquals; @@ -962,7 +962,7 @@ void EtsAbilityManager::NativeOffPreloadedUIExtensionAbilityDestroyed(ani_env *e destroyCallback_.clear(); return; } - auto it = std::find_if(destroyCallback_.begin(), destroyCallback_.end(), [&](const auto &cb) { + auto it = std::find_if(destroyCallback_.begin(), destroyCallback_.end(), [env, callback](const auto &cb) { ani_boolean isEquals = ANI_FALSE; env->Reference_StrictEquals(callback, cb.first, &isEquals); return isEquals; diff --git a/frameworks/ets/ani/ani_common/include/ani_common_util.h b/frameworks/ets/ani/ani_common/include/ani_common_util.h index 4a6d0898f7..03633e45c7 100644 --- a/frameworks/ets/ani/ani_common/include/ani_common_util.h +++ b/frameworks/ets/ani/ani_common/include/ani_common_util.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 Huawei Device Co., Ltd. + * Copyright (c) 2025-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 @@ -94,6 +94,8 @@ bool IsValidProperty(ani_env *env, ani_ref param); bool CheckCallerIsSystemApp(); ani_object WrapLocale(ani_env *env, const std::string &locale); ani_object CreateIntAniArray(ani_env *env, const std::vector &dataArry); +bool CreateObjectByClassName(ani_env *env, const char *className, ani_object &object); +bool CreateArrayObject(ani_env *env, ani_object &object, size_t length); } // namespace AppExecFwk } // namespace OHOS #endif // OHOS_ABILITY_RUNTIME_ANI_COMMON_UTIL_H diff --git a/frameworks/ets/ani/ani_common/include/ets_application_context_utils.h b/frameworks/ets/ani/ani_common/include/ets_application_context_utils.h index 67484c20bf..06b0befead 100644 --- a/frameworks/ets/ani/ani_common/include/ets_application_context_utils.h +++ b/frameworks/ets/ani/ani_common/include/ets_application_context_utils.h @@ -18,6 +18,7 @@ #include "ani.h" #include "ability_manager_client.h" +#include "ability_native_thread.h" #include "ani_common_util.h" #include "ani_common_want.h" #include "application_context.h" @@ -59,6 +60,9 @@ public: static ani_string GetCurrentInstanceKey(ani_env *env, ani_object aniObj); static void GetAllRunningInstanceKeys(ani_env *env, ani_object aniObj, ani_object callback); static void GetAllWindowStages(ani_env *env, ani_object aniObj, ani_object callback); + static void EnableDelayedProcessExit(ani_env *env, ani_object aniObj, ani_object callback); + static void DisableDelayedProcessExit(ani_env *env, ani_object aniObj, ani_object callback); + static void StartSelfUIAbility(ani_env *env, ani_object aniObj, ani_object wantObj, ani_object callback); static ani_int NativeOnLifecycleCallbackSync(ani_env *env, ani_object aniObj, ani_string type, ani_object callback); static void NativeOffLifecycleCallbackSync(ani_env *env, ani_object aniObj, ani_string type, @@ -72,6 +76,7 @@ public: static void NativeOffEnvironmentSync(ani_env *env, ani_object aniObj, ani_int callbackId, ani_object callback); static void NativeOffEnvironmentCheck(ani_env *env, ani_object aniObj); static ani_int NativeOnEnvironmentSync(ani_env *env, ani_object aniObj, ani_object envCallback); + static ani_object GetUIAbilityByInstanceId(ani_env *env, ani_object aniObj, ani_string instanceId); protected: std::weak_ptr applicationContext_; private: @@ -99,8 +104,12 @@ private: void OnNativeSystemConfigurationUpdatedSync(ani_env *env, ani_object aniObj, ani_object callback); void OffNativeSystemConfigurationUpdatedSync(ani_env *env, ani_object aniObj, ani_object callback); ani_int OnNativeOnEnvironmentSync(ani_env *env, ani_object aniObj, ani_object envCallback); + ani_object OnGetUIAbilityByInstanceId(ani_env *env, ani_string instanceId); static void SetEventHubContextIsApplicationContext(ani_env *aniEnv, ani_ref eventHubRef); ani_object CreateWindowStageArray(ani_env *env, std::vector> uiAbility); + void OnEnableDelayedProcessExit(ani_env *env, ani_object aniObj, ani_object callback); + void OnDisableDelayedProcessExit(ani_env *env, ani_object aniObj, ani_object callback); + void OnStartSelfUIAbility(ani_env *env, ani_object aniObj, ani_object wantObj, ani_object callback); std::shared_ptr etsEnviromentCallback_; std::shared_ptr applicationStateCallback_; std::shared_ptr systemConfigurationUpdatedCallback_; diff --git a/frameworks/ets/ani/ani_common/src/ani_common_execute_param.cpp b/frameworks/ets/ani/ani_common/src/ani_common_execute_param.cpp index f220abb8e6..a2edbde7ef 100644 --- a/frameworks/ets/ani/ani_common/src/ani_common_execute_param.cpp +++ b/frameworks/ets/ani/ani_common/src/ani_common_execute_param.cpp @@ -63,6 +63,15 @@ bool UnwrapExecuteParam(ani_env *env, ani_object param, AppExecFwk::InsightInten } executeParam.insightIntentName_ = insightIntentName; + if (IsExistsProperty(env, param, "deviceId")) { + std::string deviceId {""}; + if (!GetStringProperty(env, param, "deviceId", deviceId)) { + TAG_LOGE(AAFwkTag::INTENT, "Wrong argument type deviceId"); + return false; + } + executeParam.deviceId_ = deviceId; + } + ani_ref aniIntentParam = nullptr; if (!GetRefProperty(env, param, "insightIntentParam", aniIntentParam)) { TAG_LOGE(AAFwkTag::INTENT, "null aniIntentParam"); diff --git a/frameworks/ets/ani/ani_common/src/ani_common_util.cpp b/frameworks/ets/ani/ani_common/src/ani_common_util.cpp index 70c02874c5..e5f0931127 100644 --- a/frameworks/ets/ani/ani_common/src/ani_common_util.cpp +++ b/frameworks/ets/ani/ani_common/src/ani_common_util.cpp @@ -1605,5 +1605,55 @@ ani_object CreateIntAniArray(ani_env *env, const std::vector &dataArry) } return arrayObj; } + +bool CreateObjectByClassName(ani_env *env, const char *className, ani_object &object) +{ + if (env == nullptr) { + TAG_LOGE(AAFwkTag::ANI, "env null"); + return false; + } + + ani_class cls = nullptr; + ani_status status = env->FindClass(className, &cls); + if (status != ANI_OK || cls == nullptr) { + TAG_LOGE(AAFwkTag::ANI, "FindClass status: %{public}d", status); + return false; + } + ani_method method = nullptr; + if ((status = env->Class_FindMethod(cls, "", ":", &method)) != ANI_OK || method == nullptr) { + TAG_LOGE(AAFwkTag::ANI, "Class_FindMethod status: %{public}d", status); + return false; + } + if ((status = env->Object_New(cls, method, &object)) != ANI_OK || object == nullptr) { + TAG_LOGE(AAFwkTag::ANI, "Object_New status: %{public}d", status); + return false; + } + return true; +} + +bool CreateArrayObject(ani_env *env, ani_object &object, size_t length) +{ + if (env == nullptr) { + TAG_LOGE(AAFwkTag::ANI, "env null"); + return false; + } + + ani_class cls = nullptr; + ani_status status = env->FindClass(CLASSNAME_ARRAY, &cls); + if (status != ANI_OK || cls == nullptr) { + TAG_LOGE(AAFwkTag::ANI, "FindClass status: %{public}d", status); + return false; + } + ani_method method = nullptr; + if ((status = env->Class_FindMethod(cls, "", "i:", &method)) != ANI_OK || method == nullptr) { + TAG_LOGE(AAFwkTag::ANI, "Class_FindMethod status: %{public}d", status); + return false; + } + if ((status = env->Object_New(cls, method, &object, length)) != ANI_OK || object == nullptr) { + TAG_LOGE(AAFwkTag::ANI, "Object_New status: %{public}d", status); + return false; + } + return true; +} } // namespace AppExecFwk } // namespace OHOS diff --git a/frameworks/ets/ani/ani_common/src/ets_application_context_utils.cpp b/frameworks/ets/ani/ani_common/src/ets_application_context_utils.cpp index 824e4dd4a8..27f778dc0d 100644 --- a/frameworks/ets/ani/ani_common/src/ets_application_context_utils.cpp +++ b/frameworks/ets/ani/ani_common/src/ets_application_context_utils.cpp @@ -100,6 +100,44 @@ ani_int EtsApplicationContextUtils::OnNativeOnEnvironmentSync(ani_env *env, ani_ return callbackId; } +ani_object EtsApplicationContextUtils::OnGetUIAbilityByInstanceId(ani_env *env, ani_string instanceId) +{ + TAG_LOGD(AAFwkTag::APPKIT, "OnGetUIAbilityByInstanceId called"); + if (env == nullptr) { + TAG_LOGE(AAFwkTag::APPKIT, "null env"); + return nullptr; + } + auto applicationContext = applicationContext_.lock(); + if (applicationContext == nullptr) { + TAG_LOGE(AAFwkTag::APPKIT, "null applicationContext"); + EtsErrorUtil::ThrowRuntimeError(env, ERR_ABILITY_RUNTIME_EXTERNAL_CONTEXT_NOT_EXIST); + return nullptr; + } + + std::string instanceIdStr; + if (!AppExecFwk::GetStdString(env, instanceId, instanceIdStr)) { + TAG_LOGE(AAFwkTag::APPKIT, "Parse instanceId failed"); + EtsErrorUtil::ThrowInvalidParamError(env, "Parse param instanceId failed, instanceId must be string."); + return nullptr; + } + + auto nativeAbility = applicationContext->GetNativeAbility(instanceIdStr); + if (nativeAbility == nullptr) { + TAG_LOGE(AAFwkTag::APPKIT, "NativeAbility not found for instanceId: %{public}s", instanceIdStr.c_str()); + EtsErrorUtil::ThrowRuntimeError(env, ERR_ABILITY_RUNTIME_EXTERNAL_NO_SUCH_ID); + return nullptr; + } + + if (nativeAbility->etsAbilityObj == nullptr) { + TAG_LOGE(AAFwkTag::APPKIT, "etsAbilityObj is null for instanceId: %{public}s", instanceIdStr.c_str()); + EtsErrorUtil::ThrowRuntimeError(env, ERR_ABILITY_RUNTIME_EXTERNAL_INTERNAL_ERROR); + return nullptr; + } + + TAG_LOGD(AAFwkTag::APPKIT, "Get UIAbility for instanceId: %{public}s", instanceIdStr.c_str()); + return nativeAbility->etsAbilityObj; +} + void EtsApplicationContextUtils::OnNativeOffEnvironmentSync(ani_env *env, ani_object aniObj, ani_int callbackId, ani_object callback) { @@ -551,6 +589,82 @@ void EtsApplicationContextUtils::OnRestartApp(ani_env *env, ani_object aniObj, a TAG_LOGD(AAFwkTag::APPKIT, "RestartApp errCode is %{public}d", errCode); } +void EtsApplicationContextUtils::OnEnableDelayedProcessExit(ani_env *env, ani_object aniObj, ani_object callback) +{ + if (env == nullptr) { + TAG_LOGE(AAFwkTag::APPKIT, "null env"); + return; + } + auto applicationContext = applicationContext_.lock(); + if (applicationContext == nullptr) { + TAG_LOGE(AAFwkTag::APPKIT, "null applicationContext"); + AppExecFwk::AsyncCallback(env, callback, EtsErrorUtil::CreateError(env, + (ani_int)AbilityErrorCode::ERROR_CODE_INVALID_CONTEXT, + "applicationContext is already released."), nullptr); + return; + } + auto errCode = applicationContext->EnableDelayedProcessExit(); + if (errCode == ERR_OK) { + AppExecFwk::AsyncCallback(env, callback, EtsErrorUtil::CreateError(env, AbilityErrorCode::ERROR_OK), nullptr); + } else { + TAG_LOGE(AAFwkTag::APPKIT, "EnableDelayedProcessExit failed %{public}d", errCode); + AppExecFwk::AsyncCallback(env, callback, EtsErrorUtil::CreateErrorByNativeErr(env, errCode), nullptr); + } +} + +void EtsApplicationContextUtils::OnDisableDelayedProcessExit(ani_env *env, ani_object aniObj, ani_object callback) +{ + if (env == nullptr) { + TAG_LOGE(AAFwkTag::APPKIT, "null env"); + return; + } + auto applicationContext = applicationContext_.lock(); + if (applicationContext == nullptr) { + TAG_LOGE(AAFwkTag::APPKIT, "null applicationContext"); + AppExecFwk::AsyncCallback(env, callback, EtsErrorUtil::CreateError(env, + (ani_int)AbilityErrorCode::ERROR_CODE_INVALID_CONTEXT, + "applicationContext is already released."), nullptr); + return; + } + auto errCode = applicationContext->DisableDelayedProcessExit(); + if (errCode == ERR_OK) { + AppExecFwk::AsyncCallback(env, callback, EtsErrorUtil::CreateError(env, AbilityErrorCode::ERROR_OK), nullptr); + } else { + TAG_LOGE(AAFwkTag::APPKIT, "DisableDelayedProcessExit failed %{public}d", errCode); + AppExecFwk::AsyncCallback(env, callback, EtsErrorUtil::CreateErrorByNativeErr(env, errCode), nullptr); + } +} + +void EtsApplicationContextUtils::OnStartSelfUIAbility(ani_env *env, + ani_object aniObj, ani_object wantObj, ani_object callback) +{ + TAG_LOGD(AAFwkTag::APPKIT, "StartSelfUIAbility Call"); + if (env == nullptr) { + TAG_LOGE(AAFwkTag::APPKIT, "null env"); + return; + } + AAFwk::Want want; + if (!OHOS::AppExecFwk::UnwrapWant(env, wantObj, want)) { + TAG_LOGE(AAFwkTag::APPKIT, "Parse want failed"); + AppExecFwk::AsyncCallback(env, callback, EtsErrorUtil::CreateInvalidParamError(env, + "Parse param want failed, want must be Want."), nullptr); + return; + } + auto context = applicationContext_.lock(); + if (!context) { + AppExecFwk::AsyncCallback(env, callback, EtsErrorUtil::CreateErrorByNativeErr(env, + (int32_t)AbilityErrorCode::ERROR_CODE_INVALID_CONTEXT), nullptr); + return; + } + auto errCode = context->StartSelfUIAbility(want); + if (errCode == ERR_OK) { + AppExecFwk::AsyncCallback(env, callback, EtsErrorUtil::CreateError(env, AbilityErrorCode::ERROR_OK), nullptr); + } else { + TAG_LOGE(AAFwkTag::APPKIT, "StartSelfUIAbility failed %{public}d", errCode); + AppExecFwk::AsyncCallback(env, callback, EtsErrorUtil::CreateErrorByNativeErr(env, errCode), nullptr); + } +} + void EtsApplicationContextUtils::OnSetFont(ani_env *env, ani_object aniObj, ani_string font) { if (env == nullptr) { @@ -774,6 +888,40 @@ void EtsApplicationContextUtils::RestartApp(ani_env *env, ani_object aniObj, ani etsContext->OnRestartApp(env, aniObj, wantObj); } +void EtsApplicationContextUtils::EnableDelayedProcessExit(ani_env *env, ani_object aniObj, ani_object callback) +{ + TAG_LOGD(AAFwkTag::APPKIT, "EnableDelayedProcessExit Call"); + auto etsContext = GeApplicationContext(env, aniObj); + if (etsContext == nullptr) { + TAG_LOGE(AAFwkTag::APPKIT, "null etsContext"); + return; + } + etsContext->OnEnableDelayedProcessExit(env, aniObj, callback); +} + +void EtsApplicationContextUtils::DisableDelayedProcessExit(ani_env *env, ani_object aniObj, ani_object callback) +{ + TAG_LOGD(AAFwkTag::APPKIT, "DisableDelayedProcessExit Call"); + auto etsContext = GeApplicationContext(env, aniObj); + if (etsContext == nullptr) { + TAG_LOGE(AAFwkTag::APPKIT, "null etsContext"); + return; + } + etsContext->OnDisableDelayedProcessExit(env, aniObj, callback); +} + +void EtsApplicationContextUtils::StartSelfUIAbility(ani_env *env, ani_object aniObj, + ani_object wantObj, ani_object callback) +{ + TAG_LOGD(AAFwkTag::APPKIT, "StartSelfUIAbility Call"); + auto etsContext = GeApplicationContext(env, aniObj); + if (etsContext == nullptr) { + TAG_LOGE(AAFwkTag::APPKIT, "null etsContext"); + return; + } + etsContext->OnStartSelfUIAbility(env, aniObj, wantObj, callback); +} + void EtsApplicationContextUtils::SetFont(ani_env *env, ani_object aniObj, ani_string font) { TAG_LOGD(AAFwkTag::APPKIT, "SetFont Call"); @@ -1030,6 +1178,19 @@ ani_int EtsApplicationContextUtils::NativeOnEnvironmentSync(ani_env *env, ani_ob return etsContext->OnNativeOnEnvironmentSync(env, aniObj, envCallback); } +ani_object EtsApplicationContextUtils::GetUIAbilityByInstanceId( + ani_env *env, ani_object aniObj, ani_string instanceId) +{ + TAG_LOGD(AAFwkTag::APPKIT, "GetUIAbilityByInstanceId called"); + auto etsContext = GeApplicationContext(env, aniObj); + if (etsContext == nullptr) { + TAG_LOGE(AAFwkTag::APPKIT, "null etsContext"); + EtsErrorUtil::ThrowError(env, AbilityErrorCode::ERROR_CODE_INVALID_CONTEXT); + return nullptr; + } + return etsContext->OnGetUIAbilityByInstanceId(env, instanceId); +} + void EtsApplicationContextUtils::NativeOffEnvironmentCheck(ani_env *env, ani_object aniObj) { TAG_LOGD(AAFwkTag::APPKIT, "NativeOffEnvironmentCheck Call"); @@ -1255,6 +1416,15 @@ void EtsApplicationContextUtils::BindApplicationContextFunc(ani_env *aniEnv) reinterpret_cast(EtsApplicationContextUtils::SetFont)}, ani_native_function {"nativerestartApp", "C{@ohos.app.ability.Want.Want}:", reinterpret_cast(EtsApplicationContextUtils::RestartApp)}, + ani_native_function {"nativeEnableDelayedProcessExitSync", + "C{utils.AbilityUtils.AsyncCallbackWrapper}:", + reinterpret_cast(EtsApplicationContextUtils::EnableDelayedProcessExit)}, + ani_native_function {"nativeDisableDelayedProcessExitSync", + "C{utils.AbilityUtils.AsyncCallbackWrapper}:", + reinterpret_cast(EtsApplicationContextUtils::DisableDelayedProcessExit)}, + ani_native_function {"nativeStartSelfUIAbilitySync", + "C{@ohos.app.ability.Want.Want}C{utils.AbilityUtils.AsyncCallbackWrapper}:", + reinterpret_cast(EtsApplicationContextUtils::StartSelfUIAbility)}, ani_native_function {"nativeOnEnvironmentSync", "C{@ohos.app.ability.EnvironmentCallback.EnvironmentCallback}:i", reinterpret_cast(EtsApplicationContextUtils::NativeOnEnvironmentSync)}, @@ -1288,6 +1458,9 @@ void EtsApplicationContextUtils::BindApplicationContextFunc(ani_env *aniEnv) ani_native_function {"nativeOffSystemConfigurationUpdatedSync", "C{@ohos.app.ability.systemConfiguration.systemConfiguration.UpdatedCallback}:", reinterpret_cast(EtsApplicationContextUtils::NativeOffSystemConfigurationUpdatedSync)}, + ani_native_function {"nativeGetUIAbilityByInstanceId", + "C{std.core.String}:C{@ohos.app.ability.UIAbility.UIAbility}", + reinterpret_cast(EtsApplicationContextUtils::GetUIAbilityByInstanceId)}, }; if ((status = aniEnv->Class_BindNativeMethods(contextClass, applicationContextFunctions.data(), applicationContextFunctions.size())) != ANI_OK) { diff --git a/frameworks/ets/ani/auto_fill_manager/BUILD.gn b/frameworks/ets/ani/auto_fill_manager/BUILD.gn index f2f783fa93..dc56b971aa 100644 --- a/frameworks/ets/ani/auto_fill_manager/BUILD.gn +++ b/frameworks/ets/ani/auto_fill_manager/BUILD.gn @@ -1,4 +1,4 @@ -# Copyright (c) 2025 Huawei Device Co., Ltd. +# Copyright (c) 2025-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 @@ -25,7 +25,10 @@ ohos_shared_library("ability_auto_fill_manager_ani_kit") { debug = false } - include_dirs = [ "./include" ] + include_dirs = [ + "./include", + "${ability_runtime_path}/frameworks/ets/ani/enum_convert", + ] configs = [ "${ability_runtime_services_path}/common:optimize_config" ] @@ -33,6 +36,8 @@ ohos_shared_library("ability_auto_fill_manager_ani_kit") { sources = [ "./src/ets_auto_fill_manager.cpp", + "./src/ets_auto_fill_manager_util.cpp", + "./src/ets_auto_fill_request_callback.cpp", "./src/ets_auto_save_request_callback.cpp", ] @@ -42,10 +47,10 @@ ohos_shared_library("ability_auto_fill_manager_ani_kit") { } deps = [ - "${ability_runtime_innerkits_path}/auto_fill_manager:auto_fill_manager", + "${ability_runtime_innerkits_path}/auto_fill_manager", "${ability_runtime_native_path}/ability/native:ability_business_error", "${ability_runtime_native_path}/ability/native:abilitykit_native", - "${ability_runtime_path}/frameworks/ets/ani/ani_common:ani_common", + "${ability_runtime_path}/frameworks/ets/ani/ani_common", ] external_deps = [ diff --git a/frameworks/ets/ani/auto_fill_manager/include/ets_auto_fill_manager.h b/frameworks/ets/ani/auto_fill_manager/include/ets_auto_fill_manager.h index 21243e6dc2..9d49b7b1e2 100644 --- a/frameworks/ets/ani/auto_fill_manager/include/ets_auto_fill_manager.h +++ b/frameworks/ets/ani/auto_fill_manager/include/ets_auto_fill_manager.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 Huawei Device Co., Ltd. + * Copyright (c) 2025-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 @@ -19,6 +19,7 @@ #include #include +#include "ets_auto_fill_request_callback.h" #include "ets_auto_save_request_callback.h" namespace OHOS { @@ -29,16 +30,26 @@ public: ~EtsAutoFillManager() = default; static EtsAutoFillManager &GetInstance(); static void RequestAutoSave(ani_env *env, ani_object autoSaveCallbackObj); + static void RequestAutoSaveWithRequest(ani_env *env, ani_object saveRequest, ani_object autoSaveCallbackObj); + static void RequestAutoFill(ani_env *env, ani_object fillRequest, ani_object autoFillCallbackObj); private: - void OnRequestAutoSave(ani_env *env, ani_object autoSaveCallbackObj); - void OnRequestAutoSaveInner(ani_env *env, int32_t instanceId, - const std::shared_ptr &saveRequestCallback); - std::shared_ptr GetCallbackByInstanceId(int32_t instanceId); + void OnRequestAutoSave(ani_env *env, ani_object saveRequest, ani_object autoSaveCallbackObj); + void OnRequestAutoSaveInner(ani_env *env, int32_t instanceId, AbilityRuntime::AutoFill::AutoFillRequest &request, + const std::shared_ptr &saveRequestCallback, const bool hasRequest); + std::shared_ptr GetSaveCallbackByInstanceId(int32_t instanceId); void OnRequestAutoSaveDone(int32_t instanceId); - std::mutex mutexLock_; + void OnRequestAutoFill(ani_env *env, ani_object fillRequest, ani_object autoFillCallbackObj); + void OnRequestAutoFillInner(ani_env *env, int32_t instanceId, AbilityRuntime::AutoFill::AutoFillRequest &request, + const std::shared_ptr &fillRequestCallback); + std::shared_ptr GetFillCallbackByInstanceId(int32_t instanceId); + void OnRequestAutoFillDone(int32_t instanceId); + + std::mutex saveMutex_; std::map> saveRequestObject_; + std::mutex fillMutex_; + std::map> fillRequestObject_; }; void EtsAutoFillManagerInit(ani_env *env); } // namespace AutoFillManagerEts diff --git a/frameworks/ets/ani/auto_fill_manager/include/ets_auto_fill_manager_util.h b/frameworks/ets/ani/auto_fill_manager/include/ets_auto_fill_manager_util.h new file mode 100644 index 0000000000..01b0013fea --- /dev/null +++ b/frameworks/ets/ani/auto_fill_manager/include/ets_auto_fill_manager_util.h @@ -0,0 +1,40 @@ +/* + * 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 perns and + * limitations under the License. + */ + +#ifndef OHOS_ABILITY_RUNTIME_ETS_AUTO_FILL_MANAGER_UTIL_H +#define OHOS_ABILITY_RUNTIME_ETS_AUTO_FILL_MANAGER_UTIL_H + +#include "ani.h" +#include "auto_fill_custom_config.h" +#include "view_data.h" + +namespace OHOS { +namespace AutoFillManagerEts { +ani_object WrapAutoFillRect(ani_env *env, const AbilityBase::Rect &rect); +ani_object WrapPageNodeInfo(ani_env *env, const AbilityBase::PageNodeInfo &pageNodeInfo); +ani_object WrapViewData(ani_env *env, const AbilityBase::ViewData &viewData); +ani_object WrapFillFailureResult(ani_env *env, int32_t errCode); + +bool UnwrapAutoFillRect(ani_env *env, ani_object object, AbilityBase::Rect &rect, std::string &errorMsg); +bool UnwrapPageNodeInfo(ani_env *env, + ani_object object, AbilityBase::PageNodeInfo &pageNodeInfo, std::string &errorMsg); +bool UnwrapViewData(ani_env *env, ani_object object, AbilityBase::ViewData &viewData, std::string &errorMsg); +bool UnwrapSaveRequest(ani_env *env, + ani_object object, AbilityRuntime::AutoFill::AutoFillRequest &request, std::string &errorMsg); +bool UnwrapFillRequest(ani_env *env, + ani_object object, AbilityRuntime::AutoFill::AutoFillRequest &request, std::string &errorMsg); +} // namespace AutoFillManagerEts +} // namespace OHOS +#endif // OHOS_ABILITY_RUNTIME_ETS_AUTO_FILL_MANAGER_UTIL_H diff --git a/frameworks/ets/ani/auto_fill_manager/include/ets_auto_fill_request_callback.h b/frameworks/ets/ani/auto_fill_manager/include/ets_auto_fill_request_callback.h new file mode 100644 index 0000000000..a473bd8c7d --- /dev/null +++ b/frameworks/ets/ani/auto_fill_manager/include/ets_auto_fill_request_callback.h @@ -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. + */ + +#ifndef OHOS_ABILITY_RUNTIME_ETS_AUTO_FILL_REQUEST_CALLBACK_H +#define OHOS_ABILITY_RUNTIME_ETS_AUTO_FILL_REQUEST_CALLBACK_H + +#include "ets_native_reference.h" +#include "fill_request_callback_interface.h" + +namespace OHOS { +namespace AutoFillManagerEts { +using AutoFillManagerFunc = std::function; +class EtsAutoFillRequestCallback : public AbilityRuntime::IFillRequestCallback, + public std::enable_shared_from_this { +public: + EtsAutoFillRequestCallback(ani_vm *vm, int32_t instanceId, AutoFillManagerFunc autoFillManagerFunc); + virtual ~EtsAutoFillRequestCallback(); + + void Register(ani_object object); + void OnFillRequestSuccess(const AbilityBase::ViewData &viewData) override; + void OnFillRequestFailed(int32_t errCode, const std::string &fillContent = "", bool isPopup = false) override; + +private: + void ETSCallFunction(const std::string &methodName, ani_ref *argv, int32_t argc); + bool IsEtsCallbackEquals(std::shared_ptr callback, ani_object object); + ani_env *GetAniEnv(); + + ani_vm *vm_ = nullptr; + std::shared_ptr callback_; + int32_t instanceId_ = -1; + AutoFillManagerFunc autoFillManagerFunc_ = nullptr; +}; +} // namespace AutoFillManagerEts +} // namespace OHOS +#endif // OHOS_ABILITY_RUNTIME_ETS_AUTO_FILL_REQUEST_CALLBACK_H \ No newline at end of file diff --git a/frameworks/ets/ani/auto_fill_manager/src/ets_auto_fill_manager.cpp b/frameworks/ets/ani/auto_fill_manager/src/ets_auto_fill_manager.cpp index 09962dc1b0..a5c7749845 100644 --- a/frameworks/ets/ani/auto_fill_manager/src/ets_auto_fill_manager.cpp +++ b/frameworks/ets/ani/auto_fill_manager/src/ets_auto_fill_manager.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 Huawei Device Co., Ltd. + * Copyright (c) 2025-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 @@ -18,6 +18,7 @@ #include "ability_business_error.h" #include "auto_fill_manager.h" #include "core/common/container_scope.h" +#include "ets_auto_fill_manager_util.h" #include "ets_error_utils.h" #include "hilog_tag_wrapper.h" @@ -35,10 +36,16 @@ EtsAutoFillManager &EtsAutoFillManager::GetInstance() void EtsAutoFillManager::RequestAutoSave(ani_env *env, ani_object autoSaveCallbackObj) { - GetInstance().OnRequestAutoSave(env, autoSaveCallbackObj); + GetInstance().OnRequestAutoSave(env, nullptr, autoSaveCallbackObj); } -void EtsAutoFillManager::OnRequestAutoSave(ani_env *env, ani_object autoSaveCallbackObj) +void EtsAutoFillManager::RequestAutoSaveWithRequest(ani_env *env, + ani_object saveRequestObj, ani_object autoSaveCallbackObj) +{ + GetInstance().OnRequestAutoSave(env, saveRequestObj, autoSaveCallbackObj); +} + +void EtsAutoFillManager::OnRequestAutoSave(ani_env *env, ani_object saveRequestObj, ani_object autoSaveCallbackObj) { TAG_LOGD(AAFwkTag::AUTOFILLMGR, "OnRequestAutoSave called"); if (env == nullptr) { @@ -46,7 +53,7 @@ void EtsAutoFillManager::OnRequestAutoSave(ani_env *env, ani_object autoSaveCall return; } int32_t instanceId = Ace::ContainerScope::CurrentId(); - auto saveCallback = GetCallbackByInstanceId(instanceId); + auto saveCallback = GetSaveCallbackByInstanceId(instanceId); if (saveCallback != nullptr) { TAG_LOGE(AAFwkTag::AUTOFILLMGR, "there are other requests in progress"); AbilityRuntime::EtsErrorUtil::ThrowError(env, AbilityRuntime::AbilityErrorCode::ERROR_CODE_INNER); @@ -65,23 +72,32 @@ void EtsAutoFillManager::OnRequestAutoSave(ani_env *env, ani_object autoSaveCall AbilityRuntime::EtsErrorUtil::ThrowError(env, AbilityRuntime::AbilityErrorCode::ERROR_CODE_INNER); return; } + AbilityRuntime::AutoFill::AutoFillRequest request; + bool hasRequest = saveRequestObj != nullptr ? true : false; + std::string errorMsg; + if (hasRequest && !UnwrapSaveRequest(env, saveRequestObj, request, errorMsg)) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "failed to parse saveRequest"); + AbilityRuntime::EtsErrorUtil::ThrowInvalidParamError(env, errorMsg.c_str()); + return; + } ani_status status = ANI_OK; - ani_boolean isCallbackUndefined; + ani_boolean isCallbackUndefined = ANI_FALSE; if ((status = env->Reference_IsUndefined(autoSaveCallbackObj, &isCallbackUndefined)) != ANI_OK) { TAG_LOGE(AAFwkTag::AUTOFILLMGR, "Falied to check undefinde status: %{public}d", status); - AbilityRuntime::EtsErrorUtil::ThrowError(env, - static_cast(AbilityRuntime::AbilityErrorCode::ERROR_CODE_INVALID_PARAM), - "Parameter error. The second parameter is not of type autoSaveCallbackObj."); + AbilityRuntime::EtsErrorUtil::ThrowInvalidParamError(env, + hasRequest ? "Parameter error. The third parameter is not of type AutoSaveCallback" + : "Parameter error. The second parameter is not of type AutoSaveCallback"); return; } if (!isCallbackUndefined) { saveCallback->Register(autoSaveCallbackObj); } - OnRequestAutoSaveInner(env, instanceId, saveCallback); + OnRequestAutoSaveInner(env, instanceId, request, saveCallback, hasRequest); } void EtsAutoFillManager::OnRequestAutoSaveInner(ani_env *env, int32_t instanceId, - const std::shared_ptr &saveRequestCallback) + AbilityRuntime::AutoFill::AutoFillRequest &request, + const std::shared_ptr &saveRequestCallback, const bool hasRequest) { #ifdef SUPPORT_GRAPHICS auto uiContent = Ace::UIContent::GetUIContent(instanceId); @@ -90,28 +106,31 @@ void EtsAutoFillManager::OnRequestAutoSaveInner(ani_env *env, int32_t instanceId AbilityRuntime::EtsErrorUtil::ThrowError(env, AbilityRuntime::AbilityErrorCode::ERROR_CODE_INNER); return; } - if (uiContent->CheckNeedAutoSave()) { - AbilityRuntime::AutoFill::AutoFillRequest request; - uiContent->DumpViewData(request.viewData, request.autoFillType); - request.autoFillCommand = AbilityRuntime::AutoFill::AutoFillCommand::SAVE; - AbilityRuntime::AutoFill::AutoFillResult result; - auto ret = AbilityRuntime::AutoFillManager::GetInstance().RequestAutoSave(uiContent, request, - saveRequestCallback, result); - if (ret != ERR_OK) { - TAG_LOGE(AAFwkTag::AUTOFILLMGR, "RequestAutoSave error[%{public}d]", ret); - AbilityRuntime::EtsErrorUtil::ThrowError(env, AbilityRuntime::EtsErrorUtil::CreateErrorByNativeErr(env, - static_cast(ret))); + if (!hasRequest) { + if (!uiContent->CheckNeedAutoSave()) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "no need auto save"); return; } - std::lock_guard lock(mutexLock_); - saveRequestObject_.emplace(instanceId, saveRequestCallback); + uiContent->DumpViewData(request.viewData, request.autoFillType); } + request.autoFillCommand = AbilityRuntime::AutoFill::AutoFillCommand::SAVE; + AbilityRuntime::AutoFill::AutoFillResult result; + auto ret = AbilityRuntime::AutoFillManager::GetInstance().RequestAutoSave(uiContent, request, + saveRequestCallback, result); + if (ret != ERR_OK) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "RequestAutoSave error[%{public}d]", ret); + AbilityRuntime::EtsErrorUtil::ThrowError(env, AbilityRuntime::EtsErrorUtil::CreateErrorByNativeErr(env, + static_cast(ret))); + return; + } + std::lock_guard lock(saveMutex_); + saveRequestObject_.emplace(instanceId, saveRequestCallback); #endif // SUPPORT_GRAPHICS } -std::shared_ptr EtsAutoFillManager::GetCallbackByInstanceId(int32_t instanceId) +std::shared_ptr EtsAutoFillManager::GetSaveCallbackByInstanceId(int32_t instanceId) { - std::lock_guard lock(mutexLock_); + std::lock_guard lock(saveMutex_); auto iter = saveRequestObject_.find(instanceId); if (iter != saveRequestObject_.end()) { return iter->second.lock(); @@ -121,13 +140,111 @@ std::shared_ptr EtsAutoFillManager::GetCallbackByIns void EtsAutoFillManager::OnRequestAutoSaveDone(int32_t instanceId) { - std::lock_guard lock(mutexLock_); + std::lock_guard lock(saveMutex_); auto iter = saveRequestObject_.find(instanceId); if (iter != saveRequestObject_.end()) { saveRequestObject_.erase(iter); } } +void EtsAutoFillManager::RequestAutoFill(ani_env *env, ani_object fillRequestObj, ani_object autoFillCallbackObj) +{ + GetInstance().OnRequestAutoFill(env, fillRequestObj, autoFillCallbackObj); +} + +void EtsAutoFillManager::OnRequestAutoFill(ani_env *env, ani_object fillRequestObj, ani_object autoFillCallbackObj) +{ + TAG_LOGD(AAFwkTag::AUTOFILLMGR, "OnRequestAutoFill called"); + if (env == nullptr || fillRequestObj == nullptr) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "null env or fillRequestObj"); + return; + } + int32_t instanceId = Ace::ContainerScope::CurrentId(); + auto fillCallback = GetFillCallbackByInstanceId(instanceId); + if (fillCallback != nullptr) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "there are other requests in progress"); + AbilityRuntime::EtsErrorUtil::ThrowError(env, AbilityRuntime::AbilityErrorCode::ERROR_CODE_INNER); + return; + } + ani_vm *vm = nullptr; + if (env->GetVM(&vm) != ANI_OK) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "get vm failed"); + AbilityRuntime::EtsErrorUtil::ThrowInvalidParamError(env, "get vm failed."); + return; + } + auto autoFillMangerFunc = [](const int32_t arg) { EtsAutoFillManager::GetInstance().OnRequestAutoFillDone(arg); }; + fillCallback = std::make_shared(vm, instanceId, autoFillMangerFunc); + if (fillCallback == nullptr) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "null fillCallback"); + AbilityRuntime::EtsErrorUtil::ThrowError(env, AbilityRuntime::AbilityErrorCode::ERROR_CODE_INNER); + return; + } + AbilityRuntime::AutoFill::AutoFillRequest request; + std::string errorMsg; + if (!UnwrapFillRequest(env, fillRequestObj, request, errorMsg)) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "failed to parse fillRequest"); + AbilityRuntime::EtsErrorUtil::ThrowInvalidParamError(env, errorMsg.c_str()); + return; + } + ani_status status = ANI_OK; + ani_boolean isCallbackUndefined = ANI_FALSE; + if ((status = env->Reference_IsUndefined(autoFillCallbackObj, &isCallbackUndefined)) != ANI_OK) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "Falied to check undefinde status: %{public}d", status); + AbilityRuntime::EtsErrorUtil::ThrowInvalidParamError(env, + "Parameter error. The third parameter is not of type AutoFillCallback"); + return; + } + if (!isCallbackUndefined) { + fillCallback->Register(autoFillCallbackObj); + } + OnRequestAutoFillInner(env, instanceId, request, fillCallback); +} + +void EtsAutoFillManager::OnRequestAutoFillInner(ani_env *env, int32_t instanceId, + AbilityRuntime::AutoFill::AutoFillRequest &request, + const std::shared_ptr &fillRequestCallback) +{ +#ifdef SUPPORT_GRAPHICS + auto uiContent = Ace::UIContent::GetUIContent(instanceId); + if (uiContent == nullptr) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "null uiContent"); + AbilityRuntime::EtsErrorUtil::ThrowError(env, AbilityRuntime::AbilityErrorCode::ERROR_CODE_INNER); + return; + } + request.autoFillCommand = AbilityRuntime::AutoFill::AutoFillCommand::FILL; + AbilityRuntime::AutoFill::AutoFillResult result; + auto ret = AbilityRuntime::AutoFillManager::GetInstance().RequestAutoFill(uiContent, request, + fillRequestCallback, result); + if (ret != ERR_OK) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "RequestAutoFill error[%{public}d]", ret); + AbilityRuntime::EtsErrorUtil::ThrowError(env, AbilityRuntime::EtsErrorUtil::CreateErrorByNativeErr(env, + static_cast(ret))); + return; + } + std::lock_guard lock(fillMutex_); + fillRequestObject_.emplace(instanceId, fillRequestCallback); +#endif // SUPPORT_GRAPHICS +} + +std::shared_ptr EtsAutoFillManager::GetFillCallbackByInstanceId(int32_t instanceId) +{ + std::lock_guard lock(fillMutex_); + auto iter = fillRequestObject_.find(instanceId); + if (iter != fillRequestObject_.end()) { + return iter->second.lock(); + } + return nullptr; +} + +void EtsAutoFillManager::OnRequestAutoFillDone(int32_t instanceId) +{ + std::lock_guard lock(fillMutex_); + auto iter = fillRequestObject_.find(instanceId); + if (iter != fillRequestObject_.end()) { + fillRequestObject_.erase(iter); + } +} + void EtsAutoFillManagerInit(ani_env *env) { TAG_LOGD(AAFwkTag::AUTOFILLMGR, "EtsAutoFillManagerInit call"); @@ -145,10 +262,14 @@ void EtsAutoFillManagerInit(ani_env *env) TAG_LOGE(AAFwkTag::AUTOFILLMGR, "FindNamespace autoFillManager failed status : %{public}d", status); return; } - std::array kitFunctions = {ani_native_function{ - "requestAutoSaveWithScope", - "C{@ohos.app.ability.autoFillManager.autoFillManager.AutoSaveCallback}:", - reinterpret_cast(EtsAutoFillManager::RequestAutoSave)}}; + std::array kitFunctions = { + ani_native_function{"requestAutoSaveWithScope", nullptr, + reinterpret_cast(EtsAutoFillManager::RequestAutoSave)}, + ani_native_function{"requestAutoSaveWithRequest", nullptr, + reinterpret_cast(EtsAutoFillManager::RequestAutoSaveWithRequest)}, + ani_native_function{"requestAutoFillWithScope", nullptr, + reinterpret_cast(EtsAutoFillManager::RequestAutoFill)} + }; status = env->Namespace_BindNativeFunctions(ns, kitFunctions.data(), kitFunctions.size()); if (status != ANI_OK) { TAG_LOGE(AAFwkTag::AUTOFILLMGR, "Namespace_BindNativeFunctions failed status : %{public}d", status); diff --git a/frameworks/ets/ani/auto_fill_manager/src/ets_auto_fill_manager_util.cpp b/frameworks/ets/ani/auto_fill_manager/src/ets_auto_fill_manager_util.cpp new file mode 100644 index 0000000000..24603b5b38 --- /dev/null +++ b/frameworks/ets/ani/auto_fill_manager/src/ets_auto_fill_manager_util.cpp @@ -0,0 +1,406 @@ +/* + * 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 "ets_auto_fill_manager_util.h" + +#include "ani_common_util.h" +#include "ani_enum_convert.h" +#include "hilog_tag_wrapper.h" + +namespace OHOS { +namespace AutoFillManagerEts { +namespace { +constexpr const char* AUTO_FILL_RECT_IMPL_CLASS_NAME = "application.AutoFillRect.AutoFillRectImpl"; +constexpr const char* PAGE_NODE_INFO_IMPL_CLASS_NAME = "application.PageNodeInfo.PageNodeInfoImpl"; +constexpr const char* VIEW_DATA_IMPL_CLASS_NAME = "application.ViewData.ViewDataImpl"; +constexpr const char* FILL_FAILURE_RESULT_INNER_CLASS_NAME = "application.AutoFillRequest.FillFailureResultInner"; +constexpr const char* AUTO_FILL_TYPE_ENUM_NAME = "application.AutoFillType.AutoFillType"; +constexpr const char* LEFT = "left"; +constexpr const char* TOP = "top"; +constexpr const char* WIDTH = "width"; +constexpr const char* HEIGHT = "height"; +constexpr const char* ID = "id"; +constexpr const char* AUTO_FILL_TYPE = "autoFillType"; +constexpr const char* VALUE = "value"; +constexpr const char* PLACEHOLDER = "placeholder"; +constexpr const char* RECT = "rect"; +constexpr const char* IS_FOCUS = "isFocus"; +constexpr const char* BUNDLE_NAME = "bundleName"; +constexpr const char* PAGE_URL = "pageUrl"; +constexpr const char* PAGE_NODE_INFOS = "pageNodeInfos"; +constexpr const char* PAGE_RECT = "pageRect"; +constexpr const char* ERR_CODE = "errCode"; +constexpr const char* VIEW_DATA = "viewData"; +constexpr const char* TYPE = "type"; +constexpr const char* TRIGGER_TYPE = "triggerType"; +} +using namespace AppExecFwk; + +ani_object WrapAutoFillRect(ani_env *env, const AbilityBase::Rect &rect) +{ + if (env == nullptr) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "env null"); + return nullptr; + } + + ani_object object = nullptr; + if (!CreateObjectByClassName(env, AUTO_FILL_RECT_IMPL_CLASS_NAME, object)) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "fail to create AutoFillRect object"); + return nullptr; + } + + if (!SetDoublePropertyValue(env, object, LEFT, rect.left)) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "set left failed"); + return nullptr; + } + + if (!SetDoublePropertyValue(env, object, TOP, rect.top)) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "set top failed"); + return nullptr; + } + + if (!SetDoublePropertyValue(env, object, WIDTH, rect.width)) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "set width failed"); + return nullptr; + } + + if (!SetDoublePropertyValue(env, object, HEIGHT, rect.height)) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "set height failed"); + return nullptr; + } + return object; +} + +ani_object WrapPageNodeInfo(ani_env *env, const AbilityBase::PageNodeInfo &pageNodeInfo) +{ + if (env == nullptr) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "env null"); + return nullptr; + } + + ani_object object = nullptr; + if (!CreateObjectByClassName(env, PAGE_NODE_INFO_IMPL_CLASS_NAME, object)) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "fail to create PageNodeInfo object"); + return nullptr; + } + + if (!SetIntPropertyValue(env, object, ID, pageNodeInfo.id)) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "set id failed"); + return nullptr; + } + + ani_enum_item aniAutoFillType = nullptr; + if (!AAFwk::AniEnumConvertUtil::EnumConvert_NativeToEts(env, + AUTO_FILL_TYPE_ENUM_NAME, pageNodeInfo.autoFillType, aniAutoFillType) || + !SetRefProperty(env, object, AUTO_FILL_TYPE, aniAutoFillType)) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "set autoFillType failed"); + return nullptr; + } + + if (!SetRefProperty(env, object, VALUE, GetAniString(env, pageNodeInfo.value))) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "set value failed"); + return nullptr; + } + + if (!SetRefProperty(env, object, PLACEHOLDER, GetAniString(env, pageNodeInfo.placeholder))) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "set placeholder failed"); + return nullptr; + } + + if (!SetRefProperty(env, object, RECT, WrapAutoFillRect(env, pageNodeInfo.rect))) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "set rect failed"); + return nullptr; + } + + ani_status status = ANI_ERROR; + if ((status = env->Object_SetPropertyByName_Boolean(object, IS_FOCUS, pageNodeInfo.isFocus)) != ANI_OK) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "Object_SetPropertyByName_Boolean failed: status: %{public}d", status); + return nullptr; + } + return object; +} + +ani_object WrapViewData(ani_env *env, const AbilityBase::ViewData &viewData) +{ + if (env == nullptr) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "env null"); + return nullptr; + } + + ani_object object = nullptr; + if (!CreateObjectByClassName(env, VIEW_DATA_IMPL_CLASS_NAME, object)) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "fail to create ViewData object"); + return nullptr; + } + + if (!SetRefProperty(env, object, BUNDLE_NAME, GetAniString(env, viewData.bundleName))) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "set bundleName failed"); + return nullptr; + } + + if (!SetRefProperty(env, object, PAGE_URL, GetAniString(env, viewData.pageUrl))) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "set pageUrl failed"); + return nullptr; + } + + ani_object aniNodes = nullptr; + if (!CreateArrayObject(env, aniNodes, viewData.nodes.size())) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "fail to create array object"); + return nullptr; + } + ani_size index = 0; + ani_status status = ANI_ERROR; + for (const auto &item : viewData.nodes) { + status = env->Object_CallMethodByName_Void(aniNodes, "$_set", "iY:", index, WrapPageNodeInfo(env, item)); + if (status != ANI_OK) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "Object_CallMethodByName_Void failed: %{public}d", status); + return nullptr; + } + ++index; + } + + if (!SetRefProperty(env, object, PAGE_NODE_INFOS, aniNodes)) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "set pageNodeInfos failed"); + return nullptr; + } + + if (!SetRefProperty(env, object, PAGE_RECT, WrapAutoFillRect(env, viewData.pageRect))) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "set pageRect failed"); + return nullptr; + } + return object; +} + +ani_object WrapFillFailureResult(ani_env *env, int32_t errCode) +{ + if (env == nullptr) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "env null"); + return nullptr; + } + + ani_object object = nullptr; + if (!CreateObjectByClassName(env, FILL_FAILURE_RESULT_INNER_CLASS_NAME, object)) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "fail to create FillFailureResult object"); + return nullptr; + } + + if (!SetIntPropertyValue(env, object, ERR_CODE, errCode)) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "set errCode failed"); + return nullptr; + } + + return object; +} + +bool UnwrapAutoFillRect(ani_env *env, ani_object object, AbilityBase::Rect &rect, std::string &errorMsg) +{ + if (env == nullptr) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "env null"); + return false; + } + + double doubleValue; + if (!GetDoublePropertyValue(env, object, LEFT, doubleValue)) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "parameter error"); + errorMsg = "Parameter error. The type of rect.left must be double"; + return false; + } + rect.left = static_cast(doubleValue); + + if (!GetDoublePropertyValue(env, object, TOP, doubleValue)) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "parameter error"); + errorMsg = "Parameter error. The type of rect.top must be double"; + return false; + } + rect.top = static_cast(doubleValue); + + if (!GetDoublePropertyValue(env, object, WIDTH, doubleValue)) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "parameter error"); + errorMsg = "Parameter error. The type of rect.width must be double"; + return false; + } + rect.width = static_cast(doubleValue); + + if (!GetDoublePropertyValue(env, object, HEIGHT, doubleValue)) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "parameter error"); + errorMsg = "Parameter error. The type of rect.height must be double"; + return false; + } + rect.height = static_cast(doubleValue); + return true; +} + +bool UnwrapPageNodeInfo(ani_env *env, ani_object object, AbilityBase::PageNodeInfo &pageNodeInfo, + std::string &errorMsg) +{ + if (env == nullptr) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "env null"); + return false; + } + + if (!GetIntPropertyValue(env, object, ID, pageNodeInfo.id)) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "parameter error"); + errorMsg = "Parameter error. The type of pageNodeInfo.id must be int"; + return false; + } + + ani_ref aniAutoFillType = nullptr; + if (!GetRefProperty(env, object, AUTO_FILL_TYPE, aniAutoFillType) || aniAutoFillType == nullptr || + !AAFwk::AniEnumConvertUtil::EnumConvert_EtsToNative(env, + reinterpret_cast(aniAutoFillType), pageNodeInfo.autoFillType)) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "parameter error"); + errorMsg = "Parameter error. The type of pageNodeInfo.autoFillType must be AutoFillType"; + return false; + } + + if (!GetStringProperty(env, object, VALUE, pageNodeInfo.value)) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "parameter error"); + errorMsg = "Parameter error. The type of pageNodeInfo.value must be string"; + return false; + } + + if (IsExistsProperty(env, object, PLACEHOLDER) && + !GetStringProperty(env, object, PLACEHOLDER, pageNodeInfo.placeholder)) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "parameter error"); + errorMsg = "Parameter error. The type of pageNodeInfo.placeholder must be string"; + return false; + } + + ani_ref aniRect = nullptr; + if (!GetRefProperty(env, object, RECT, aniRect) || aniRect == nullptr || + !UnwrapAutoFillRect(env, reinterpret_cast(aniRect), pageNodeInfo.rect, errorMsg)) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "UnwrapAutoFillRect failed"); + return false; + } + + ani_boolean aniIsFocus = ANI_FALSE; + if (env->Object_GetPropertyByName_Boolean(object, IS_FOCUS, &aniIsFocus) != ANI_OK) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "parameter error"); + errorMsg = "Parameter error. The type of pageNodeInfo.isFocus must be boolean"; + return false; + } + pageNodeInfo.isFocus = static_cast(aniIsFocus); + return true; +} + +bool UnwrapViewData(ani_env *env, ani_object object, AbilityBase::ViewData &viewData, std::string &errorMsg) +{ + if (env == nullptr) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "env null"); + return false; + } + + if (!GetStringProperty(env, object, BUNDLE_NAME, viewData.bundleName)) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "parameter error"); + errorMsg = "Parameter error. The type of viewData.bundleName must be string"; + return false; + } + + if (!GetStringProperty(env, object, PAGE_URL, viewData.pageUrl)) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "parameter error"); + errorMsg = "Parameter error. The type of viewData.pageUrl must be string"; + return false; + } + + ani_ref aniNodes = nullptr; + if (!GetRefProperty(env, object, PAGE_NODE_INFOS, aniNodes) || aniNodes == nullptr) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "parameter error"); + errorMsg = "Parameter error. The type of viewData.pageNodeInfos must be array"; + return false; + } + ani_int length = 0; + ani_status status = env->Object_GetPropertyByName_Int(reinterpret_cast(aniNodes), "length", &length); + if (status != ANI_OK) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "Object_GetPropertyByName_Int failed: status: %{public}d", status); + return false; + } + viewData.nodes.clear(); + for (int i = 0; i < length; ++i) { + ani_ref aniPageNodeInfo = nullptr; + if ((status = env->Object_CallMethodByName_Ref(reinterpret_cast(aniNodes), + "$_get", "i:Y", &aniPageNodeInfo, (ani_int)i)) != ANI_OK) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "Object_CallMethodByName_Ref failed: status: %{public}d", status); + return false; + } + AbilityBase::PageNodeInfo pageNodeInfo; + if (!UnwrapPageNodeInfo(env, reinterpret_cast(aniPageNodeInfo), pageNodeInfo, errorMsg)) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "UnwrapPageNodeInfo failed"); + return false; + } + viewData.nodes.emplace_back(pageNodeInfo); + } + + ani_ref aniPageRect = nullptr; + if (!GetRefProperty(env, object, PAGE_RECT, aniPageRect) || aniPageRect == nullptr || + !UnwrapAutoFillRect(env, reinterpret_cast(aniPageRect), viewData.pageRect, errorMsg)) { + return false; + } + return true; +} + +bool UnwrapSaveRequest(ani_env *env, + ani_object object, AbilityRuntime::AutoFill::AutoFillRequest &request, std::string &errorMsg) +{ + if (env == nullptr) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "env null"); + return false; + } + + ani_ref aniViewData = nullptr; + if (!GetRefProperty(env, object, VIEW_DATA, aniViewData) || aniViewData == nullptr || + !UnwrapViewData(env, reinterpret_cast(aniViewData), request.viewData, errorMsg)) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "UnwrapViewData failed"); + return false; + } + return true; +} + +bool UnwrapFillRequest(ani_env *env, + ani_object object, AbilityRuntime::AutoFill::AutoFillRequest &request, std::string &errorMsg) +{ + if (env == nullptr) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "env null"); + return false; + } + + ani_ref aniAutoFillType = nullptr; + if (!GetRefProperty(env, object, TYPE, aniAutoFillType) || aniAutoFillType == nullptr || + !AAFwk::AniEnumConvertUtil::EnumConvert_EtsToNative(env, + reinterpret_cast(aniAutoFillType), request.autoFillType)) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "parameter error"); + errorMsg = "Parameter error. The type of request.autoFillType must be AutoFillType"; + return false; + } + + ani_ref aniViewData = nullptr; + if (!GetRefProperty(env, object, VIEW_DATA, aniViewData) || aniViewData == nullptr || + !UnwrapViewData(env, reinterpret_cast(aniViewData), request.viewData, errorMsg)) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "UnwrapViewData failed"); + return false; + } + + ani_ref aniAutoFillTriggerType = nullptr; + if (IsExistsProperty(env, object, TRIGGER_TYPE) && + (!GetRefProperty(env, object, TRIGGER_TYPE, aniAutoFillTriggerType) || aniAutoFillTriggerType == nullptr || + !AAFwk::AniEnumConvertUtil::EnumConvert_EtsToNative(env, + reinterpret_cast(aniAutoFillTriggerType), request.autoFillTriggerType))) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "parameter error"); + errorMsg = "Parameter error. The type of request.autoFillTriggerType must be AutoFillTriggerType"; + return false; + } + return true; +} +} // namespace AutoFillManagerEts +} // namespace OHOS diff --git a/frameworks/ets/ani/auto_fill_manager/src/ets_auto_fill_request_callback.cpp b/frameworks/ets/ani/auto_fill_manager/src/ets_auto_fill_request_callback.cpp new file mode 100644 index 0000000000..dc28a1d065 --- /dev/null +++ b/frameworks/ets/ani/auto_fill_manager/src/ets_auto_fill_request_callback.cpp @@ -0,0 +1,167 @@ +/* + * 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 "ets_auto_fill_request_callback.h" + +#include "ani_common_util.h" +#include "ets_auto_fill_manager_util.h" +#include "hilog_tag_wrapper.h" + +namespace OHOS { +namespace AutoFillManagerEts { +namespace { +constexpr int32_t ARGC_ZERO = 0; +constexpr int32_t ARGC_ONE = 1; +const std::string METHOD_ON_FILL_REQUEST_SUCCESS = "onSuccess"; +const std::string METHOD_ON_FILL_REQUEST_FAILED = "onFailure"; +} // namespace + +EtsAutoFillRequestCallback::EtsAutoFillRequestCallback(ani_vm *vm, int32_t instanceId, + AutoFillManagerFunc autoFillManagerFunc) + : vm_(vm), instanceId_(instanceId), autoFillManagerFunc_(autoFillManagerFunc) {} + +EtsAutoFillRequestCallback::~EtsAutoFillRequestCallback() {} + +void EtsAutoFillRequestCallback::OnFillRequestSuccess(const AbilityBase::ViewData &viewData) +{ + TAG_LOGD(AAFwkTag::AUTOFILLMGR, "onSuccess called"); + ani_env *env = GetAniEnv(); + ani_ref argv[ARGC_ONE] = { reinterpret_cast(WrapViewData(env, viewData)) }; + ETSCallFunction(METHOD_ON_FILL_REQUEST_SUCCESS, argv, ARGC_ONE); + if (autoFillManagerFunc_ != nullptr) { + autoFillManagerFunc_(instanceId_); + } +} + +void EtsAutoFillRequestCallback::OnFillRequestFailed(int32_t errCode, const std::string &fillContent, bool isPopup) +{ + TAG_LOGD(AAFwkTag::AUTOFILLMGR, "onFailure called"); + ani_env *env = GetAniEnv(); + ani_ref argv[ARGC_ONE] = { reinterpret_cast(WrapFillFailureResult(env, errCode)) }; + ETSCallFunction(METHOD_ON_FILL_REQUEST_FAILED, argv, ARGC_ONE); + if (autoFillManagerFunc_ != nullptr) { + autoFillManagerFunc_(instanceId_); + } +} + +void EtsAutoFillRequestCallback::Register(ani_object object) +{ + ani_env *env = GetAniEnv(); + if (env == nullptr) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "null env"); + return; + } + if (object == nullptr) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "null object"); + return; + } + + if (IsEtsCallbackEquals(callback_, object)) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "callback exist"); + return; + } + + callback_ = std::make_shared(); + if (callback_ == nullptr) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "null callback_"); + return; + } + + ani_ref objRef = nullptr; + ani_status status = ANI_ERROR; + status = env->GlobalReference_Create(object, &objRef); + if (status != ANI_OK) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "GlobalReference_Create failed status: %{public}d", status); + return; + } + + callback_->aniObj = object; + callback_->aniRef = objRef; +} + +void EtsAutoFillRequestCallback::ETSCallFunction(const std::string &methodName, ani_ref *argv, int32_t argc) +{ + ani_env *env = GetAniEnv(); + if (env == nullptr) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "null env"); + return; + } + if (callback_ == nullptr) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "null callback_"); + return; + } + + ani_status status = ANI_ERROR; + ani_ref funRef; + status = env->Object_GetPropertyByName_Ref(reinterpret_cast(callback_->aniRef), methodName.c_str(), + &funRef); + if (status != ANI_OK) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "Object_GetPropertyByName_Ref failed, status: %{public}d", status); + return; + } + if (!AppExecFwk::IsValidProperty(env, funRef)) { + TAG_LOGI(AAFwkTag::AUTOFILLMGR, "invalid property"); + return; + } + ani_ref result; + status = env->FunctionalObject_Call(reinterpret_cast(funRef), argc, argv, &result); + if (status != ANI_OK) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "FunctionalObject_Call failed, status: %{public}d", status); + } +} + +bool EtsAutoFillRequestCallback::IsEtsCallbackEquals(std::shared_ptr callback, + ani_object object) +{ + if (callback == nullptr) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "Invalid etsCallback"); + return false; + } + + ani_env *env = GetAniEnv(); + if (env == nullptr) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "null env"); + return false; + } + if (object == nullptr) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "null object"); + return false; + } + + ani_boolean isEquals = false; + if ((env->Reference_StrictEquals(reinterpret_cast(object), callback->aniRef, &isEquals)) != ANI_OK) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "Object not match"); + return false; + } + + return isEquals; +} + +ani_env *EtsAutoFillRequestCallback::GetAniEnv() +{ + TAG_LOGD(AAFwkTag::AUTOFILLMGR, "GetAniEnv call"); + if (vm_ == nullptr) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "null vm_"); + return nullptr; + } + ani_env* env = nullptr; + if (vm_->GetEnv(ANI_VERSION_1, &env) != ANI_OK) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "GetEnv failed"); + return nullptr; + } + return env; +} +} // namespace AutoFillManagerEts +} // namespace OHOS \ No newline at end of file diff --git a/frameworks/ets/ani/error_manager/src/error_manager_ani.cpp b/frameworks/ets/ani/error_manager/src/error_manager_ani.cpp index 8520e2e7cc..e73423cca0 100644 --- a/frameworks/ets/ani/error_manager/src/error_manager_ani.cpp +++ b/frameworks/ets/ani/error_manager/src/error_manager_ani.cpp @@ -183,6 +183,10 @@ public: if (!CheckDefaultFreezeError(env, function)) { return result; } + if (IsRefUndefined(env, function)) { + function = nullptr; + TAG_LOGI(AAFwkTag::JSNAPI, "func is undefined."); + } std::lock_guard lock(g_defaultFreezeMtx); if (g_defaultFreezeObserver.ref) { ani_wref weakRef; @@ -235,9 +239,6 @@ public: EtsErrorUtil::ThrowInvalidNumParametersError(env); return false; } - if (IsRefUndefined(env, function)) { - TAG_LOGI(AAFwkTag::JSNAPI, "func is undefined."); - } return true; } diff --git a/frameworks/ets/ani/ui_ability/include/ets_ability_context.h b/frameworks/ets/ani/ui_ability/include/ets_ability_context.h index fad7d1fd77..ad6367a385 100644 --- a/frameworks/ets/ani/ui_ability/include/ets_ability_context.h +++ b/frameworks/ets/ani/ui_ability/include/ets_ability_context.h @@ -98,6 +98,8 @@ public: ani_env *env, ani_object aniObj, ani_object wantObj, ani_string aniSpecifiedFlag, ani_object call); static void StartSelfUIAbilityInCurrentProcessWithOptions(ani_env *env, ani_object aniObj, ani_object wantObj, ani_string aniSpecifiedFlag, ani_object opt, ani_object call); + static void StartSelfUIAbilityInChildProcess( + ani_env *env, ani_object aniObj, ani_object wantObj, ani_string aniSpecifiedFlag, ani_object call); static void ConnectUIServiceExtension(ani_env *env, ani_object aniObj, ani_object wantObj, ani_object uiServiceExtConCallbackObj, ani_object callback); static void StartUIServiceExtension(ani_env *env, ani_object aniObj, @@ -155,6 +157,7 @@ public: static void OpenAtomicServiceCheck(ani_env *env, ani_object aniObj); static void StartAbilityForResultWithAccountCheck(ani_env *env, ani_object aniObj); static void SetOnNewWantSkipScenarios(ani_env *env, ani_object aniObj, ani_int etsScenarios, ani_object callback); + static void StartSelf(ani_env *env, ani_object aniObj, ani_object callback); #ifdef SUPPORT_GRAPHICS public: @@ -219,6 +222,8 @@ private: ani_env *env, ani_object aniObj, ani_string aniAppId, ani_object callbackObj, ani_object optionsObj); void OnStartSelfUIAbilityInCurrentProcess(ani_env *env, ani_object aniObj, ani_object wantObj, ani_string aniSpecifiedFlag, ani_object opt, ani_object call); + void OnStartSelfUIAbilityInChildProcess( + ani_env *env, ani_object wantObj, ani_string aniSpecifiedFlag, ani_object call); ani_long OnConnectServiceExtensionAbilityWithAccount(ani_env *env, ani_object aniObj, ani_object wantObj, ani_int aniAccountId, ani_object connectOptionsObj); void OnStopServiceExtensionAbilityWithAccount(ani_env *env, ani_object aniObj, ani_object wantObj, @@ -248,6 +253,7 @@ private: void OnStartAbilityWithAccount( ani_env *env, ani_object aniObj, ani_object aniWant, ani_int aniAccountId, ani_object aniOpt, ani_object call); void OnSetOnNewWantSkipScenarios(ani_env *env, ani_object aniObj, ani_int etsScenarios, ani_object callback); + void OnStartSelf(ani_env *env, ani_object callback); void OnStartAbilityAsCaller(ani_env *env, ani_object aniObj, ani_object wantObj, ani_object startOptionsObj, ani_object callbackObj); void UnwrapCompletionHandlerInStartOptions(ani_env *env, ani_object param, AAFwk::StartOptions &options); diff --git a/frameworks/ets/ani/ui_ability/src/ets_ability_context.cpp b/frameworks/ets/ani/ui_ability/src/ets_ability_context.cpp index a9065b5964..f1b6c8aa32 100644 --- a/frameworks/ets/ani/ui_ability/src/ets_ability_context.cpp +++ b/frameworks/ets/ani/ui_ability/src/ets_ability_context.cpp @@ -610,6 +610,19 @@ void EtsAbilityContext::StartSelfUIAbilityInCurrentProcessWithOptions( etsContext->OnStartSelfUIAbilityInCurrentProcess(env, aniObj, wantObj, aniSpecifiedFlag, opt, call); } +void EtsAbilityContext::StartSelfUIAbilityInChildProcess( + ani_env *env, ani_object aniObj, ani_object wantObj, ani_string aniSpecifiedFlag, ani_object call) +{ + HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__); + TAG_LOGD(AAFwkTag::CONTEXT, "StartSelfUIAbilityInChildProcess called"); + auto etsContext = GetEtsAbilityContext(env, aniObj); + if (etsContext == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "null etsContext"); + return; + } + etsContext->OnStartSelfUIAbilityInChildProcess(env, wantObj, aniSpecifiedFlag, call); +} + void EtsAbilityContext::OpenAtomicServiceCheck(ani_env *env, ani_object aniObj) { TAG_LOGD(AAFwkTag::CONTEXT, "OpenAtomicServiceCheck called"); @@ -888,6 +901,17 @@ void EtsAbilityContext::SetOnNewWantSkipScenarios(ani_env *env, ani_object aniOb etsContext->OnSetOnNewWantSkipScenarios(env, aniObj, etsScenarios, callback); } +void EtsAbilityContext::StartSelf(ani_env *env, ani_object aniObj, ani_object callback) +{ + TAG_LOGD(AAFwkTag::CONTEXT, "StartSelf called"); + auto etsContext = GetEtsAbilityContext(env, aniObj); + if (etsContext == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "null etsContext"); + return; + } + etsContext->OnStartSelf(env, callback); +} + int32_t EtsAbilityContext::GenerateRequestCode() { static int32_t curRequestCode_ = 0; @@ -1909,6 +1933,36 @@ void EtsAbilityContext::OnStartSelfUIAbilityInCurrentProcess(ani_env *env, ani_o AppExecFwk::AsyncCallback(env, call, aniObject, nullptr); } +void EtsAbilityContext::OnStartSelfUIAbilityInChildProcess( + ani_env *env, ani_object wantObj, ani_string aniSpecifiedFlag, ani_object call) +{ + HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__); + AAFwk::Want want; + if (!AppExecFwk::UnwrapWant(env, wantObj, want)) { + TAG_LOGE(AAFwkTag::CONTEXT, "Failed to parse want"); + EtsErrorUtil::ThrowInvalidParamError(env, "Parse param want failed, must be a Want"); + return; + } + std::string specifiedFlag; + if (!AppExecFwk::GetStdString(env, aniSpecifiedFlag, specifiedFlag)) { + TAG_LOGE(AAFwkTag::CONTEXT, "Failed to parse specifiedFlag"); + EtsErrorUtil::ThrowInvalidParamError(env, "Failed to parse specifiedFlag."); + return; + } + ErrCode innerErrCode = ERR_OK; + auto context = context_.lock(); + if (context == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "null context"); + innerErrCode = static_cast(AbilityErrorCode::ERROR_CODE_INVALID_CONTEXT); + ani_object aniObject = EtsErrorUtil::CreateErrorByNativeErr(env, innerErrCode); + AppExecFwk::AsyncCallback(env, call, aniObject, nullptr); + return; + } + innerErrCode = context->StartSelfUIAbilityInChildProcess(want, specifiedFlag); + ani_object aniObject = EtsErrorUtil::CreateErrorByNativeErr(env, innerErrCode); + AppExecFwk::AsyncCallback(env, call, aniObject, nullptr); +} + ani_long EtsAbilityContext::OnConnectServiceExtensionAbilityWithAccount(ani_env *env, ani_object aniObj, ani_object wantObj, ani_int aniAccountId, ani_object connectOptionsObj) { @@ -2990,6 +3044,24 @@ void EtsAbilityContext::OnSetOnNewWantSkipScenarios(ani_env *env, ani_object ani AppExecFwk::AsyncCallback(env, callback, EtsErrorUtil::CreateErrorByNativeErr(env, ERR_OK), nullptr); } +void EtsAbilityContext::OnStartSelf(ani_env *env, ani_object callback) +{ + TAG_LOGD(AAFwkTag::CONTEXT, "OnStartSelf called"); + if (env == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "null env"); + return; + } + auto context = context_.lock(); + if (context == nullptr) { + TAG_LOGE(AAFwkTag::CONTEXT, "null context"); + EtsErrorUtil::ThrowError(env, AbilityErrorCode::ERROR_CODE_INVALID_CONTEXT); + return; + } + ErrCode innerErrCode = context->StartSelf(); + AppExecFwk::AsyncCallback( + env, callback, EtsErrorUtil::CreateErrorByNativeErr(env, static_cast(innerErrCode)), nullptr); +} + namespace { bool BindNativeMethods(ani_env *env, ani_class &cls) { @@ -3083,6 +3155,9 @@ bool BindNativeMethods(ani_env *env, ani_class &cls) "C{@ohos.app.ability.Want.Want}C{std.core.String}C{@ohos.app.ability.StartOptions.StartOptions}C{utils." "AbilityUtils.AsyncCallbackWrapper}:", reinterpret_cast(EtsAbilityContext::StartSelfUIAbilityInCurrentProcessWithOptions) }, + ani_native_function { "nativeStartSelfUIAbilityInChildProcessSync", + "C{@ohos.app.ability.Want.Want}C{std.core.String}C{utils.AbilityUtils.AsyncCallbackWrapper}:", + reinterpret_cast(EtsAbilityContext::StartSelfUIAbilityInChildProcess) }, ani_native_function { "nativeOnSetRestoreEnabled", "z:", reinterpret_cast(EtsAbilityContext::NativeOnSetRestoreEnabled) }, ani_native_function { "nativeConnectServiceExtensionAbilityWithAccount", @@ -3159,6 +3234,9 @@ bool BindNativeMethods(ani_env *env, ani_class &cls) ani_native_function { "nativeSetOnNewWantSkipScenarios", "iC{utils.AbilityUtils.AsyncCallbackWrapper}:", reinterpret_cast(EtsAbilityContext::SetOnNewWantSkipScenarios) }, + ani_native_function { "nativeStartSelf", + "C{utils.AbilityUtils.AsyncCallbackWrapper}:", + reinterpret_cast(EtsAbilityContext::StartSelf) }, }; if ((status = env->Class_BindNativeMethods(cls, functions.data(), functions.size())) != ANI_OK) { TAG_LOGE(AAFwkTag::CONTEXT, "Class_BindNativeMethods failed status: %{public}d", status); diff --git a/frameworks/ets/ani/ui_ability/src/ets_ui_ability.cpp b/frameworks/ets/ani/ui_ability/src/ets_ui_ability.cpp index cc312586d7..c67e0fcb83 100644 --- a/frameworks/ets/ani/ui_ability/src/ets_ui_ability.cpp +++ b/frameworks/ets/ani/ui_ability/src/ets_ui_ability.cpp @@ -669,7 +669,7 @@ void EtsUIAbility::HandleNativeModule() } // Create NativeAbilityWrapper - auto wrapper = std::make_shared(); + auto wrapper = std::make_shared(); wrapper->instanceId = GetInstanceId(); wrapper->abilityName = GetAbilityName(); wrapper->etsAbilityObj = reinterpret_cast(etsAbilityObj_->aniRef); @@ -1542,7 +1542,9 @@ void EtsUIAbility::ExecuteInsightIntentMoveToForeground(const Want &want, } ability->CallOnForegroundFunc(want); }; - callback->Push(asyncCallback); + if (!CheckIsSilentForeground()) { + callback->Push(asyncCallback); + } const WantParams &wantParams = want.GetParams(); std::string arkTSMode = wantParams.GetStringParam(AppExecFwk::INSIGHT_INTENT_ARKTS_MODE); InsightIntentExecutorInfo executeInfo; diff --git a/frameworks/ets/ani/want/src/ani_want_module.cpp b/frameworks/ets/ani/want/src/ani_want_module.cpp index 1040662928..442d99bef0 100644 --- a/frameworks/ets/ani/want/src/ani_want_module.cpp +++ b/frameworks/ets/ani/want/src/ani_want_module.cpp @@ -269,7 +269,7 @@ ani_boolean EtsWantParams::NativeSetLongParam(ani_env *env, ani_object, ani_long return false; } - params->SetParam(keyString, AAFwk::Long::Box(value)); + params->SetParam(keyString, AAFwk::Long::Box64(value)); return true; } @@ -580,7 +580,7 @@ bool EtsWantParams::SetArrayLong(ani_env *env, const std::string &key, ani_objec sptr ao = sptr::MakeSptr(length, AAFwk::g_IID_ILong); for (int i = 0; i < length; i++) { - ao->Set(i, AAFwk::Long::Box(nativeArray[i])); + ao->Set(i, AAFwk::Long::Box64(nativeArray[i])); } wantParams.SetParam(key, ao); return true; diff --git a/frameworks/ets/ets/@ohos.app.ability.AbilityConstant.ets b/frameworks/ets/ets/@ohos.app.ability.AbilityConstant.ets index 7a0781e06f..2d46e4e930 100644 --- a/frameworks/ets/ets/@ohos.app.ability.AbilityConstant.ets +++ b/frameworks/ets/ets/@ohos.app.ability.AbilityConstant.ets @@ -17,7 +17,7 @@ import appManager from '@ohos.app.ability.appManager'; namespace AbilityConstant { - const REASON_MESSAGE_DESKTOP_SHORTCUT = 'ReasonMessage_DesktopShortcut'; + const REASON_MESSAGE_DESKTOP_SHORTCUT: string = 'ReasonMessage_DesktopShortcut'; export interface LaunchParam { launchReason: LaunchReason; @@ -91,7 +91,7 @@ namespace AbilityConstant { WINDOW_MODE_SPLIT_PRIMARY = 100, WINDOW_MODE_SPLIT_SECONDARY = 101, WINDOW_MODE_FLOATING = 102, - WINDOW_MODE_SPLIT = 103 + WINDOW_MODE_SPLIT = 105 } export enum OnSaveResult { diff --git a/frameworks/ets/ets/@ohos.app.ability.InsightIntentDecorator.ets b/frameworks/ets/ets/@ohos.app.ability.InsightIntentDecorator.ets new file mode 100644 index 0000000000..0fc0ab7440 --- /dev/null +++ b/frameworks/ets/ets/@ohos.app.ability.InsightIntentDecorator.ets @@ -0,0 +1,21 @@ +/* + * 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 insightIntent from '@ohos.app.ability.insightIntent'; + +export enum LinkParamCategory { + LINK = 'link', + WANT = 'want' +} diff --git a/frameworks/ets/ets/@ohos.app.ability.autoFillManager.ets b/frameworks/ets/ets/@ohos.app.ability.autoFillManager.ets index fef51cee76..eb0978223b 100644 --- a/frameworks/ets/ets/@ohos.app.ability.autoFillManager.ets +++ b/frameworks/ets/ets/@ohos.app.ability.autoFillManager.ets @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 Huawei Device Co., Ltd. + * Copyright (c) 2025-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 @@ -20,6 +20,7 @@ import { FillRequest as _FillRequest, SaveRequest as _SaveRequest} from 'applica import { UpdateRequest as _UpdateRequest, FillResponse as _FillResponse} from 'application.AutoFillRequest'; import { FillRequestCallback as _FillRequestCallback } from 'application.AutoFillRequest'; import { SaveRequestCallback as _SaveRequestCallback } from 'application.AutoFillRequest'; +import { FillFailureResult as _FillFailureResult } from 'application.AutoFillRequest'; import _CustomData from 'application.CustomData'; import _AutoFillRect from 'application.AutoFillRect'; import _AutoFillPopupConfig from 'application.AutoFillPopupConfig'; @@ -30,6 +31,8 @@ import { PopupPlacement as _PopupPlacement } from 'application.AutoFillPopupConf namespace autoFillManager { export type OnSuccessFn = () => void; export type OnFailureFn = () => void; + export type OnFillSuccessFn = (viewData: ViewData) => void; + export type OnFillFailureFn = (result: FillFailureResult) => void; loadLibraryWithPermissionCheck("ability_auto_fill_manager_ani_kit.z", "@ohos.app.ability.autoFillManager"); @@ -43,13 +46,38 @@ namespace autoFillManager { onFailure: OnFailureFn = () => {}; } + export interface AutoFillCallback { + onSuccess: OnFillSuccessFn; + onFailure: OnFillFailureFn; + } + + class AutoFillCallbackInner implements AutoFillCallback { + onSuccess: OnFillSuccessFn = (viewData: ViewData) => {}; + onFailure: OnFillFailureFn = (result: FillFailureResult) => {}; + } + export native function requestAutoSaveWithScope(callback?: AutoSaveCallback): void; + export native function requestAutoSaveWithRequest(request: SaveRequest, callback?: AutoSaveCallback): void; + export native function requestAutoFillWithScope(request: FillRequest, callback?: AutoFillCallback): void; + export function requestAutoSave(context: UIContext, callback?: AutoSaveCallback): void { context.runScopedTask(() => { requestAutoSaveWithScope(callback); }); } + export function requestAutoSave(context: UIContext, request: SaveRequest, callback?: AutoSaveCallback): void { + context.runScopedTask(() => { + requestAutoSaveWithRequest(request, callback); + }); + } + + export function requestAutoFill(context: UIContext, request: FillRequest, callback?: AutoFillCallback): void { + context.runScopedTask(() => { + requestAutoFillWithScope(request, callback); + }); + } + export type ViewData = _ViewData; export type PageNodeInfo = _PageNodeInfo; export type AutoFillType = _AutoFillType; @@ -59,6 +87,7 @@ namespace autoFillManager { export type FillResponse = _FillResponse; export type FillRequestCallback = _FillRequestCallback; export type SaveRequestCallback = _SaveRequestCallback; + export type FillFailureResult = _FillFailureResult; export type CustomData = _CustomData; export type AutoFillRect = _AutoFillRect; export type AutoFillPopupConfig = _AutoFillPopupConfig; diff --git a/frameworks/ets/ets/@ohos.app.ability.dialogRequest.ets b/frameworks/ets/ets/@ohos.app.ability.dialogRequest.ets index 5790a4d587..38fd5e7510 100644 --- a/frameworks/ets/ets/@ohos.app.ability.dialogRequest.ets +++ b/frameworks/ets/ets/@ohos.app.ability.dialogRequest.ets @@ -41,11 +41,11 @@ namespace dialogRequest { cleaner.cleanToReqCallback(); } - let destroyRegisterReqInfo = new FinalizationRegistry(callbackToReqInfo); - let unregisterTokenReqInfo = new object(); + let destroyRegisterReqInfo: FinalizationRegistry = new FinalizationRegistry(callbackToReqInfo); + let unregisterTokenReqInfo: object = new object(); - let destroyRegisterReqCallback = new FinalizationRegistry(callbackToReqCallback); - let unregisterTokenReqCallback = new object(); + let destroyRegisterReqCallback: FinalizationRegistry = new FinalizationRegistry(callbackToReqCallback); + let unregisterTokenReqCallback: object = new object(); export interface WindowRect { left: int; diff --git a/frameworks/ets/ets/@ohos.app.ability.insightIntentDriver.ets b/frameworks/ets/ets/@ohos.app.ability.insightIntentDriver.ets index 7d79aeb887..6dd79b0fb5 100644 --- a/frameworks/ets/ets/@ohos.app.ability.insightIntentDriver.ets +++ b/frameworks/ets/ets/@ohos.app.ability.insightIntentDriver.ets @@ -34,6 +34,7 @@ export default namespace insightIntentDriver { uris?: Array; flags?: int; userId?: int; + deviceId?: string; } export interface InsightIntentInfoFilter { diff --git a/frameworks/ets/ets/@ohos.app.ability.verticalPanelManager.ets b/frameworks/ets/ets/@ohos.app.ability.verticalPanelManager.ets index ce3f7fa1f1..3509afa6f1 100644 --- a/frameworks/ets/ets/@ohos.app.ability.verticalPanelManager.ets +++ b/frameworks/ets/ets/@ohos.app.ability.verticalPanelManager.ets @@ -71,13 +71,13 @@ export default namespace verticalPanelManager { onResult?: OnResultFn; } - export const SOURCE_APP_BUNDLE_NAME = 'bundleName'; + export const SOURCE_APP_BUNDLE_NAME: string = 'bundleName'; - export const SOURCE_APP_MODULE_NAME = 'moduleName'; + export const SOURCE_APP_MODULE_NAME: string = 'moduleName'; - export const SOURCE_APP_ABILITY_NAME = 'abilityName'; + export const SOURCE_APP_ABILITY_NAME: string = 'abilityName'; - export const SOURCE_APP_WINDOW_ID = 'windowId'; + export const SOURCE_APP_WINDOW_ID: string = 'windowId'; - export const SOURCE_APP_SCREEN_MODE = 'screenMode'; + export const SOURCE_APP_SCREEN_MODE: string = 'screenMode'; } \ No newline at end of file diff --git a/frameworks/ets/ets/@ohos.app.ability.wantAgent.ets b/frameworks/ets/ets/@ohos.app.ability.wantAgent.ets index abd6d45822..ac3fb57456 100644 --- a/frameworks/ets/ets/@ohos.app.ability.wantAgent.ets +++ b/frameworks/ets/ets/@ohos.app.ability.wantAgent.ets @@ -42,8 +42,8 @@ namespace wantAgent { cleaner.clean(); } - let destroyRegister = new FinalizationRegistry(callback); - let unregisterToken = new object(); + let destroyRegister: FinalizationRegistry = new FinalizationRegistry(callback); + let unregisterToken: object = new object(); class WantAgentCls { wantAgentPtr: long = 0; diff --git a/frameworks/ets/ets/BUILD.gn b/frameworks/ets/ets/BUILD.gn index 74910f06b8..42ed98f074 100644 --- a/frameworks/ets/ets/BUILD.gn +++ b/frameworks/ets/ets/BUILD.gn @@ -1035,6 +1035,25 @@ ohos_prebuilt_etc("ability_runtime_insight_intent_context_abc_etc") { deps = [ ":ability_runtime_insight_intent_context_abc" ] } +generate_static_abc("ability_runtime_insight_intent_decorator_abc") { + base_url = "./" + files = [ + "./@ohos.app.ability.InsightIntentDecorator.ets", + ] + + is_boot_abc = "True" + device_dst_file = + "/system/framework/ability_runtime_insight_intent_decorator_abc.abc" +} + +ohos_prebuilt_etc("ability_runtime_insight_intent_decorator_abc_etc") { + source = "$target_out_dir/ability_runtime_insight_intent_decorator_abc.abc" + module_install_dir = "framework" + subsystem_name = "ability" + part_name = "ability_runtime" + deps = [ ":ability_runtime_insight_intent_decorator_abc" ] +} + generate_static_abc("ability_runtime_insight_intent_driver_abc") { base_url = "./" files = [ "./@ohos.app.ability.insightIntentDriver.ets" ] @@ -2183,6 +2202,7 @@ group("ets_packages") { ":ability_runtime_hyper_snap_manager_abc_etc", ":ability_runtime_insight_intent_abc_etc", ":ability_runtime_insight_intent_context_abc_etc", + ":ability_runtime_insight_intent_decorator_abc_etc", ":ability_runtime_insight_intent_driver_abc_etc", ":ability_runtime_insight_intent_executor_abc_etc", ":ability_runtime_insight_intent_provider_abc_etc", diff --git a/frameworks/ets/ets/application/AbilityDelegator.ets b/frameworks/ets/ets/application/AbilityDelegator.ets index 80a88b01de..cbdb01e32f 100644 --- a/frameworks/ets/ets/application/AbilityDelegator.ets +++ b/frameworks/ets/ets/application/AbilityDelegator.ets @@ -95,7 +95,7 @@ export interface AbilityDelegator { startAbility(want: Want): Promise; - getCurrentTopAbility(callback: AsyncCallback); + getCurrentTopAbility(callback: AsyncCallback): void; getCurrentTopAbility(): Promise; diff --git a/frameworks/ets/ets/application/ApplicationContext.ets b/frameworks/ets/ets/application/ApplicationContext.ets index cd52662835..00dc112ed5 100644 --- a/frameworks/ets/ets/application/ApplicationContext.ets +++ b/frameworks/ets/ets/application/ApplicationContext.ets @@ -26,6 +26,7 @@ import EnvironmentCallback from '@ohos.app.ability.EnvironmentCallback'; import type ApplicationStateChangeCallback from '@ohos.app.ability.ApplicationStateChangeCallback'; import { AbilityUtils } from '../utils/AbilityUtils'; import systemConfiguration from '../@ohos.app.ability.systemConfiguration'; +import UIAbility from '@ohos.app.ability.UIAbility'; export class Cleaner { public ptr: long = 0; @@ -93,6 +94,10 @@ export default class ApplicationContext extends Context { public native nativeOffApplicationStateChangeSync(callback?: ApplicationStateChangeCallback): void; public native nativegetCurrentAppCloneIndex(): int; public native nativegetCurrentInstanceKey(): string; + public native nativeEnableDelayedProcessExitSync(callback: AsyncCallbackWrapper): void; + public native nativeDisableDelayedProcessExitSync(callback: AsyncCallbackWrapper): void; + public native nativeStartSelfUIAbilitySync(want: Want, callback: AsyncCallbackWrapper): void; + private native nativeGetUIAbilityByInstanceId(instanceId: string): UIAbility; private static native nativeTransferStatic(input: ESValue, type: string): Object; private static native nativeTransferDynamic(input: Object): ESValue; private static contextType: string = 'ApplicationContext'; @@ -227,6 +232,57 @@ export default class ApplicationContext extends Context { this.nativerestartApp(want); } + enableDelayedProcessExit(): Promise { + let p = + new Promise((resolve: (data: undefined) => void, reject: (err: BusinessError) => void): void => { + let asyncCbWrapper = new AsyncCallbackWrapper((err: BusinessError | null) => { + if (err == null || err.code == 0) { + resolve(undefined); + } else { + reject(err); + } + }); + taskpool.execute((): void => { + this.nativeEnableDelayedProcessExitSync(asyncCbWrapper); + }); + }); + return p; + } + + disableDelayedProcessExit(): Promise { + let p = + new Promise((resolve: (data: undefined) => void, reject: (err: BusinessError) => void): void => { + let asyncCbWrapper = new AsyncCallbackWrapper((err: BusinessError | null) => { + if (err == null || err.code == 0) { + resolve(undefined); + } else { + reject(err); + } + }); + taskpool.execute((): void => { + this.nativeDisableDelayedProcessExitSync(asyncCbWrapper); + }); + }); + return p; + } + + startSelfUIAbility(want: Want): Promise { + let p = + new Promise((resolve: (data: undefined) => void, reject: (err: BusinessError) => void): void => { + let asyncCbWrapper = new AsyncCallbackWrapper((err: BusinessError | null) => { + if (err == null || err.code == 0) { + resolve(undefined); + } else { + reject(err); + } + }); + taskpool.execute((): void => { + this.nativeStartSelfUIAbilitySync(want, asyncCbWrapper); + }); + }); + return p; + } + getAllRunningInstanceKeys(): Promise> { let p = new Promise>((resolve: (data: Array) => void, reject: (err: BusinessError) => void): void => { @@ -343,6 +399,10 @@ export default class ApplicationContext extends Context { return this.nativegetCurrentAppCloneIndex(); } + getUIAbilityByInstanceId(instanceId: string): UIAbility { + return this.nativeGetUIAbilityByInstanceId(instanceId); + } + static transferStatic(input: Any): Object { let type: string = ''; try { diff --git a/frameworks/ets/ets/application/AutoFillRequest.ets b/frameworks/ets/ets/application/AutoFillRequest.ets index acd4576b36..126d4c4cbb 100644 --- a/frameworks/ets/ets/application/AutoFillRequest.ets +++ b/frameworks/ets/ets/application/AutoFillRequest.ets @@ -45,6 +45,9 @@ export interface SaveRequestCallback { onSuccess(): void; onFailure(): void; } +export interface FillFailureResult { + errCode: int; +} class FillRequestInner implements FillRequest { public type: AutoFillType = AutoFillType.UNSPECIFIED; @@ -62,6 +65,9 @@ class UpdateRequestInner implements UpdateRequest { class FillResponseInner implements FillResponse { public viewData!: ViewData; } +class FillFailureResultInner implements FillFailureResult { + public errCode: int; +} export class Cleaner { public ptr: long = 0; diff --git a/frameworks/ets/ets/application/UIAbilityContext.ets b/frameworks/ets/ets/application/UIAbilityContext.ets index 23384c15b0..3c59285221 100644 --- a/frameworks/ets/ets/application/UIAbilityContext.ets +++ b/frameworks/ets/ets/application/UIAbilityContext.ets @@ -157,6 +157,8 @@ export default class UIAbilityContext extends Context { private native nativeStartSelfUIAbilityInCurrentProcessSync(want: Want, specifiedFlag: string, options: StartOptions, callback: AsyncCallbackWrapper): void; + private native nativeStartSelfUIAbilityInChildProcessSync(want: Want, specifiedFlag: string, callback: AsyncCallbackWrapper): void; + private native nativeOnSetRestoreEnabled(enabled: boolean): void; private native nativeConnectServiceExtensionAbilityWithAccount(want: Want, accountId: int, @@ -218,6 +220,8 @@ export default class UIAbilityContext extends Context { private native nativeSetOnNewWantSkipScenarios(scenarios: int, callback: AsyncCallbackWrapper): void; + private native nativeStartSelf(callback: AsyncCallbackWrapper): void; + startAbility(want: Want, callback: AsyncCallback): void { let myCall = new AsyncCallbackWrapper(callback); taskpool.execute((): void => { @@ -731,6 +735,23 @@ hideAbility(): Promise { }); } + startSelfUIAbilityInChildProcess(want: Want, specifiedFlag: string): Promise { + return new Promise((resolve: (data: undefined) => void, reject: (err: BusinessError) => void): void => { + let syncCall = new AsyncCallbackWrapper((err: BusinessError | null) => { + if (err == null || err.code == 0) { + resolve(undefined); + } else { + reject(err); + } + }); + taskpool.execute((): void => { + this.nativeStartSelfUIAbilityInChildProcessSync(want, specifiedFlag, syncCall); + }).catch((err: Error): void => { + reject(err as BusinessError); + }); + }); + } + setAbilityInstanceInfo(label: string, icon: image.PixelMap): Promise { this.nativeSetAbilityInstanceInfoCheck(label, icon); let p:Promise = new Promise((resolve: (data:undefined)=>void, reject: (err: BusinessError)=>void):void => { @@ -1120,4 +1141,21 @@ hideAbility(): Promise { }); }); } + + startSelf(): Promise { + return new Promise((resolve: (data: undefined) => void, reject: (err: BusinessError) => void): void => { + let callback = new AsyncCallbackWrapper((err: BusinessError | null) => { + if (err == null || err.code == 0) { + resolve(undefined); + } else { + reject(err); + } + }); + taskpool.execute((): void => { + this.nativeStartSelf(callback); + }).catch((err: Error): void => { + reject(err as BusinessError); + }); + }); + } } diff --git a/frameworks/ets/ets/utils/AbilityUtils.ets b/frameworks/ets/ets/utils/AbilityUtils.ets index 20be18b1c6..0ac2d7ebce 100644 --- a/frameworks/ets/ets/utils/AbilityUtils.ets +++ b/frameworks/ets/ets/utils/AbilityUtils.ets @@ -70,7 +70,7 @@ export class AbilityUtils { return false; } - public static createBusinessError(code: int, message: string) { + public static createBusinessError(code: int, message: string): BusinessError { let err = new BusinessError(); err.code = code; err.name = 'Error'; diff --git a/frameworks/js/napi/ability_context/ability_context.js b/frameworks/js/napi/ability_context/ability_context.js index 77e3a512e3..7e46d50be4 100644 --- a/frameworks/js/napi/ability_context/ability_context.js +++ b/frameworks/js/napi/ability_context/ability_context.js @@ -277,6 +277,10 @@ class AbilityContext extends Context { return this.__context_impl__.startSelfUIAbilityInCurrentProcess(want, specifiedFlag, options); } + startSelfUIAbilityInChildProcess(want, specifiedFlag) { + return this.__context_impl__.startSelfUIAbilityInChildProcess(want, specifiedFlag); + } + restartApp(want) { return this.__context_impl__.restartApp(want); } diff --git a/frameworks/js/napi/ability_manager/js_ability_manager.cpp b/frameworks/js/napi/ability_manager/js_ability_manager.cpp index 04a57e8a88..195cc06d51 100644 --- a/frameworks/js/napi/ability_manager/js_ability_manager.cpp +++ b/frameworks/js/napi/ability_manager/js_ability_manager.cpp @@ -16,6 +16,7 @@ #include "js_ability_manager.h" #include +#include #include #include #include @@ -993,11 +994,12 @@ private: loadedCallback_.clear(); return CreateJsUndefined(env); } - auto it = std::find_if(loadedCallback_.begin(), loadedCallback_.end(), [&](const auto &cb) { + auto it = std::find_if(loadedCallback_.begin(), loadedCallback_.end(), + [env, targetFunc = info.argv[INDEX_ZERO]](const auto &cb) { napi_value jsFunc = nullptr; bool isEquals = false; napi_get_reference_value(env, cb.first, &jsFunc); - napi_strict_equals(env, info.argv[INDEX_ZERO], jsFunc, &isEquals); + napi_strict_equals(env, targetFunc, jsFunc, &isEquals); return isEquals; }); if (it == loadedCallback_.end()) { @@ -1072,11 +1074,12 @@ private: destroyCallback_.clear(); return CreateJsUndefined(env); } - auto it = std::find_if(destroyCallback_.begin(), destroyCallback_.end(), [&](const auto &cb) { + auto it = std::find_if(destroyCallback_.begin(), destroyCallback_.end(), + [env, targetFunc = info.argv[INDEX_ZERO]](const auto &cb) { napi_value jsFunc = nullptr; bool isEquals = false; napi_get_reference_value(env, cb.first, &jsFunc); - napi_strict_equals(env, info.argv[INDEX_ZERO], jsFunc, &isEquals); + napi_strict_equals(env, targetFunc, jsFunc, &isEquals); return isEquals; }); if (it == destroyCallback_.end()) { diff --git a/frameworks/js/napi/app/application_context/application_context.js b/frameworks/js/napi/app/application_context/application_context.js index af66e814ae..20cccd7fd7 100644 --- a/frameworks/js/napi/app/application_context/application_context.js +++ b/frameworks/js/napi/app/application_context/application_context.js @@ -353,6 +353,18 @@ class ApplicationContext { return this.__context_impl__.restartApp(want); } + enableDelayedProcessExit() { + return this.__context_impl__.enableDelayedProcessExit(); + } + + disableDelayedProcessExit() { + return this.__context_impl__.disableDelayedProcessExit(); + } + + startSelfUIAbility(want) { + return this.__context_impl__.startSelfUIAbility(want); + } + setSupportedProcessCache(isSupport) { return this.__context_impl__.setSupportedProcessCache(isSupport); } diff --git a/frameworks/js/napi/app/error_manager/js_error_manager.cpp b/frameworks/js/napi/app/error_manager/js_error_manager.cpp index db12063bbb..61a3de8643 100644 --- a/frameworks/js/napi/app/error_manager/js_error_manager.cpp +++ b/frameworks/js/napi/app/error_manager/js_error_manager.cpp @@ -1104,6 +1104,7 @@ private: } if (CheckTypeForNapiValue(env, function, napi_undefined)) { TAG_LOGI(AAFwkTag::JSNAPI, "func is undefined."); + function = nullptr; } std::lock_guard lock(g_defaultFreezeMtx); napi_value object = nullptr; @@ -1579,15 +1580,21 @@ private: return result; } + static napi_value DeleteUnhandledRejectionObservers(napi_env env) + { + auto res = CreateJsUndefined(env); + for (auto& iter : unhandledRejectionObservers) { + napi_delete_reference(env, iter); + } + unhandledRejectionObservers.clear(); + return res; + } + napi_value OnOffUnhandledRejection(napi_env env, size_t argc, napi_value* argv) { auto res = CreateJsUndefined(env); if (argc == ARGC_ONE) { - for (auto& iter : unhandledRejectionObservers) { - napi_delete_reference(env, iter); - } - unhandledRejectionObservers.clear(); - return res; + return DeleteUnhandledRejectionObservers(env); } napi_value function = argv[INDEX_ONE]; if (function == nullptr || CheckTypeForNapiValue(env, function, napi_null)) { @@ -1595,6 +1602,10 @@ private: ThrowInvalidNumParametersError(env); return CreateJsUndefined(env); } + if (CheckTypeForNapiValue(env, function, napi_undefined)) { + TAG_LOGI(AAFwkTag::JSNAPI, "undefined function."); + return DeleteUnhandledRejectionObservers(env); + } for (auto& iter : unhandledRejectionObservers) { napi_value observer = nullptr; NAPI_CALL(env, napi_get_reference_value(env, iter, &observer)); diff --git a/frameworks/js/napi/app/js_app_manager/js_app_manager.cpp b/frameworks/js/napi/app/js_app_manager/js_app_manager.cpp index ee4b0a9b37..1c51eac195 100644 --- a/frameworks/js/napi/app/js_app_manager/js_app_manager.cpp +++ b/frameworks/js/napi/app/js_app_manager/js_app_manager.cpp @@ -60,7 +60,6 @@ constexpr size_t ARGC_TWO = 2; constexpr size_t ARGC_THREE = 3; constexpr size_t ARGC_FOUR = 4; constexpr const char* ON_OFF_TYPE = "applicationState"; -constexpr const char* ON_OFF_TYPE_SYNC = "applicationStateEvent"; constexpr const char* ON_OFF_TYPE_APP_FOREGROUND_STATE = "appForegroundState"; constexpr const char* ON_OFF_TYPE_ABILITY_FIRST_FRAME_STATE = "abilityFirstFrameState"; @@ -271,9 +270,7 @@ private: { TAG_LOGD(AAFwkTag::APPMGR, "called"); std::string type = ParseParamType(env, argc, argv); - if (type == ON_OFF_TYPE_SYNC) { - return OnOnNew(env, argc, argv); - } else if (type == ON_OFF_TYPE_APP_FOREGROUND_STATE) { + if (type == ON_OFF_TYPE_APP_FOREGROUND_STATE) { return OnOnForeground(env, argc, argv); } else if (type == ON_OFF_TYPE_ABILITY_FIRST_FRAME_STATE) { #ifdef SUPPORT_SCREEN @@ -357,51 +354,6 @@ private: return CreateJsValue(env, observerId); } - napi_value OnOnNew(napi_env env, size_t argc, napi_value* argv) - { - TAG_LOGD(AAFwkTag::APPMGR, "called"); - if (argc < ARGC_TWO) { // support 2 or 3 params, if > 3 params, ignore other params - TAG_LOGE(AAFwkTag::APPMGR, "invalid argc"); - ThrowTooFewParametersError(env); - return CreateJsUndefined(env); - } - if (!AppExecFwk::IsTypeForNapiValue(env, argv[INDEX_ONE], napi_object)) { - TAG_LOGE(AAFwkTag::APPMGR, "Invalid param"); - ThrowInvalidParamError(env, "Parse param observer failed, must be a ApplicationStateObserver."); - return CreateJsUndefined(env); - } - std::vector bundleNameList; - if (argc > ARGC_TWO) { - AppExecFwk::UnwrapArrayStringFromJS(env, argv[INDEX_TWO], bundleNameList); - } - if (observerSync_ == nullptr) { - observerSync_ = new JSAppStateObserver(env); - } - if (appManager_ == nullptr || observerSync_ == nullptr) { - TAG_LOGE(AAFwkTag::APPMGR, "null appMgr or observer"); - ThrowError(env, AbilityErrorCode::ERROR_CODE_INNER); - return CreateJsUndefined(env); - } - if (observerSync_->GetJsObserverMapSize() == 0) { - int32_t ret = appManager_->RegisterApplicationStateObserver(observerSync_, bundleNameList); - if (ret == 0) { - TAG_LOGD(AAFwkTag::APPMGR, "success"); - } else { - TAG_LOGE(AAFwkTag::APPMGR, "err:%{public}d", ret); - ThrowErrorByNativeErr(env, ret); - return CreateJsUndefined(env); - } - } - int32_t observerId = serialNumber_; - observerSync_->AddJsObserverObject(observerId, argv[INDEX_ONE]); - if (serialNumber_ < INT32_MAX) { - serialNumber_++; - } else { - serialNumber_ = 0; - } - return CreateJsValue(env, observerId); - } - napi_value OnOnForeground(napi_env env, size_t argc, napi_value *argv) { TAG_LOGD(AAFwkTag::APPMGR, "called"); @@ -441,9 +393,7 @@ private: { TAG_LOGD(AAFwkTag::APPMGR, "called"); std::string type = ParseParamType(env, argc, argv); - if (type == ON_OFF_TYPE_SYNC) { - return OnOffNew(env, argc, argv); - } else if (type == ON_OFF_TYPE_APP_FOREGROUND_STATE) { + if (type == ON_OFF_TYPE_APP_FOREGROUND_STATE) { return OnOffForeground(env, argc, argv); } else if (type == ON_OFF_TYPE_ABILITY_FIRST_FRAME_STATE) { #ifdef SUPPORT_SCREEN @@ -609,44 +559,6 @@ private: return result; } - napi_value OnOffNew(napi_env env, size_t argc, napi_value* argv) - { - TAG_LOGD(AAFwkTag::APPMGR, "called"); - if (argc < ARGC_TWO) { - TAG_LOGE(AAFwkTag::APPMGR, "invalid argc"); - ThrowTooFewParametersError(env); - return CreateJsUndefined(env); - } - int32_t observerId = -1; - if (!ConvertFromJsValue(env, argv[INDEX_ONE], observerId)) { - TAG_LOGE(AAFwkTag::APPMGR, "Parse observerId failed"); - ThrowInvalidParamError(env, "Parse param observerId failed, must be a number."); - return CreateJsUndefined(env); - } - - if (observerSync_ == nullptr || appManager_ == nullptr) { - TAG_LOGE(AAFwkTag::APPMGR, "null observer or appMgr"); - ThrowError(env, AbilityErrorCode::ERROR_CODE_INNER); - return CreateJsUndefined(env); - } - if (!observerSync_->FindObserverByObserverId(observerId)) { - TAG_LOGE(AAFwkTag::APPMGR, "not find observer:%{public}d", static_cast(observerId)); - ThrowInvalidParamError(env, "not find observerId."); - return CreateJsUndefined(env); - } - observerSync_->RemoveJsObserverObject(observerId); - if (observerSync_->GetJsObserverMapSize() == 0) { - int32_t ret = appManager_->UnregisterApplicationStateObserver(observerSync_); - if (ret != 0) { - TAG_LOGE(AAFwkTag::APPMGR, "err:%{public}d", ret); - ThrowError(env, AbilityErrorCode::ERROR_CODE_INNER); - return CreateJsUndefined(env); - } - } - TAG_LOGI(AAFwkTag::APPMGR, "unregister success size:%{public}zu", observerSync_->GetJsObserverMapSize()); - return CreateJsUndefined(env); - } - napi_value OnOffForeground(napi_env env, size_t argc, napi_value *argv) { TAG_LOGD(AAFwkTag::APPMGR, "called"); diff --git a/frameworks/js/napi/app/recovery/BUILD.gn b/frameworks/js/napi/app/recovery/BUILD.gn index 1fa8e3c129..5e89c4a209 100644 --- a/frameworks/js/napi/app/recovery/BUILD.gn +++ b/frameworks/js/napi/app/recovery/BUILD.gn @@ -51,6 +51,12 @@ ohos_shared_library("apprecovery_napi") { "ipc:ipc_single", "napi:ace_napi", ] + if (hiviewdfx_runtime_api_metrics_enable) { + defines += [ "APPRECOVERY_ENABLE_API_METRICS" ] + external_deps += [ + "api_metrics:histogrammanager" + ] + } relative_install_dir = "module/app/ability" diff --git a/frameworks/js/napi/app/recovery/app_recovery_api.cpp b/frameworks/js/napi/app/recovery/app_recovery_api.cpp index d8ef23c487..612689b269 100644 --- a/frameworks/js/napi/app/recovery/app_recovery_api.cpp +++ b/frameworks/js/napi/app/recovery/app_recovery_api.cpp @@ -29,6 +29,9 @@ #include "want.h" #include "ability_manager_client.h" #include "exit_reason.h" +#ifdef APPRECOVERY_ENABLE_API_METRICS +#include "histogram_plugin_macros.h" +#endif namespace OHOS { namespace AbilityRuntime { @@ -75,6 +78,9 @@ public: private: napi_value OnEnableAppRecovery(napi_env env, const size_t argc, napi_value* argv) { + #ifdef APPRECOVERY_ENABLE_API_METRICS + HISTOGRAM_BOOLEAN("AbilityKit.ApiCall.enableAppRecovery", 1); + #endif size_t parameterCount = argc; napi_value result = CreateJsUndefined(env); constexpr int maxCount = 3; @@ -135,6 +141,9 @@ private: napi_value OnSaveAppState(napi_env env, const size_t argc, napi_value* argv) { + #ifdef APPRECOVERY_ENABLE_API_METRICS + HISTOGRAM_BOOLEAN("AbilityKit.ApiCall.saveAppState", 1); + #endif if (argc > 1) { TAG_LOGE(AAFwkTag::RECOVERY, "invalid argc"); return CreateJsValue(env, false); @@ -158,6 +167,9 @@ private: napi_value OnRestartApp(napi_env env, const size_t argc, napi_value* argv) { + #ifdef APPRECOVERY_ENABLE_API_METRICS + HISTOGRAM_BOOLEAN("AbilityKit.ApiCall.restartApp", 1); + #endif if (argc != 0) { TAG_LOGE(AAFwkTag::RECOVERY, "invalid argc"); return CreateJsUndefined(env); @@ -178,6 +190,9 @@ private: napi_value OnSetRestartWant(napi_env env, const size_t argc, napi_value* argv) { + #ifdef APPRECOVERY_ENABLE_API_METRICS + HISTOGRAM_BOOLEAN("AbilityKit.ApiCall.setRestartWant", 1); + #endif if (argc != 1) { TAG_LOGE(AAFwkTag::RECOVERY, "invalid argc"); return CreateJsUndefined(env); diff --git a/frameworks/js/napi/auto_fill_manager/BUILD.gn b/frameworks/js/napi/auto_fill_manager/BUILD.gn index 212881675f..363e4f85b3 100644 --- a/frameworks/js/napi/auto_fill_manager/BUILD.gn +++ b/frameworks/js/napi/auto_fill_manager/BUILD.gn @@ -1,4 +1,4 @@ -# Copyright (c) 2023-2025 Huawei Device Co., Ltd. +# Copyright (c) 2023-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 @@ -18,6 +18,8 @@ ohos_shared_library("autofillmanager_napi") { sources = [ "auto_fill_manager_module.cpp", "js_auto_fill_manager.cpp", + "js_auto_fill_manager_util.cpp", + "js_auto_fill_request_callback.cpp", "js_auto_save_request_callback.cpp", ] @@ -32,9 +34,9 @@ ohos_shared_library("autofillmanager_napi") { ] deps = [ - "${ability_runtime_innerkits_path}/auto_fill_manager:auto_fill_manager", - "${ability_runtime_innerkits_path}/runtime:runtime", - "${ability_runtime_napi_path}/inner/napi_common:napi_common", + "${ability_runtime_innerkits_path}/auto_fill_manager", + "${ability_runtime_innerkits_path}/runtime", + "${ability_runtime_napi_path}/inner/napi_common", "${ability_runtime_native_path}/ability/native:ability_business_error", ] diff --git a/frameworks/js/napi/auto_fill_manager/js_auto_fill_manager.cpp b/frameworks/js/napi/auto_fill_manager/js_auto_fill_manager.cpp index bf4c74f99d..b4d68e926d 100644 --- a/frameworks/js/napi/auto_fill_manager/js_auto_fill_manager.cpp +++ b/frameworks/js/napi/auto_fill_manager/js_auto_fill_manager.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023-2024 Huawei Device Co., Ltd. + * Copyright (c) 2023-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 @@ -19,14 +19,19 @@ #include "auto_fill_manager.h" #include "hilog_tag_wrapper.h" #include "ipc_skeleton.h" +#include "js_auto_fill_manager_util.h" #include "js_error_utils.h" +#include "napi_common_util.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 size_t ARGC_ONE = 1; +constexpr size_t ARGC_TWO = 2; +constexpr size_t ARGC_THREE = 3; } // namespace void JsAutoFillManager::Finalizer(napi_env env, void *data, void *hint) @@ -40,6 +45,50 @@ napi_value JsAutoFillManager::RequestAutoSave(napi_env env, napi_callback_info i GET_NAPI_INFO_AND_CALL(env, info, JsAutoFillManager, OnRequestAutoSave); } +static bool ConvertSaveArgs(napi_env env, NapiCallbackInfo &info, AutoFill::AutoFillRequest &request, + std::shared_ptr &saveCallback, bool &hasRequest) +{ + if (info.argc == ARGC_TWO) { + if (!CheckTypeForNapiValue(env, info.argv[INDEX_ONE], napi_object)) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "Second parameter error"); + ThrowInvalidParamError(env, "Parameter error. The second parameter is not of type object"); + return false; + } + if (AppExecFwk::IsExistsByPropertyName(env, info.argv[INDEX_ONE], "viewData")) { + std::string errorMsg; + if (!UnwrapSaveRequest(env, info.argv[INDEX_ONE], request, errorMsg)) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "failed to parse type"); + ThrowInvalidParamError(env, errorMsg.c_str()); + return false; + } + hasRequest = true; + } else { + saveCallback->Register(info.argv[INDEX_ONE]); + } + } else if (info.argc == ARGC_THREE) { + if (!CheckTypeForNapiValue(env, info.argv[INDEX_ONE], napi_object)) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "Second parameter error"); + ThrowInvalidParamError(env, "Parameter error. The second parameter is not of type object"); + return false; + } + std::string errorMsg; + if (!UnwrapSaveRequest(env, info.argv[INDEX_ONE], request, errorMsg)) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "failed to parse type"); + ThrowInvalidParamError(env, errorMsg.c_str()); + return false; + } + hasRequest = true; + + if (!CheckTypeForNapiValue(env, info.argv[INDEX_TWO], napi_object)) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "Third parameter error"); + ThrowInvalidParamError(env, "Parameter error. The third parameter is not of type object"); + return false; + } + saveCallback->Register(info.argv[INDEX_TWO]); + } + return true; +} + napi_value JsAutoFillManager::OnRequestAutoSave(napi_env env, NapiCallbackInfo &info) { TAG_LOGD(AAFwkTag::AUTOFILLMGR, "called"); @@ -53,19 +102,17 @@ napi_value JsAutoFillManager::OnRequestAutoSave(napi_env env, NapiCallbackInfo & napi_value instanceIdValue = nullptr; if (napi_get_named_property(env, info.argv[INDEX_ZERO], "instanceId_", &instanceIdValue) != napi_ok) { TAG_LOGE(AAFwkTag::AUTOFILLMGR, "get function by name failed"); - ThrowError(env, static_cast(AbilityErrorCode::ERROR_CODE_INVALID_PARAM), - "Parameter error. Get instance id failed."); + ThrowInvalidParamError(env, "Parameter error. Get instance id failed"); return CreateJsUndefined(env); } int32_t instanceId = -1; if (!ConvertFromJsValue(env, instanceIdValue, instanceId)) { TAG_LOGE(AAFwkTag::AUTOFILLMGR, "failed to parse type"); - ThrowError(env, static_cast(AbilityErrorCode::ERROR_CODE_INVALID_PARAM), - "Parameter error. Parse instance id failed."); + ThrowInvalidParamError(env, "Parameter error. Parse instance id failed"); return CreateJsUndefined(env); } - auto saveCallback = GetCallbackByInstanceId(instanceId); + auto saveCallback = GetSaveCallbackByInstanceId(instanceId); if (saveCallback != nullptr) { TAG_LOGE(AAFwkTag::AUTOFILLMGR, "there are other requests in progress"); ThrowError(env, AbilityErrorCode::ERROR_CODE_INNER); @@ -80,21 +127,17 @@ napi_value JsAutoFillManager::OnRequestAutoSave(napi_env env, NapiCallbackInfo & return CreateJsUndefined(env); } - if (info.argc != ARGC_ONE) { - if (!CheckTypeForNapiValue(env, info.argv[INDEX_ONE], napi_object)) { - TAG_LOGE(AAFwkTag::AUTOFILLMGR, "Second parameter error"); - ThrowError(env, static_cast(AbilityErrorCode::ERROR_CODE_INVALID_PARAM), - "Parameter error. The second parameter is not of type callback."); - return CreateJsUndefined(env); - } - saveCallback->Register(info.argv[INDEX_ONE]); + AutoFill::AutoFillRequest request; + bool hasRequest = false; + if (!ConvertSaveArgs(env, info, request, saveCallback, hasRequest)) { + return CreateJsUndefined(env); } - OnRequestAutoSaveInner(env, instanceId, saveCallback); + OnRequestAutoSaveInner(env, instanceId, request, saveCallback, hasRequest); return CreateJsUndefined(env); } -void JsAutoFillManager::OnRequestAutoSaveInner(napi_env env, int32_t instanceId, - const std::shared_ptr &saveRequestCallback) +void JsAutoFillManager::OnRequestAutoSaveInner(napi_env env, int32_t instanceId, AutoFill::AutoFillRequest &request, + const std::shared_ptr &saveRequestCallback, const bool hasRequest) { #ifdef SUPPORT_GRAPHICS auto uiContent = Ace::UIContent::GetUIContent(instanceId); @@ -103,26 +146,29 @@ void JsAutoFillManager::OnRequestAutoSaveInner(napi_env env, int32_t instanceId, ThrowError(env, AbilityErrorCode::ERROR_CODE_INNER); return; } - if (uiContent->CheckNeedAutoSave()) { - AutoFill::AutoFillRequest request; - uiContent->DumpViewData(request.viewData, request.autoFillType); - request.autoFillCommand = AutoFill::AutoFillCommand::SAVE; - AbilityRuntime::AutoFill::AutoFillResult result; - auto ret = AutoFillManager::GetInstance().RequestAutoSave(uiContent, request, saveRequestCallback, result); - if (ret != ERR_OK) { - TAG_LOGE(AAFwkTag::AUTOFILLMGR, "RequestAutoSave error[%{public}d]", ret); - ThrowError(env, GetJsErrorCodeByNativeError(ret)); + if (!hasRequest) { + if (!uiContent->CheckNeedAutoSave()) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "no need auto save"); return; } - std::lock_guard lock(mutexLock_); - saveRequestObject_.emplace(instanceId, saveRequestCallback); + uiContent->DumpViewData(request.viewData, request.autoFillType); } + request.autoFillCommand = AutoFill::AutoFillCommand::SAVE; + AbilityRuntime::AutoFill::AutoFillResult result; + auto ret = AutoFillManager::GetInstance().RequestAutoSave(uiContent, request, saveRequestCallback, result); + if (ret != ERR_OK) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "RequestAutoSave error[%{public}d]", ret); + ThrowError(env, GetJsErrorCodeByNativeError(ret)); + return; + } + std::lock_guard lock(saveMutex_); + saveRequestObject_.emplace(instanceId, saveRequestCallback); #endif // SUPPORT_GRAPHICS } -std::shared_ptr JsAutoFillManager::GetCallbackByInstanceId(int32_t instanceId) +std::shared_ptr JsAutoFillManager::GetSaveCallbackByInstanceId(int32_t instanceId) { - std::lock_guard lock(mutexLock_); + std::lock_guard lock(saveMutex_); auto iter = saveRequestObject_.find(instanceId); if (iter != saveRequestObject_.end()) { return iter->second.lock(); @@ -132,13 +178,143 @@ std::shared_ptr JsAutoFillManager::GetCallbackByInsta void JsAutoFillManager::OnRequestAutoSaveDone(int32_t instanceId) { - std::lock_guard lock(mutexLock_); + std::lock_guard lock(saveMutex_); auto iter = saveRequestObject_.find(instanceId); if (iter != saveRequestObject_.end()) { saveRequestObject_.erase(iter); } } +napi_value JsAutoFillManager::RequestAutoFill(napi_env env, napi_callback_info info) +{ + GET_NAPI_INFO_AND_CALL(env, info, JsAutoFillManager, OnRequestAutoFill); +} + +static bool ConvertFillArgs(napi_env env, NapiCallbackInfo &info, AutoFill::AutoFillRequest &request, + std::shared_ptr &fillCallback) +{ + if (info.argc == ARGC_TWO) { + if (!CheckTypeForNapiValue(env, info.argv[INDEX_ONE], napi_object)) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "Second parameter error"); + ThrowInvalidParamError(env, "Parameter error. The second parameter is not of type object"); + return false; + } + std::string errorMsg; + if (!UnwrapFillRequest(env, info.argv[INDEX_ONE], request, errorMsg)) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "failed to parse type"); + ThrowInvalidParamError(env, errorMsg.c_str()); + return false; + } + } else if (info.argc == ARGC_THREE) { + if (!CheckTypeForNapiValue(env, info.argv[INDEX_ONE], napi_object)) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "Second parameter error"); + ThrowInvalidParamError(env, "Parameter error. The second parameter is not of type object"); + return false; + } + std::string errorMsg; + if (!UnwrapFillRequest(env, info.argv[INDEX_ONE], request, errorMsg)) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "failed to parse type"); + ThrowInvalidParamError(env, errorMsg.c_str()); + return false; + } + + if (!CheckTypeForNapiValue(env, info.argv[INDEX_TWO], napi_object)) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "Third parameter error"); + ThrowInvalidParamError(env, "Parameter error. The third parameter is not of type object"); + return false; + } + fillCallback->Register(info.argv[INDEX_TWO]); + } + return true; +} + +napi_value JsAutoFillManager::OnRequestAutoFill(napi_env env, NapiCallbackInfo &info) +{ + TAG_LOGD(AAFwkTag::AUTOFILLMGR, "called"); + HandleScope handleScope(env); + if (info.argc < ARGC_ONE) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "invalid argc"); + ThrowTooFewParametersError(env); + return CreateJsUndefined(env); + } + + napi_value instanceIdValue = nullptr; + if (napi_get_named_property(env, info.argv[INDEX_ZERO], "instanceId_", &instanceIdValue) != napi_ok) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "get function by name failed"); + ThrowInvalidParamError(env, "Parameter error. Get instance id failed"); + return CreateJsUndefined(env); + } + int32_t instanceId = -1; + if (!ConvertFromJsValue(env, instanceIdValue, instanceId)) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "failed to parse type"); + ThrowInvalidParamError(env, "Parameter error. Parse instance id failed"); + return CreateJsUndefined(env); + } + + auto fillCallback = GetFillCallbackByInstanceId(instanceId); + if (fillCallback != nullptr) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "there are other requests in progress"); + ThrowError(env, AbilityErrorCode::ERROR_CODE_INNER); + return CreateJsUndefined(env); + } + + auto autoFillMangerFunc = [this](const int32_t arg) { this->OnRequestAutoFillDone(arg); }; + fillCallback = std::make_shared(env, instanceId, autoFillMangerFunc); + if (fillCallback == nullptr) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "null fillCallback"); + ThrowError(env, AbilityErrorCode::ERROR_CODE_INNER); + return CreateJsUndefined(env); + } + + AutoFill::AutoFillRequest request; + if (!ConvertFillArgs(env, info, request, fillCallback)) { + return CreateJsUndefined(env); + } + OnRequestAutoFillInner(env, instanceId, request, fillCallback); + return CreateJsUndefined(env); +} + +void JsAutoFillManager::OnRequestAutoFillInner(napi_env env, int32_t instanceId, AutoFill::AutoFillRequest &request, + const std::shared_ptr &fillRequestCallback) +{ +#ifdef SUPPORT_GRAPHICS + auto uiContent = Ace::UIContent::GetUIContent(instanceId); + if (uiContent == nullptr) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "null uiContent"); + ThrowError(env, AbilityErrorCode::ERROR_CODE_INNER); + return; + } + request.autoFillCommand = AutoFill::AutoFillCommand::FILL; + AbilityRuntime::AutoFill::AutoFillResult result; + auto ret = AutoFillManager::GetInstance().RequestAutoFill(uiContent, request, fillRequestCallback, result); + if (ret != ERR_OK) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "RequestAutoFill error[%{public}d]", ret); + ThrowError(env, GetJsErrorCodeByNativeError(ret)); + return; + } + std::lock_guard lock(fillMutex_); + fillRequestObject_.emplace(instanceId, fillRequestCallback); +#endif // SUPPORT_GRAPHICS +} + +std::shared_ptr JsAutoFillManager::GetFillCallbackByInstanceId(int32_t instanceId) +{ + std::lock_guard lock(fillMutex_); + auto iter = fillRequestObject_.find(instanceId); + if (iter != fillRequestObject_.end()) { + return iter->second.lock(); + } + return nullptr; +} + +void JsAutoFillManager::OnRequestAutoFillDone(int32_t instanceId) +{ + std::lock_guard lock(fillMutex_); + auto iter = fillRequestObject_.find(instanceId); + if (iter != fillRequestObject_.end()) { + fillRequestObject_.erase(iter); + } +} void SetAutoFillTypePropertyPartTwo(napi_env env, napi_value objValue) { @@ -254,6 +430,26 @@ napi_value CreateJsPopupPlacement(napi_env env) return handleEscape.Escape(objValue); } +napi_value CreateJsAutoFillTriggerType(napi_env env) +{ + TAG_LOGD(AAFwkTag::AUTOFILLMGR, "called"); + HandleEscape handleEscape(env); + napi_value objValue = nullptr; + napi_status createStatus = napi_create_object(env, &objValue); + if (createStatus != napi_ok || objValue == nullptr) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "napi_create_reference failed, %{public}d", createStatus); + return nullptr; + } + + napi_set_named_property(env, objValue, "AUTO_REQUEST", + CreateJsValue(env, AutoFill::AutoFillTriggerType::AUTO_REQUEST)); + napi_set_named_property(env, objValue, "MANUAL_REQUEST", + CreateJsValue(env, AutoFill::AutoFillTriggerType::MANUAL_REQUEST)); + napi_set_named_property(env, objValue, "PASTE_REQUEST", + CreateJsValue(env, AutoFill::AutoFillTriggerType::PASTE_REQUEST)); + return handleEscape.Escape(objValue); +} + napi_value JsAutoFillManagerInit(napi_env env, napi_value exportObj) { TAG_LOGD(AAFwkTag::AUTOFILLMGR, "called"); @@ -269,8 +465,11 @@ napi_value JsAutoFillManagerInit(napi_env env, napi_value exportObj) napi_set_named_property(env, exportObj, "PopupPlacement", CreateJsPopupPlacement(env)); + napi_set_named_property(env, exportObj, "AutoFillTriggerType", CreateJsAutoFillTriggerType(env)); + const char *moduleName = "JsAutoFillManager"; BindNativeFunction(env, exportObj, "requestAutoSave", moduleName, JsAutoFillManager::RequestAutoSave); + BindNativeFunction(env, exportObj, "requestAutoFill", moduleName, JsAutoFillManager::RequestAutoFill); return CreateJsUndefined(env); } diff --git a/frameworks/js/napi/auto_fill_manager/js_auto_fill_manager.h b/frameworks/js/napi/auto_fill_manager/js_auto_fill_manager.h index a8a3ebcc21..0eeca4073e 100644 --- a/frameworks/js/napi/auto_fill_manager/js_auto_fill_manager.h +++ b/frameworks/js/napi/auto_fill_manager/js_auto_fill_manager.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023-2024 Huawei Device Co., Ltd. + * Copyright (c) 2023-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 @@ -19,6 +19,7 @@ #include #include "js_auto_fill_manager.h" +#include "js_auto_fill_request_callback.h" #include "js_auto_save_request_callback.h" #include "js_runtime.h" #include "js_runtime_utils.h" @@ -32,16 +33,25 @@ public: ~JsAutoFillManager() = default; static void Finalizer(napi_env env, void *data, void *hint); static napi_value RequestAutoSave(napi_env env, napi_callback_info info); + static napi_value RequestAutoFill(napi_env env, napi_callback_info info); private: napi_value OnRequestAutoSave(napi_env env, NapiCallbackInfo &info); - void OnRequestAutoSaveInner(napi_env env, int32_t instanceId, - const std::shared_ptr &saveRequestCallback); - std::shared_ptr GetCallbackByInstanceId(int32_t instanceId); + void OnRequestAutoSaveInner(napi_env env, int32_t instanceId, AutoFill::AutoFillRequest &request, + const std::shared_ptr &saveRequestCallback, const bool hasRequest); + std::shared_ptr GetSaveCallbackByInstanceId(int32_t instanceId); void OnRequestAutoSaveDone(int32_t instanceId); - std::mutex mutexLock_; + napi_value OnRequestAutoFill(napi_env env, NapiCallbackInfo &info); + void OnRequestAutoFillInner(napi_env env, int32_t instanceId, AutoFill::AutoFillRequest &request, + const std::shared_ptr &fillRequestCallback); + std::shared_ptr GetFillCallbackByInstanceId(int32_t instanceId); + void OnRequestAutoFillDone(int32_t instanceId); + + std::mutex saveMutex_; std::map> saveRequestObject_; + std::mutex fillMutex_; + std::map> fillRequestObject_; }; napi_value JsAutoFillManagerInit(napi_env env, napi_value exportObj); } // namespace AbilityRuntime diff --git a/frameworks/js/napi/auto_fill_manager/js_auto_fill_manager_util.cpp b/frameworks/js/napi/auto_fill_manager/js_auto_fill_manager_util.cpp new file mode 100644 index 0000000000..41334e3641 --- /dev/null +++ b/frameworks/js/napi/auto_fill_manager/js_auto_fill_manager_util.cpp @@ -0,0 +1,353 @@ +/* + * 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_auto_fill_manager_util.h" + +#include "hilog_tag_wrapper.h" +#include "napi_common_util.h" + +namespace OHOS { +namespace AbilityRuntime { +namespace { +constexpr const char* LEFT = "left"; +constexpr const char* TOP = "top"; +constexpr const char* WIDTH = "width"; +constexpr const char* HEIGHT = "height"; +constexpr const char* ID = "id"; +constexpr const char* AUTO_FILL_TYPE = "autoFillType"; +constexpr const char* VALUE = "value"; +constexpr const char* PLACEHOLDER = "placeholder"; +constexpr const char* RECT = "rect"; +constexpr const char* IS_FOCUS = "isFocus"; +constexpr const char* BUNDLE_NAME = "bundleName"; +constexpr const char* PAGE_URL = "pageUrl"; +constexpr const char* PAGE_NODE_INFOS = "pageNodeInfos"; +constexpr const char* PAGE_RECT = "pageRect"; +constexpr const char* ERR_CODE = "errCode"; +constexpr const char* VIEW_DATA = "viewData"; +constexpr const char* TYPE = "type"; +constexpr const char* TRIGGER_TYPE = "triggerType"; +} +using namespace AppExecFwk; + +napi_value WrapAutoFillRect(napi_env env, const AbilityBase::Rect &rect) +{ + HandleEscape handleEscape(env); + napi_value jsObject = nullptr; + NAPI_CALL(env, napi_create_object(env, &jsObject)); + + if (!SetPropertyValueByPropertyName(env, jsObject, LEFT, WrapDoubleToJS(env, rect.left))) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "set left failed"); + return CreateJsUndefined(env); + } + + if (!SetPropertyValueByPropertyName(env, jsObject, TOP, WrapDoubleToJS(env, rect.top))) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "set top failed"); + return CreateJsUndefined(env); + } + + if (!SetPropertyValueByPropertyName(env, jsObject, WIDTH, WrapDoubleToJS(env, rect.width))) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "set width failed"); + return CreateJsUndefined(env); + } + + if (!SetPropertyValueByPropertyName(env, jsObject, HEIGHT, WrapDoubleToJS(env, rect.height))) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "set height failed"); + return CreateJsUndefined(env); + } + + return handleEscape.Escape(jsObject); +} + +napi_value WrapPageNodeInfo(napi_env env, const AbilityBase::PageNodeInfo &pageNodeInfo) +{ + HandleEscape handleEscape(env); + napi_value jsObject = nullptr; + NAPI_CALL(env, napi_create_object(env, &jsObject)); + + if (!SetPropertyValueByPropertyName(env, jsObject, ID, WrapInt32ToJS(env, pageNodeInfo.id))) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "set id failed"); + return CreateJsUndefined(env); + } + + if (!SetPropertyValueByPropertyName(env, + jsObject, AUTO_FILL_TYPE, WrapInt32ToJS(env, static_cast(pageNodeInfo.autoFillType)))) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "set autoFillType failed"); + return CreateJsUndefined(env); + } + + if (!SetPropertyValueByPropertyName(env, jsObject, VALUE, WrapStringToJS(env, pageNodeInfo.value))) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "set value failed"); + return CreateJsUndefined(env); + } + + if (!SetPropertyValueByPropertyName(env, jsObject, PLACEHOLDER, WrapStringToJS(env, pageNodeInfo.placeholder))) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "set placeholder failed"); + return CreateJsUndefined(env); + } + + if (!SetPropertyValueByPropertyName(env, jsObject, RECT, WrapAutoFillRect(env, pageNodeInfo.rect))) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "set rect failed"); + return CreateJsUndefined(env); + } + + if (!SetPropertyValueByPropertyName(env, jsObject, IS_FOCUS, WrapBoolToJS(env, pageNodeInfo.isFocus))) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "set isFocus failed"); + return CreateJsUndefined(env); + } + + return handleEscape.Escape(jsObject); +} + +napi_value WrapViewData(const napi_env env, const AbilityBase::ViewData &viewData) +{ + HandleEscape handleEscape(env); + napi_value jsObject = nullptr; + NAPI_CALL(env, napi_create_object(env, &jsObject)); + + if (!SetPropertyValueByPropertyName(env, jsObject, BUNDLE_NAME, WrapStringToJS(env, viewData.bundleName))) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "set bundleName failed"); + return CreateJsUndefined(env); + } + + if (!SetPropertyValueByPropertyName(env, jsObject, PAGE_URL, WrapStringToJS(env, viewData.pageUrl))) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "set pageUrl failed"); + return CreateJsUndefined(env); + } + + napi_value jsArray = nullptr; + NAPI_CALL(env, napi_create_array(env, &jsArray)); + uint32_t index = 0; + for (const auto &element : viewData.nodes) { + napi_value jsSubValue = WrapPageNodeInfo(env, element); + if (jsSubValue != nullptr && napi_set_element(env, jsArray, index, jsSubValue) == napi_ok) { + ++index; + } else { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "Set element fail"); + } + } + if (!SetPropertyValueByPropertyName(env, jsObject, PAGE_NODE_INFOS, jsArray)) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "set pageNodeInfos failed"); + return CreateJsUndefined(env); + } + + if (!SetPropertyValueByPropertyName(env, jsObject, PAGE_RECT, WrapAutoFillRect(env, viewData.pageRect))) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "set pageRect failed"); + return CreateJsUndefined(env); + } + return handleEscape.Escape(jsObject); +} + +napi_value WrapFillFailureResult(napi_env env, int32_t errCode) +{ + HandleEscape handleEscape(env); + napi_value jsObject = nullptr; + NAPI_CALL(env, napi_create_object(env, &jsObject)); + + if (!SetPropertyValueByPropertyName(env, jsObject, ERR_CODE, WrapInt32ToJS(env, errCode))) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "set errCode failed"); + return CreateJsUndefined(env); + } + return handleEscape.Escape(jsObject); +} + +bool UnwrapAutoFillRect(napi_env env, napi_value jsValue, AbilityBase::Rect &rect, std::string &errorMsg) +{ + if (!IsTypeForNapiValue(env, jsValue, napi_object)) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "parameter error"); + errorMsg = "Parameter error. The type of jsValue must be AutoFillRect"; + return false; + } + + double doubleValue = 0; + if (!UnwrapDoubleByPropertyName(env, jsValue, LEFT, doubleValue)) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "parameter error"); + errorMsg = "Parameter error. The type of rect.left must be number"; + return false; + } + rect.left = static_cast(doubleValue); + + if (!UnwrapDoubleByPropertyName(env, jsValue, TOP, doubleValue)) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "parameter error"); + errorMsg = "Parameter error. The type of rect.top must be number"; + return false; + } + rect.top = static_cast(doubleValue); + + if (!UnwrapDoubleByPropertyName(env, jsValue, WIDTH, doubleValue)) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "parameter error"); + errorMsg = "Parameter error. The type of rect.width must be number"; + return false; + } + rect.width = static_cast(doubleValue); + + if (!UnwrapDoubleByPropertyName(env, jsValue, HEIGHT, doubleValue)) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "parameter error"); + errorMsg = "Parameter error. The type of rect.height must be number"; + return false; + } + rect.height = static_cast(doubleValue); + return true; +} + +bool UnwrapPageNodeInfo(napi_env env, napi_value jsValue, AbilityBase::PageNodeInfo &pageNodeInfo, + std::string &errorMsg) +{ + if (!IsTypeForNapiValue(env, jsValue, napi_object)) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "parameter error"); + errorMsg = "Parameter error. The type of jsValue must be PageNodeInfo"; + return false; + } + + if (!UnwrapInt32ByPropertyName(env, jsValue, ID, pageNodeInfo.id)) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "parameter error"); + errorMsg = "Parameter error. The type of pageNodeInfo.id must be number"; + return false; + } + + int32_t int32Value = 0; + if (!UnwrapInt32ByPropertyName(env, jsValue, AUTO_FILL_TYPE, int32Value)) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "parameter error"); + errorMsg = "Parameter error. The type of pageNodeInfo.autoFillType must be AutoFillType"; + return false; + } + pageNodeInfo.autoFillType = static_cast(int32Value); + + if (!UnwrapStringByPropertyName(env, jsValue, VALUE, pageNodeInfo.value)) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "parameter error"); + errorMsg = "Parameter error. The type of pageNodeInfo.value must be string"; + return false; + } + + if (IsExistsByPropertyName(env, jsValue, PLACEHOLDER) && + !UnwrapStringByPropertyName(env, jsValue, PLACEHOLDER, pageNodeInfo.placeholder)) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "parameter error"); + errorMsg = "Parameter error. The type of pageNodeInfo.placeholder must be string"; + return false; + } + + napi_value jsRect = GetPropertyValueByPropertyName(env, jsValue, RECT, napi_object); + if (jsRect != nullptr && !UnwrapAutoFillRect(env, jsRect, pageNodeInfo.rect, errorMsg)) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "UnwrapAutoFillRect error"); + return false; + } + + if (!UnwrapBooleanByPropertyName(env, jsValue, IS_FOCUS, pageNodeInfo.isFocus)) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "parameter error"); + errorMsg = "Parameter error. The type of pageNodeInfo.isFocus must be boolean"; + return false; + } + return true; +} + +bool UnwrapViewData(napi_env env, napi_value jsValue, AbilityBase::ViewData &viewData, std::string &errorMsg) +{ + if (!IsTypeForNapiValue(env, jsValue, napi_object)) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "parameter error"); + errorMsg = "Parameter error. The type of jsValue must be ViewData"; + return false; + } + + if (!UnwrapStringByPropertyName(env, jsValue, BUNDLE_NAME, viewData.bundleName)) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "parameter error"); + errorMsg = "Parameter error. The type of viewData.bundleName must be string"; + return false; + } + + if (!UnwrapStringByPropertyName(env, jsValue, PAGE_URL, viewData.pageUrl)) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "parameter error"); + errorMsg = "Parameter error. The type of viewData.pageUrl must be string"; + return false; + } + + napi_value jsPageNodeInfos = GetPropertyValueByPropertyName(env, jsValue, PAGE_NODE_INFOS, napi_object); + uint32_t arraySize = 0; + if (jsPageNodeInfos == nullptr || !IsArrayForNapiValue(env, jsPageNodeInfos, arraySize)) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "parameter error"); + errorMsg = "Parameter error. The type of viewData.pageNodeInfos must be array"; + return false; + } + viewData.nodes.clear(); + for (uint32_t i = 0; i < arraySize; ++i) { + napi_value jsPageNodeInfo = nullptr; + if (napi_get_element(env, jsPageNodeInfos, i, &jsPageNodeInfo) != napi_ok) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "napi_get_element failed"); + return false; + } + AbilityBase::PageNodeInfo pageNodeInfo; + if (!UnwrapPageNodeInfo(env, jsPageNodeInfo, pageNodeInfo, errorMsg)) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "UnwrapPageNodeInfo failed"); + return false; + } + viewData.nodes.emplace_back(pageNodeInfo); + } + + napi_value jsPageRect = GetPropertyValueByPropertyName(env, jsValue, PAGE_RECT, napi_object); + if (jsPageRect != nullptr && !UnwrapAutoFillRect(env, jsPageRect, viewData.pageRect, errorMsg)) { + return false; + } + return true; +} + +bool UnwrapSaveRequest(napi_env env, napi_value jsValue, AutoFill::AutoFillRequest &request, std::string &errorMsg) +{ + if (!IsTypeForNapiValue(env, jsValue, napi_object)) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "parameter error"); + errorMsg = "Parameter error. The type of jsValue must be SaveRequest"; + return false; + } + + napi_value jsViewData = GetPropertyValueByPropertyName(env, jsValue, VIEW_DATA, napi_object); + if (jsViewData != nullptr && !UnwrapViewData(env, jsViewData, request.viewData, errorMsg)) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "UnwrapViewData failed"); + return false; + } + return true; +} + +bool UnwrapFillRequest(napi_env env, napi_value jsValue, AutoFill::AutoFillRequest &request, std::string &errorMsg) +{ + if (!IsTypeForNapiValue(env, jsValue, napi_object)) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "parameter error"); + errorMsg = "Parameter error. The type of jsValue must be FillRequest"; + return false; + } + + int32_t int32Value; + if (!UnwrapInt32ByPropertyName(env, jsValue, TYPE, int32Value)) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "parameter error"); + errorMsg = "Parameter error. The type of request.autoFillType must be AutoFillType"; + return false; + } + request.autoFillType = static_cast(int32Value); + + napi_value jsViewData = GetPropertyValueByPropertyName(env, jsValue, VIEW_DATA, napi_object); + if (jsViewData != nullptr && !UnwrapViewData(env, jsViewData, request.viewData, errorMsg)) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "UnwrapViewData failed"); + return false; + } + + if (IsExistsByPropertyName(env, jsValue, TRIGGER_TYPE)) { + if (!UnwrapInt32ByPropertyName(env, jsValue, TRIGGER_TYPE, int32Value)) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "parameter error"); + errorMsg = "Parameter error. The type of request.autoFillTriggerType must be AutoFillTriggerType"; + return false; + } + request.autoFillTriggerType = static_cast(int32Value); + } + return true; +} +} // namespace AbilityRuntime +} // namespace OHOS diff --git a/frameworks/js/napi/auto_fill_manager/js_auto_fill_manager_util.h b/frameworks/js/napi/auto_fill_manager/js_auto_fill_manager_util.h new file mode 100644 index 0000000000..ab9e7b55b3 --- /dev/null +++ b/frameworks/js/napi/auto_fill_manager/js_auto_fill_manager_util.h @@ -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 perns and + * limitations under the License. + */ + +#ifndef OHOS_ABILITY_RUNTIME_JS_AUTO_FILL_MANAGER_UTIL_H +#define OHOS_ABILITY_RUNTIME_JS_AUTO_FILL_MANAGER_UTIL_H + +#include "auto_fill_custom_config.h" +#include "napi/native_api.h" +#include "view_data.h" + +namespace OHOS { +namespace AbilityRuntime { +napi_value WrapAutoFillRect(napi_env env, const AbilityBase::Rect &rect); +napi_value WrapPageNodeInfo(napi_env env, const AbilityBase::PageNodeInfo &pageNodeInfo); +napi_value WrapViewData(napi_env env, const AbilityBase::ViewData &viewData); +napi_value WrapFillFailureResult(napi_env env, int32_t errCode); + +bool UnwrapAutoFillRect(napi_env env, napi_value jsValue, AbilityBase::Rect &rect, std::string &errorMsg); +bool UnwrapPageNodeInfo(napi_env env, napi_value jsValue, AbilityBase::PageNodeInfo &pageNodeInfo, + std::string &errorMsg); +bool UnwrapViewData(napi_env env, napi_value jsValue, AbilityBase::ViewData &viewData, std::string &errorMsg); +bool UnwrapSaveRequest(napi_env env, napi_value jsValue, AutoFill::AutoFillRequest &request, std::string &errorMsg); +bool UnwrapFillRequest(napi_env env, napi_value jsValue, AutoFill::AutoFillRequest &request, std::string &errorMsg); +} // namespace AbilityRuntime +} // namespace OHOS +#endif // OHOS_ABILITY_RUNTIME_JS_AUTO_FILL_MANAGER_UTIL_H diff --git a/frameworks/js/napi/auto_fill_manager/js_auto_fill_request_callback.cpp b/frameworks/js/napi/auto_fill_manager/js_auto_fill_request_callback.cpp new file mode 100644 index 0000000000..31bf590a18 --- /dev/null +++ b/frameworks/js/napi/auto_fill_manager/js_auto_fill_request_callback.cpp @@ -0,0 +1,135 @@ +/* + * 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_auto_fill_request_callback.h" + +#include "hilog_tag_wrapper.h" +#include "js_auto_fill_manager_util.h" +#include "js_auto_fill_manager.h" +#include "js_runtime.h" +#include "js_runtime_utils.h" + +namespace OHOS { +namespace AbilityRuntime { +namespace { +constexpr size_t ARGC_ONE = 1; +const std::string METHOD_ON_FILL_REQUEST_SUCCESS = "onSuccess"; +const std::string METHOD_ON_FILL_REQUEST_FAILED = "onFailure"; +} // namespace +JsAutoFillRequestCallback::JsAutoFillRequestCallback( + napi_env env, int32_t instanceId, AutoFillManagerFunc autoFillManagerFunc) + : env_(env), instanceId_(instanceId), autoFillManagerFunc_(autoFillManagerFunc) {} + +JsAutoFillRequestCallback::~JsAutoFillRequestCallback() {} + +void JsAutoFillRequestCallback::OnFillRequestSuccess(const AbilityBase::ViewData &viewData) +{ + TAG_LOGD(AAFwkTag::AUTOFILLMGR, "called"); + napi_value argv[ARGC_ONE] = { WrapViewData(env_, viewData) }; + JSCallFunction(METHOD_ON_FILL_REQUEST_SUCCESS, argv, ARGC_ONE); + if (autoFillManagerFunc_ != nullptr) { + autoFillManagerFunc_(instanceId_); + } +} + +void JsAutoFillRequestCallback::OnFillRequestFailed(int32_t errCode, const std::string &fillContent, bool isPopup) +{ + TAG_LOGD(AAFwkTag::AUTOFILLMGR, "called"); + napi_value argv[ARGC_ONE] = { WrapFillFailureResult(env_, errCode) }; + JSCallFunction(METHOD_ON_FILL_REQUEST_FAILED, argv, ARGC_ONE); + if (autoFillManagerFunc_ != nullptr) { + autoFillManagerFunc_(instanceId_); + } +} + +void JsAutoFillRequestCallback::Register(napi_value value) +{ + TAG_LOGD(AAFwkTag::AUTOFILLMGR, "called"); + if (IsJsCallbackEquals(callback_, value)) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "callback exist"); + return; + } + + napi_ref ref = nullptr; + napi_status createStatus = napi_create_reference(env_, value, 1, &ref); + if (createStatus != napi_ok || ref == nullptr) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "napi_create_reference failed, %{public}d", createStatus); + return; + } + callback_ = std::unique_ptr(reinterpret_cast(ref)); +} + +void JsAutoFillRequestCallback::JSCallFunction(const std::string &methodName, napi_value const *argv, size_t argc) +{ + auto thisPtr = shared_from_this(); + std::vector argvCopy(argv, argv + argc); + NapiAsyncTask::CompleteCallback complete = + [thisPtr, methodName, argvCopy](napi_env env, NapiAsyncTask &task, int32_t status) { + if (thisPtr) { + thisPtr->JSCallFunctionWorker(methodName, argvCopy.data(), argvCopy.size()); + } + }; + + NapiAsyncTask::Schedule("JsAutoFillRequestCallback::JSCallFunction:" + methodName, + env_, + CreateAsyncTaskWithLastParam(env_, nullptr, nullptr, std::move(complete), nullptr)); +} + +void JsAutoFillRequestCallback::JSCallFunctionWorker(const std::string &methodName, napi_value const *argv, size_t argc) +{ + HandleScope handleScope(env_); + if (callback_ == nullptr) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "null callback_"); + return; + } + + auto obj = callback_->GetNapiValue(); + if (obj == nullptr) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "null obj"); + return; + } + + napi_value funcObject; + if (napi_get_named_property(env_, obj, methodName.c_str(), &funcObject) != napi_ok) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "Get function failed"); + return; + } + + napi_call_function(env_, obj, funcObject, argc, argv, nullptr); +} + +bool JsAutoFillRequestCallback::IsJsCallbackEquals(std::shared_ptr callback, napi_value value) +{ + if (callback == nullptr) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "Invalid jsCallback"); + return false; + } + + auto object = callback->GetNapiValue(); + if (object == nullptr) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "null obj"); + return false; + } + + bool result = false; + if (napi_strict_equals(env_, object, value, &result) != napi_ok) { + TAG_LOGE(AAFwkTag::AUTOFILLMGR, "Object not match"); + return false; + } + + return result; +} +} // namespace AbilityRuntime +} // namespace OHOS \ No newline at end of file diff --git a/frameworks/js/napi/auto_fill_manager/js_auto_fill_request_callback.h b/frameworks/js/napi/auto_fill_manager/js_auto_fill_request_callback.h new file mode 100644 index 0000000000..0e07b05efd --- /dev/null +++ b/frameworks/js/napi/auto_fill_manager/js_auto_fill_request_callback.h @@ -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. + */ + +#ifndef OHOS_ABILITY_RUNTIME_JS_AUTO_FILL_REQUEST_CALLBACK_H +#define OHOS_ABILITY_RUNTIME_JS_AUTO_FILL_REQUEST_CALLBACK_H + +#include "fill_request_callback_interface.h" +#include "native_engine/native_value.h" + +class NativeReference; +namespace OHOS { +namespace AbilityRuntime { +using AutoFillManagerFunc = std::function; +class JsAutoFillRequestCallback : public IFillRequestCallback, + public std::enable_shared_from_this { +public: + JsAutoFillRequestCallback(napi_env env, int32_t instanceId, AutoFillManagerFunc autoFillManagerFunc); + virtual ~JsAutoFillRequestCallback(); + + void Register(napi_value value); + void OnFillRequestSuccess(const AbilityBase::ViewData &viewData) override; + void OnFillRequestFailed(int32_t errCode, const std::string &fillContent = "", bool isPopup = false) override; + +private: + void JSCallFunction(const std::string &methodName, napi_value const *argv, size_t argc); + void JSCallFunctionWorker(const std::string &methodName, napi_value const *argv, size_t argc); + bool IsJsCallbackEquals(std::shared_ptr callback, napi_value value); + + napi_env env_ = nullptr; + std::shared_ptr callback_; + int32_t instanceId_ = -1; + AutoFillManagerFunc autoFillManagerFunc_ = nullptr; +}; +} // namespace AbilityRuntime +} // namespace OHOS +#endif // OHOS_ABILITY_RUNTIME_JS_AUTO_FILL_REQUEST_CALLBACK_H diff --git a/frameworks/js/napi/inner/napi_common/BUILD.gn b/frameworks/js/napi/inner/napi_common/BUILD.gn index 730c3708d8..d6f85f7244 100644 --- a/frameworks/js/napi/inner/napi_common/BUILD.gn +++ b/frameworks/js/napi/inner/napi_common/BUILD.gn @@ -43,6 +43,7 @@ ohos_shared_library("napi_common") { "napi_common_util.cpp", "napi_common_want.cpp", "napi_common_query_entity_param.cpp", + "napi_common_skill_execute.cpp", "open_link/napi_common_open_link_options.cpp", ] diff --git a/frameworks/js/napi/inner/napi_common/napi_common_execute_param.cpp b/frameworks/js/napi/inner/napi_common/napi_common_execute_param.cpp index f237c4c5b3..6d1a543a24 100644 --- a/frameworks/js/napi/inner/napi_common/napi_common_execute_param.cpp +++ b/frameworks/js/napi/inner/napi_common/napi_common_execute_param.cpp @@ -64,6 +64,16 @@ bool UnwrapExecuteParam(napi_env env, napi_value param, InsightIntentExecutePara return false; } + if (IsExistsByPropertyName(env, param, "deviceId")) { + TAG_LOGI(AAFwkTag::JSNAPI, "has deviceId"); + std::string deviceId {""}; + if (!UnwrapStringByPropertyName(env, param, "deviceId", deviceId)) { + TAG_LOGE(AAFwkTag::JSNAPI, "Wrong argument type deviceId"); + return false; + } + executeParam.deviceId_ = deviceId; + } + napi_valuetype valueType = napi_undefined; napi_typeof(env, napiIntentParam, &valueType); if (valueType != napi_object) { diff --git a/frameworks/js/napi/inner/napi_common/napi_common_skill_execute.cpp b/frameworks/js/napi/inner/napi_common/napi_common_skill_execute.cpp new file mode 100644 index 0000000000..300e6893aa --- /dev/null +++ b/frameworks/js/napi/inner/napi_common/napi_common_skill_execute.cpp @@ -0,0 +1,135 @@ +/* + * 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 "napi_common_skill_execute.h" + +#include "hilog_tag_wrapper.h" +#include "napi_common_util.h" +#include "napi_common_want.h" +#include "want_params.h" + +namespace OHOS { +namespace AbilityRuntime { +using namespace OHOS::AppExecFwk; + +bool UnwrapSkillExecuteResult(napi_env env, napi_value param, SkillExecuteResult &result) +{ + if (param == nullptr) { + TAG_LOGE(AAFwkTag::JSNAPI, "null param"); + return false; + } + + napi_valuetype valueType = napi_undefined; + napi_typeof(env, param, &valueType); + if (valueType != napi_object) { + TAG_LOGE(AAFwkTag::JSNAPI, "param type not object"); + return false; + } + + // code + napi_value codeVal = nullptr; + napi_get_named_property(env, param, "code", &codeVal); + if (codeVal != nullptr) { + napi_get_value_int32(env, codeVal, &result.code); + } + + // result (optional) + napi_value resultVal = nullptr; + napi_get_named_property(env, param, "result", &resultVal); + if (resultVal != nullptr) { + napi_typeof(env, resultVal, &valueType); + if (valueType == napi_object) { + auto wp = std::make_shared(); + if (UnwrapWantParams(env, resultVal, *wp)) { + result.result = wp; + } + } + } + + // uris (optional) + napi_value urisVal = nullptr; + napi_get_named_property(env, param, "uris", &urisVal); + if (urisVal != nullptr) { + bool isArray = false; + napi_is_array(env, urisVal, &isArray); + if (isArray) { + uint32_t length = 0; + napi_get_array_length(env, urisVal, &length); + for (uint32_t i = 0; i < length; i++) { + napi_value element = nullptr; + napi_get_element(env, urisVal, i, &element); + if (element == nullptr) { + continue; + } + napi_typeof(env, element, &valueType); + if (valueType != napi_string) { + continue; + } + size_t strLen = 0; + napi_get_value_string_utf8(env, element, nullptr, 0, &strLen); + std::string uriStr(strLen, '\0'); + napi_get_value_string_utf8(env, element, uriStr.data(), strLen + 1, &strLen); + result.uris.push_back(uriStr); + } + } + } + + // flags (optional) + napi_value flagsVal = nullptr; + napi_get_named_property(env, param, "flags", &flagsVal); + if (flagsVal != nullptr) { + napi_typeof(env, flagsVal, &valueType); + if (valueType == napi_number) { + uint32_t flags = 0; + napi_get_value_uint32(env, flagsVal, &flags); + result.flags = flags; + } + } + + return true; +} + +napi_value WrapSkillExecuteResult(napi_env env, const SkillExecuteResult &result) +{ + napi_value obj = nullptr; + napi_create_object(env, &obj); + + napi_value codeVal = nullptr; + napi_create_int32(env, result.code, &codeVal); + napi_set_named_property(env, obj, "code", codeVal); + + if (result.result != nullptr) { + napi_value resultVal = WrapWantParams(env, *result.result); + napi_set_named_property(env, obj, "result", resultVal); + } + + napi_value urisArray = nullptr; + napi_create_array(env, &urisArray); + for (uint32_t i = 0; i < result.uris.size(); i++) { + napi_value uri = nullptr; + napi_create_string_utf8(env, result.uris[i].c_str(), result.uris[i].size(), &uri); + napi_set_element(env, urisArray, i, uri); + } + napi_set_named_property(env, obj, "uris", urisArray); + + napi_value flagsVal = nullptr; + napi_create_uint32(env, result.flags, &flagsVal); + napi_set_named_property(env, obj, "flags", flagsVal); + + return obj; +} + +} // namespace AbilityRuntime +} // namespace OHOS diff --git a/frameworks/js/napi/inner/napi_common/napi_common_skill_execute.h b/frameworks/js/napi/inner/napi_common/napi_common_skill_execute.h new file mode 100644 index 0000000000..978221b08d --- /dev/null +++ b/frameworks/js/napi/inner/napi_common/napi_common_skill_execute.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_NAPI_COMMON_SKILL_EXECUTE_H +#define OHOS_ABILITY_RUNTIME_NAPI_COMMON_SKILL_EXECUTE_H + +#include "napi_common_data.h" +#include "skill/skill_execute_result.h" + +namespace OHOS { +namespace AbilityRuntime { + +bool UnwrapSkillExecuteResult( + napi_env env, napi_value param, AppExecFwk::SkillExecuteResult &result); + +napi_value WrapSkillExecuteResult( + napi_env env, const AppExecFwk::SkillExecuteResult &result); + +} // namespace AbilityRuntime +} // namespace OHOS +#endif // OHOS_ABILITY_RUNTIME_NAPI_COMMON_SKILL_EXECUTE_H diff --git a/frameworks/js/napi/insight_intent/insight_intent_driver/BUILD.gn b/frameworks/js/napi/insight_intent/insight_intent_driver/BUILD.gn index 38edb8f258..e53e9392c1 100644 --- a/frameworks/js/napi/insight_intent/insight_intent_driver/BUILD.gn +++ b/frameworks/js/napi/insight_intent/insight_intent_driver/BUILD.gn @@ -48,6 +48,13 @@ ohos_shared_library("insightintentdriver_napi") { "json:nlohmann_json_static", "napi:ace_napi", ] + defines = [] + if (hiviewdfx_runtime_api_metrics_enable) { + defines += [ "HIVIEWDFX_RUNTIME_API_METRICS" ] + external_deps += [ + "api_metrics:histogrammanager" + ] + } cflags_cc = [] if (os_dlp_part_enabled) { cflags_cc += [ "-DWITH_DLP" ] diff --git a/frameworks/js/napi/insight_intent/insight_intent_driver/js_insight_intent_driver.cpp b/frameworks/js/napi/insight_intent/insight_intent_driver/js_insight_intent_driver.cpp index 295176e03e..5c45488579 100644 --- a/frameworks/js/napi/insight_intent/insight_intent_driver/js_insight_intent_driver.cpp +++ b/frameworks/js/napi/insight_intent/insight_intent_driver/js_insight_intent_driver.cpp @@ -34,6 +34,9 @@ #include "native_engine/native_value.h" #include "string_wrapper.h" #include "int_wrapper.h" +#ifdef HIVIEWDFX_RUNTIME_API_METRICS +#include "histogram_plugin_macros.h" +#endif #include @@ -219,6 +222,9 @@ private: param.isServiceMatch_ = false; ParseParam(param); if (param.isServiceMatch_) { + #ifdef HIVIEWDFX_RUNTIME_API_METRICS + HISTOGRAM_BOOLEAN("Ability.InsightIntentDriver.Execute.service_match", 1); + #endif return true; } } diff --git a/frameworks/native/ability/BUILD.gn b/frameworks/native/ability/BUILD.gn index 77a438d22b..dba36b0df4 100644 --- a/frameworks/native/ability/BUILD.gn +++ b/frameworks/native/ability/BUILD.gn @@ -166,6 +166,7 @@ ohos_shared_library("cj_ability_context_native") { "faultloggerd:libdfx_dumpcatcher", "hilog:libhilog", "hitrace:hitrace_meter", + "icu:shared_icuuc", "image_framework:image_native", "ipc:ipc_single", "napi:cj_bind_ffi", diff --git a/frameworks/native/ability/ability_runtime/ability_context_impl.cpp b/frameworks/native/ability/ability_runtime/ability_context_impl.cpp index ac319ed5e2..a32f22f217 100644 --- a/frameworks/native/ability/ability_runtime/ability_context_impl.cpp +++ b/frameworks/native/ability/ability_runtime/ability_context_impl.cpp @@ -1768,5 +1768,16 @@ ErrCode AbilityContextImpl::NotifyCompleteGamePreLaunch() } return err; } + +ErrCode AbilityContextImpl::StartSelfUIAbilityInChildProcess(const AAFwk::Want &want, const std::string &specifiedFlag) +{ + HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__); + ErrCode err = + AAFwk::AbilityManagerClient::GetInstance()->StartSelfUIAbilityInChildProcess(want, specifiedFlag, token_); + if (err != ERR_OK) { + TAG_LOGE(AAFwkTag::CONTEXT, "ret=%{public}d", err); + } + return err; +} } // namespace AbilityRuntime } // namespace OHOS diff --git a/frameworks/native/ability/ability_runtime/connection_manager.cpp b/frameworks/native/ability/ability_runtime/connection_manager.cpp index 7fa4e341ec..9e23f5f8b1 100644 --- a/frameworks/native/ability/ability_runtime/connection_manager.cpp +++ b/frameworks/native/ability/ability_runtime/connection_manager.cpp @@ -131,15 +131,16 @@ bool ConnectionManager::MatchConnection( if (accountId != connection.first.userid) { return false; } - if (!connectReceiver.GetElement().GetAbilityName().empty()) { + auto receiverEle = connectReceiver.GetElement(); + if (!receiverEle.GetAbilityName().empty()) { return connectCaller == connection.first.connectCaller && - connectReceiver.GetElement().GetBundleName() == connection.first.connectReceiver.GetBundleName() && - connectReceiver.GetElement().GetModuleName() == connection.first.connectReceiver.GetModuleName() && - connectReceiver.GetElement().GetAbilityName() == connection.first.connectReceiver.GetAbilityName(); + receiverEle.GetBundleName() == connection.first.connectReceiver.GetBundleName() && + receiverEle.GetModuleName() == connection.first.connectReceiver.GetModuleName() && + receiverEle.GetAbilityName() == connection.first.connectReceiver.GetAbilityName(); } else { // ImplicitConnect return connectCaller == connection.first.connectCaller && - connectReceiver.GetElement().GetBundleName() == connection.first.connectReceiver.GetBundleName() && + receiverEle.GetBundleName() == connection.first.connectReceiver.GetBundleName() && connectReceiver.GetOperation() == connection.first.connectReceiver; } } diff --git a/frameworks/native/ability/native/BUILD.gn b/frameworks/native/ability/native/BUILD.gn index b32c3e4c5f..85c3e884aa 100644 --- a/frameworks/native/ability/native/BUILD.gn +++ b/frameworks/native/ability/native/BUILD.gn @@ -562,6 +562,7 @@ config("extensionkit_public_config") { visibility = [ ":*" ] include_dirs = [ "${ability_runtime_path}/interfaces/kits/native/ability/native/continuation/distributed", + "${ability_runtime_path}/interfaces/kits/native/ability/native/ui_extension_ability", "${ability_runtime_path}/interfaces/kits/native/ability/native/continuation/kits", "${ability_runtime_path}/interfaces/kits/native/appkit/ability_runtime/context", "${ability_runtime_path}/interfaces/kits/native/ability/ability_runtime", @@ -1563,6 +1564,13 @@ ohos_shared_library("service_extension") { "samgr:samgr_proxy", ] + if (defined(hiviewdfx_runtime_api_metrics_enable) && hiviewdfx_runtime_api_metrics_enable) { + defines += [ "HIVIEWDFX_RUNTIME_API_METRICS" ] + external_deps += [ + "api_metrics:histogrammanager", + ] + } + if (ability_runtime_graphics) { external_deps += [ "image_framework:image", @@ -2432,6 +2440,7 @@ ohos_shared_library("ui_extension") { "${ability_runtime_native_path}/ability/native/ui_extension_base/js_ui_extension_context.cpp", "${ability_runtime_native_path}/ability/native/ui_extension_base/js_uiservice_uiext_connection.cpp", "${ability_runtime_native_path}/ability/native/ui_extension_base/ui_extension_context.cpp", + "${ability_runtime_native_path}/ability/native/ui_extension_base/ui_extension_modal_callback.cpp", "${ability_runtime_native_path}/ability/native/ui_extension_base/ui_extension_servicehost_stub_impl.cpp", "${ability_runtime_path}/frameworks/ets/ani/ui_extension_ability/src/ets_ui_extension_instance.cpp", ] @@ -3942,6 +3951,7 @@ ohos_shared_library("modular_object_extension") { sources = [ "${ability_runtime_native_path}/ability/native/modular_object_extension/modular_object_extension.cpp", "${ability_runtime_native_path}/ability/native/modular_object_extension/modular_object_extension_context_impl.cpp", + "${ability_runtime_native_path}/ability/native/modular_object_worker_manager.cpp", ] deps = [ diff --git a/frameworks/native/ability/native/ability_business_error/ability_business_error.cpp b/frameworks/native/ability/native/ability_business_error/ability_business_error.cpp index cde6a479be..b02374827f 100644 --- a/frameworks/native/ability/native/ability_business_error/ability_business_error.cpp +++ b/frameworks/native/ability/native/ability_business_error/ability_business_error.cpp @@ -113,6 +113,8 @@ constexpr const char* ERROR_MSG_CALLER_NOT_ATOMIC_SERVICE = "The caller is not an atomic service."; constexpr const char* ERROR_MSG_AGENT_ID_NOT_EXIST = "The specified agentId does not exist."; +constexpr const char* ERROR_MSG_AGENT_CARD_LIST_OUT_OF_RANGE = + "The number of AgentCards in the bundle reaches the limit."; constexpr const char* ERROR_MSG_AGENT_CARD_VERSION_TOO_OLD = "The specified agent card version is older than the current version."; constexpr const char* ERROR_MSG_AGENT_CARD_VERSION_INVALID = @@ -170,6 +172,16 @@ constexpr const char* ERROR_MSG_UIABILITY_IS_ALREADY_EXIST = constexpr const char* ERROR_MSG_SELF_REDIRECTION_DISALLOWED = "The UIAbility is prohibited from launching itself via App Linking."; constexpr const char* ERROR_MSG_SEND_REQUEST_TO_SYSTEM_FAIL = "Failed to send request to system service."; +constexpr const char* ERROR_MSG_INTENT_CONNECTION_FAILED = + "Cross-device execution intent connection failed."; +constexpr const char* ERROR_MSG_INTENT_DEVICE_DISCONNECTED = + "Device disconnected during cross-device intent execution."; +constexpr const char* ERROR_MSG_DELAYED_PROCESS_EXIT_NO_UIABILITY = + "The current process has no UIAbility, and this API cannot be called."; +constexpr const char* ERROR_MSG_DELAYED_PROCESS_EXIT_NOT_PENDING = + "Delayed process exit is not pending in the current process, and this API cannot be called."; +constexpr const char* ERROR_MSG_DELAYED_PROCESS_EXIT_HAS_OTHER_UIABILITY = + "The current process still has another UIAbility, and this API cannot be called."; // follow ERR_BUNDLE_MANAGER_BUNDLE_NOT_EXIST of appexecfwk_errors.h in bundle_framework constexpr int32_t ERR_BUNDLE_MANAGER_BUNDLE_NOT_EXIST = 8521220; @@ -250,6 +262,7 @@ static std::unordered_map ERR_CODE_MAP = { { AbilityErrorCode::ERROR_CODE_TARGET_NOT_STARTED, ERROR_TARGET_NOT_STARTED}, { AbilityErrorCode::ERROR_CODE_CALLER_NOT_ATOMIC_SERVICE, ERROR_MSG_CALLER_NOT_ATOMIC_SERVICE}, { AbilityErrorCode::ERROR_CODE_AGENT_ID_NOT_EXIST, ERROR_MSG_AGENT_ID_NOT_EXIST}, + { AbilityErrorCode::ERROR_CODE_AGENT_CARD_LIST_OUT_OF_RANGE, ERROR_MSG_AGENT_CARD_LIST_OUT_OF_RANGE}, { AbilityErrorCode::ERROR_CODE_AGENT_CARD_VERSION_TOO_OLD, ERROR_MSG_AGENT_CARD_VERSION_TOO_OLD}, { AbilityErrorCode::ERROR_CODE_AGENT_CARD_VERSION_INVALID, ERROR_MSG_AGENT_CARD_VERSION_INVALID}, { AbilityErrorCode::ERROR_CODE_AGENT_CARD_DUPLICATE_REGISTER, ERROR_MSG_AGENT_CARD_DUPLICATE_REGISTER}, @@ -284,6 +297,12 @@ static std::unordered_map ERR_CODE_MAP = { { AbilityErrorCode::ERROR_CODE_UIABILITY_IS_ALREADY_EXIST, ERROR_MSG_UIABILITY_IS_ALREADY_EXIST }, { AbilityErrorCode::ERROR_CODE_SELF_REDIRECTION_DISALLOWED, ERROR_MSG_SELF_REDIRECTION_DISALLOWED }, { AbilityErrorCode::ERROR_CODE_SEND_REQUEST_TO_SYSTEM_FAIL, ERROR_MSG_SEND_REQUEST_TO_SYSTEM_FAIL }, + { AbilityErrorCode::ERROR_CODE_INTENT_CONNECTION_FAILED, ERROR_MSG_INTENT_CONNECTION_FAILED }, + { AbilityErrorCode::ERROR_CODE_INTENT_DEVICE_DISCONNECTED, ERROR_MSG_INTENT_DEVICE_DISCONNECTED }, + { AbilityErrorCode::ERROR_CODE_DELAYED_PROCESS_EXIT_NO_UIABILITY, ERROR_MSG_DELAYED_PROCESS_EXIT_NO_UIABILITY }, + { AbilityErrorCode::ERROR_CODE_DELAYED_PROCESS_EXIT_NOT_PENDING, ERROR_MSG_DELAYED_PROCESS_EXIT_NOT_PENDING }, + { AbilityErrorCode::ERROR_CODE_DELAYED_PROCESS_EXIT_HAS_OTHER_UIABILITY, + ERROR_MSG_DELAYED_PROCESS_EXIT_HAS_OTHER_UIABILITY }, }; static std::unordered_map INNER_TO_JS_ERROR_CODE_MAP { @@ -370,6 +389,7 @@ static std::unordered_map INNER_TO_JS_ERROR_CODE_MAP {ERR_TARGET_NOT_STARTED, AbilityErrorCode::ERROR_CODE_TARGET_NOT_STARTED}, {ERR_CALLER_NOT_ATOMIC_SERVICE, AbilityErrorCode::ERROR_CODE_CALLER_NOT_ATOMIC_SERVICE}, {ERR_INVALID_AGENT_CARD_ID, AbilityErrorCode::ERROR_CODE_AGENT_ID_NOT_EXIST}, + {ERR_AGENT_CARD_LIST_OUT_OF_RANGE, AbilityErrorCode::ERROR_CODE_AGENT_CARD_LIST_OUT_OF_RANGE}, {ERR_MAX_AGENT_CONNECTIONS_REACHED, AbilityErrorCode::ERROR_CODE_MAX_CONNECTIONS_REACHED}, {ERR_AGENT_CARD_VERSION_TOO_OLD, AbilityErrorCode::ERROR_CODE_AGENT_CARD_VERSION_TOO_OLD}, {ERR_INVALID_AGENT_CARD_VERSION, AbilityErrorCode::ERROR_CODE_AGENT_CARD_VERSION_INVALID}, @@ -404,6 +424,12 @@ static std::unordered_map INNER_TO_JS_ERROR_CODE_MAP {ERROR_UIABILITY_IS_ALREADY_EXIST, AbilityErrorCode::ERROR_CODE_UIABILITY_IS_ALREADY_EXIST}, {ERR_CODE_INVALID_ID, AbilityErrorCode::ERROR_CODE_INVALID_ID}, {ERR_SELF_REDIRECTION_DISALLOWED, AbilityErrorCode::ERROR_CODE_SELF_REDIRECTION_DISALLOWED }, + {ERR_INTENT_CONNECTION_FAILED, AbilityErrorCode::ERROR_CODE_INTENT_CONNECTION_FAILED}, + {ERR_INTENT_DEVICE_DISCONNECTED, AbilityErrorCode::ERROR_CODE_INTENT_DEVICE_DISCONNECTED}, + {ERR_DELAYED_PROCESS_EXIT_NOT_PENDING, AbilityErrorCode::ERROR_CODE_DELAYED_PROCESS_EXIT_NOT_PENDING}, + {ERR_DELAYED_PROCESS_EXIT_NO_UIABILITY, AbilityErrorCode::ERROR_CODE_DELAYED_PROCESS_EXIT_NO_UIABILITY}, + {ERR_DELAYED_PROCESS_EXIT_HAS_OTHER_UIABILITY, + AbilityErrorCode::ERROR_CODE_DELAYED_PROCESS_EXIT_HAS_OTHER_UIABILITY}, }; } diff --git a/frameworks/native/ability/native/ability_runtime/js_ability_context.cpp b/frameworks/native/ability/native/ability_runtime/js_ability_context.cpp index d812fbc917..5f25010775 100644 --- a/frameworks/native/ability/native/ability_runtime/js_ability_context.cpp +++ b/frameworks/native/ability/native/ability_runtime/js_ability_context.cpp @@ -493,6 +493,11 @@ napi_value JsAbilityContext::StartSelf(napi_env env, napi_callback_info info) GET_NAPI_INFO_AND_CALL(env, info, JsAbilityContext, OnStartSelf); } +napi_value JsAbilityContext::StartSelfUIAbilityInChildProcess(napi_env env, napi_callback_info info) +{ + GET_NAPI_INFO_AND_CALL(env, info, JsAbilityContext, OnStartSelfUIAbilityInChildProcess); +} + napi_value JsAbilityContext::RestartAppWithWindow(napi_env env, napi_callback_info info) { GET_NAPI_INFO_AND_CALL(env, info, JsAbilityContext, OnRestartAppWithWindow); @@ -2295,6 +2300,51 @@ napi_value JsAbilityContext::OnStartSelfUIAbilityInCurrentProcess(napi_env env, return result; } +napi_value JsAbilityContext::OnStartSelfUIAbilityInChildProcess(napi_env env, NapiCallbackInfo &info) +{ + if (info.argc < ARGC_TWO) { + TAG_LOGE(AAFwkTag::CONTEXT, "not enough params"); + ThrowTooFewParametersError(env); + return CreateJsUndefined(env); + } + AAFwk::Want want; + if (!AppExecFwk::UnwrapWant(env, info.argv[INDEX_ZERO], want)) { + ThrowInvalidParamError(env, "Parse param want failed, want must be Want."); + return CreateJsUndefined(env); + } + + std::string specifiedFlag; + if (!ConvertFromJsValue(env, info.argv[INDEX_ONE], specifiedFlag)) { + TAG_LOGE(AAFwkTag::CONTEXT, "parse specifiedFlag failed"); + ThrowInvalidParamError(env, "Parse param specifiedFlag failed, specifiedFlag must be string."); + return CreateJsUndefined(env); + } + + auto innerErrCode = std::make_shared(ERR_OK); + NapiAsyncTask::ExecuteCallback execute = [weak = context_, want, specifiedFlag, innerErrCode]() { + TAG_LOGI(AAFwkTag::CONTEXT, "async execute"); + auto context = weak.lock(); + if (!context) { + TAG_LOGW(AAFwkTag::CONTEXT, "null context"); + *innerErrCode = static_cast(AAFwk::ERR_INVALID_CONTEXT); + return; + } + + *innerErrCode = context->StartSelfUIAbilityInChildProcess(want, specifiedFlag); + }; + + NapiAsyncTask::CompleteCallback complete = [innerErrCode](napi_env env, NapiAsyncTask &task, int32_t status) { + HandleScope handleScope(env); + (*innerErrCode == ERR_OK) ? task.ResolveWithNoError(env, CreateJsUndefined(env)) : + task.Reject(env, CreateJsErrorByNativeErr(env, *innerErrCode)); + }; + + napi_value result = nullptr; + NapiAsyncTask::ScheduleHighQos("JsAbilityContext::OnStartSelfUIAbilityInChildProcess", + env, CreateAsyncTaskWithLastParam(env, nullptr, std::move(execute), std::move(complete), &result)); + return result; +} + napi_value JsAbilityContext::WrapRequestDialogResult(napi_env env, int32_t resultCode, const AAFwk::Want &want) { @@ -2490,6 +2540,8 @@ napi_value CreateJsAbilityContext(napi_env env, std::shared_ptr JsAbilityContext::StartSelfUIAbilityInCurrentProcess); BindNativeFunction(env, object, "startSelf", moduleName, JsAbilityContext::StartSelf); + BindNativeFunction(env, object, "startSelfUIAbilityInChildProcess", moduleName, + JsAbilityContext::StartSelfUIAbilityInChildProcess); BindNativeFunction(env, object, "restartApp", moduleName, JsAbilityContext::RestartAppWithWindow); BindNativeFunction(env, object, "setMissionWindowIcon", moduleName, JsAbilityContext::SetMissionWindowIcon); diff --git a/frameworks/native/ability/native/ability_runtime/js_ui_ability.cpp b/frameworks/native/ability/native/ability_runtime/js_ui_ability.cpp index c744f0907e..f3c36665b6 100644 --- a/frameworks/native/ability/native/ability_runtime/js_ui_ability.cpp +++ b/frameworks/native/ability/native/ability_runtime/js_ui_ability.cpp @@ -52,6 +52,7 @@ #include "ohos_application.h" #include "madvise/madvise_utils.h" #include "napi_common_configuration.h" +#include "napi_common_util.h" #include "napi_common_want.h" #include "napi_remote_object.h" #include "page_switch_log.h" @@ -59,6 +60,8 @@ #include "string_wrapper.h" #include "system_ability_definition.h" #include "time_util.h" +#include "skill/skill_execute_param.h" +#include "skill/skill_execute_result.h" namespace OHOS { namespace AbilityRuntime { @@ -1488,7 +1491,9 @@ void JsUIAbility::ExecuteInsightIntentMoveToForeground(const Want &want, FreezeUtil::TimeoutState::FOREGROUND, "IntentForeground"); ability->CallOnForegroundFunc(want); }; - callback->Push(asyncCallback); + if (!CheckIsSilentForeground()) { + callback->Push(asyncCallback); + } InsightIntentExecutorInfo executeInfo; auto ret = GetInsightIntentExecutorInfo(want, executeParam, executeInfo); @@ -2510,6 +2515,99 @@ void JsUIAbility::NotifyWindowDestroy() } } +napi_value JsUIAbility::LoadSkillFunction( + const std::shared_ptr ¶m, napi_value &outJsObj) +{ + napi_env env = jsRuntime_.GetNapiEnv(); + std::unique_ptr moduleRef = nullptr; + napi_value method = nullptr; + for (const auto &srcEntry : param->srcEntries_) { + std::string srcPath(param->moduleName_ + "/" + srcEntry); + auto pos = srcPath.rfind('.'); + if (pos == std::string::npos) { + TAG_LOGW(AAFwkTag::UIABILITY, "skip srcEntry, no extension:%{public}s", srcEntry.c_str()); + continue; + } + srcPath.erase(pos); + srcPath.append(".abc"); + moduleRef = jsRuntime_.LoadModule(param->moduleName_, srcPath, param->hapPath_, true); + if (moduleRef == nullptr) { + TAG_LOGW(AAFwkTag::UIABILITY, "LoadModule failed, path:%{public}s", srcPath.c_str()); + continue; + } + outJsObj = moduleRef->GetNapiValue(); + method = AppExecFwk::GetPropertyValueByPropertyName( + env, outJsObj, param->functionName_.c_str(), napi_valuetype::napi_function); + if (method != nullptr) { + TAG_LOGI(AAFwkTag::UIABILITY, "func found in srcEntry:%{public}s", srcEntry.c_str()); + break; + } + TAG_LOGW(AAFwkTag::UIABILITY, "func not found:%{public}s in srcEntry:%{public}s", + param->functionName_.c_str(), srcEntry.c_str()); + } + return method; +} + +std::vector JsUIAbility::BuildSkillCallArgs(napi_env env, + const std::shared_ptr ¶m) +{ + napi_value info = nullptr; + napi_create_object(env, &info); + napi_value requestCodeVal = nullptr; + napi_create_string_utf8(env, param->requestCode_.c_str(), param->requestCode_.length(), &requestCodeVal); + napi_set_named_property(env, info, "requestCode", requestCodeVal); + napi_value contextObj = nullptr; + if (shellContextRef_ != nullptr) { + contextObj = shellContextRef_->GetNapiValue(); + } + napi_set_named_property(env, info, "context", contextObj); + + std::vector args; + args.push_back(info); + if (param->skillArgs_ != nullptr && !param->skillArgs_->GetParams().empty()) { + napi_value wrappedObj = AppExecFwk::WrapWantParams(env, *param->skillArgs_); + for (const auto &[key, value] : param->skillArgs_->GetParams()) { + napi_value val = nullptr; + napi_get_named_property(env, wrappedObj, key.c_str(), &val); + args.push_back(val); + } + } + return args; +} + +void JsUIAbility::ExecuteSkill(const AAFwk::Want &want, + const std::shared_ptr ¶m) +{ + TAG_LOGD(AAFwkTag::UIABILITY, "ExecuteSkill requestCode:%{public}s", + param != nullptr ? param->requestCode_.c_str() : ""); + if (param == nullptr) { + TAG_LOGE(AAFwkTag::UIABILITY, "null param"); + return; + } + napi_env env = jsRuntime_.GetNapiEnv(); + if (env == nullptr) { + TAG_LOGE(AAFwkTag::UIABILITY, "null napi env, skill will time out"); + return; + } + napi_value jsObj = nullptr; + napi_value method = LoadSkillFunction(param, jsObj); + if (method == nullptr) { + TAG_LOGE(AAFwkTag::UIABILITY, "func not found in any srcEntry:%{public}s", param->functionName_.c_str()); + return; + } + auto args = BuildSkillCallArgs(env, param); + napi_value result = nullptr; + napi_status status = napi_call_function(env, jsObj, method, args.size(), args.data(), &result); + if (status != napi_ok) { + TAG_LOGE(AAFwkTag::UIABILITY, "napi_call_function failed, status:%{public}d func:%{public}s", + status, param->functionName_.c_str()); + return; + } + TAG_LOGD(AAFwkTag::UIABILITY, + "ExecuteSkill dispatched, waiting completeArkTSScriptInApp, requestCode:%{public}s", + param->requestCode_.c_str()); +} + void JsUIAbility::RegisterDelayResultCallback(const std::shared_ptr &executeParam) { auto delayResultCallback = [intentId = executeParam->insightIntentId_, token = token_] @@ -2543,7 +2641,7 @@ void JsUIAbility::HandleNativeModule(napi_env env) } // Create NativeAbilityWrapper - auto wrapper = std::make_shared(); + auto wrapper = std::make_shared(); wrapper->instanceId = GetInstanceId(); wrapper->abilityName = GetAbilityName(); wrapper->env = env; diff --git a/frameworks/native/ability/native/child_process_manager/child_process_manager.cpp b/frameworks/native/ability/native/child_process_manager/child_process_manager.cpp index a7a8f833c8..1c81f03e33 100644 --- a/frameworks/native/ability/native/child_process_manager/child_process_manager.cpp +++ b/frameworks/native/ability/native/child_process_manager/child_process_manager.cpp @@ -25,7 +25,9 @@ #include "app_utils.h" #include "application_info.h" +#include "app_mgr_client.h" #include "app_mgr_interface.h" +#include "singleton.h" #include "bundle_info.h" #include "bundle_mgr_interface.h" #include "child_process.h" @@ -629,24 +631,28 @@ ChildProcessManagerErrorCode ChildProcessManager::KillChildProcessByPid(int32_t return ChildProcessManagerErrorCode::ERR_OK; } -void ChildProcessManager::SetArkChildProcessSupported(bool supported) -{ - isArkChildProcessSupported_ = supported; -} - -void ChildProcessManager::SetNativeChildProcessSupported(bool supported) -{ - isNativeChildProcessSupported_ = supported; -} - bool ChildProcessManager::IsArkChildProcessSupported() { - return isArkChildProcessSupported_; + TAG_LOGD(AAFwkTag::PROCESSMGR, "called"); + bool isSupported = false; + auto client = DelayedSingleton::GetInstance(); + if (client->IsChildProcessSupported(false, isSupported) != ERR_OK) { + TAG_LOGE(AAFwkTag::PROCESSMGR, "ipc query failed"); + return false; + } + return isSupported; } bool ChildProcessManager::IsNativeChildProcessSupported() { - return isNativeChildProcessSupported_; + TAG_LOGD(AAFwkTag::PROCESSMGR, "called"); + bool isSupported = false; + auto client = DelayedSingleton::GetInstance(); + if (client->IsChildProcessSupported(true, isSupported) != ERR_OK) { + TAG_LOGE(AAFwkTag::PROCESSMGR, "ipc query failed"); + return false; + } + return isSupported; } } // namespace AbilityRuntime } // namespace OHOS \ No newline at end of file diff --git a/frameworks/native/ability/native/distributed_ability_runtime/distributed_client.cpp b/frameworks/native/ability/native/distributed_ability_runtime/distributed_client.cpp index f463324ecf..a4d579ab7c 100644 --- a/frameworks/native/ability/native/distributed_ability_runtime/distributed_client.cpp +++ b/frameworks/native/ability/native/distributed_ability_runtime/distributed_client.cpp @@ -585,6 +585,7 @@ int32_t DistributedClient::StartRemoteIntent(const OHOS::AAFwk::Want& want, return ERR_FLATTEN_OBJECT; } PARCEL_WRITE_HELPER(data, Parcelable, &want); + PARCEL_WRITE_HELPER(data, String, want.GetElement().GetModuleName()); PARCEL_WRITE_HELPER(data, Int32, callerInfo.callerUid); PARCEL_WRITE_HELPER(data, Uint64, callerInfo.requestCode); PARCEL_WRITE_HELPER(data, Uint32, callerInfo.accessToken); diff --git a/frameworks/native/ability/native/etc/extension_blocklist_config.json b/frameworks/native/ability/native/etc/extension_blocklist_config.json index 19cb6ce631..dbc4b9b4a9 100644 --- a/frameworks/native/ability/native/etc/extension_blocklist_config.json +++ b/frameworks/native/ability/native/etc/extension_blocklist_config.json @@ -557,9 +557,6 @@ "connectedTag", "contact", "continuation.continuationManager", - "data.distributedData", - "data.distributedDataObject", - "data.distributedKVStore", "distributedBundle", "distributedMissionManager", "enterprise.adminManager", diff --git a/frameworks/native/ability/native/extension.cpp b/frameworks/native/ability/native/extension.cpp index fdab779810..6e8b49d35d 100644 --- a/frameworks/native/ability/native/extension.cpp +++ b/frameworks/native/ability/native/extension.cpp @@ -15,6 +15,7 @@ #include "extension.h" +#include "ability_info.h" #include "ability_local_record.h" #include "configuration.h" #include "extension_context.h" @@ -23,6 +24,12 @@ namespace OHOS { namespace AbilityRuntime { +std::shared_ptr Extension::GetAbilityHandler( + const std::shared_ptr &abilityInfo) +{ + return nullptr; +} + void Extension::Init(const std::shared_ptr &record, const std::shared_ptr &application, std::shared_ptr &handler, @@ -199,6 +206,12 @@ bool Extension::HandleInsightIntent(const AAFwk::Want &want) return true; } +bool Extension::HandleExecuteSkill(const AAFwk::Want &want) +{ + TAG_LOGD(AAFwkTag::EXT, "call"); + return true; +} + bool Extension::OnInsightIntentExecuteDone(uint64_t intentId, const AppExecFwk::InsightIntentExecuteResult &result) { return true; diff --git a/frameworks/native/ability/native/extension_ability_thread.cpp b/frameworks/native/ability/native/extension_ability_thread.cpp index bfccea65ab..0ea215f41a 100644 --- a/frameworks/native/ability/native/extension_ability_thread.cpp +++ b/frameworks/native/ability/native/extension_ability_thread.cpp @@ -25,6 +25,7 @@ #include "js_start_abilities_observer.h" #include "ui_extension_wrapper.h" #include "start_abilities_observer.h" +#include "skill/skill_execute_param.h" namespace OHOS { namespace AbilityRuntime { @@ -243,17 +244,14 @@ void ExtensionAbilityThread::HandleAttach(const std::shared_ptrextensionAbilityType == AppExecFwk::ExtensionAbilityType::CONTENT_EMBED) { HandleNativeExtensionAttach(abilityRecord, abilityName); } else { HandleNormalExtensionAttach(abilityRecord, mainRunner, abilityName); } - - if (abilityHandler_ == nullptr) { - TAG_LOGE(AAFwkTag::EXT, "Failed to create abilityHandler_"); - return; - } - + // 2.new ability auto extension = AppExecFwk::AbilityLoader::GetInstance().GetExtensionByName(abilityName, abilityInfo->arkTSMode); @@ -262,6 +260,19 @@ void ExtensionAbilityThread::HandleAttach(const std::shared_ptrGetAbilityHandler(abilityInfo); + if (customHandler != nullptr) { + TAG_LOGD(AAFwkTag::EXT, "Using extension-provided abilityHandler"); + abilityHandler_ = customHandler; + } + + if (abilityHandler_ == nullptr) { + TAG_LOGE(AAFwkTag::EXT, "Failed to create abilityHandler_"); + return; + } + token_ = abilityRecord->GetToken(); abilityRecord->SetAbilityThread(this); HandleAttachInner(application, abilityRecord); @@ -356,6 +367,7 @@ void ExtensionAbilityThread::HandleConnectExtension(const Want &want) TAG_LOGE(AAFwkTag::EXT, "null extensionImpl_"); return; } + bool isAsyncCallback = false; sptr service = extensionImpl_->ConnectExtension(want, isAsyncCallback); if (!isAsyncCallback) { @@ -413,6 +425,22 @@ void ExtensionAbilityThread::HandleInsightIntent(const Want &want) TAG_LOGD(AAFwkTag::EXT, "End"); } +void ExtensionAbilityThread::HandleExecuteSkill(const Want &want) +{ + HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__); + TAG_LOGD(AAFwkTag::EXT, "Begin"); + if (extensionImpl_ == nullptr) { + TAG_LOGE(AAFwkTag::EXT, "null extensionImpl_"); + return; + } + auto ret = extensionImpl_->HandleExecuteSkill(want); + if (!ret) { + TAG_LOGE(AAFwkTag::EXT, "HandleExecuteSkill failed"); + return; + } + TAG_LOGD(AAFwkTag::EXT, "End"); +} + void ExtensionAbilityThread::HandleCommandExtensionWindow( const Want &want, const sptr &sessionInfo, AAFwk::WindowCommand winCmd) { @@ -532,6 +560,8 @@ void ExtensionAbilityThread::ScheduleCommandAbility(const Want &want, bool resta ScheduleCommandAbilityInner(want, restart, startId); if (AppExecFwk::InsightIntentExecuteParam::IsInsightIntentExecute(want)) { ScheduleInsightIntentInner(want); + } else if (AppExecFwk::SkillExecuteParam::IsSkillExecute(want)) { + ScheduleSkillExecuteInner(want); } TAG_LOGD(AAFwkTag::EXT, "End"); } @@ -570,6 +600,29 @@ void ExtensionAbilityThread::ScheduleInsightIntentInner(const Want &want) } } +void ExtensionAbilityThread::ScheduleSkillExecuteInner(const Want &want) +{ + wptr weak = this; + auto task = [weak, want]() { + auto abilityThread = weak.promote(); + if (abilityThread == nullptr) { + TAG_LOGE(AAFwkTag::EXT, "null AbilityThread"); + return; + } + abilityThread->HandleExecuteSkill(want); + }; + bool ret = abilityHandler_->PostTask(task, AppExecFwk::EventQueue::Priority::HIGH); + if (!ret) { + TAG_LOGE(AAFwkTag::EXT, "PostTask error"); + } +} + +void ExtensionAbilityThread::ExecuteSkill(const Want &want) +{ + TAG_LOGD(AAFwkTag::EXT, "ExecuteSkill called"); + ScheduleSkillExecuteInner(want); +} + void ExtensionAbilityThread::ScheduleCommandAbilityWindow( const Want &want, const sptr &sessionInfo, AAFwk::WindowCommand winCmd) { @@ -774,5 +827,15 @@ void ExtensionAbilityThread::DumpOtherInfo(std::vector &info) runner->DumpRunnerInfo(dumpInfo); info.push_back(dumpInfo); } + +int ExtensionAbilityThread::CreateModalUIExtension(const Want &want) +{ + if (extensionImpl_ == nullptr) { + TAG_LOGE(AAFwkTag::EXT, "null extensionImpl_"); + return ERR_INVALID_VALUE; + } + + return extensionImpl_->CreateModalUIExtension(want); +} } // namespace AbilityRuntime } // namespace OHOS diff --git a/frameworks/native/ability/native/extension_impl.cpp b/frameworks/native/ability/native/extension_impl.cpp index 5d90b701ba..cb1b33f93e 100644 --- a/frameworks/native/ability/native/extension_impl.cpp +++ b/frameworks/native/ability/native/extension_impl.cpp @@ -24,6 +24,7 @@ #include "extension_context.h" #include "hilog_tag_wrapper.h" #include "ui_extension_wrapper.h" +#include "ui_extension.h" namespace OHOS { @@ -432,6 +433,22 @@ bool ExtensionImpl::HandleInsightIntent(const Want &want) return true; } +bool ExtensionImpl::HandleExecuteSkill(const Want &want) +{ + TAG_LOGD(AAFwkTag::EXT, "call"); + if (extension_ == nullptr) { + TAG_LOGE(AAFwkTag::EXT, "null extension_"); + return false; + } + auto ret = extension_->HandleExecuteSkill(want); + if (!ret) { + TAG_LOGE(AAFwkTag::EXT, "handle failed"); + return false; + } + TAG_LOGD(AAFwkTag::EXT, "ok"); + return true; +} + void ExtensionImpl::CommandExtensionWindow(const Want &want, const sptr &sessionInfo, AAFwk::WindowCommand winCmd) { @@ -524,6 +541,27 @@ void ExtensionImpl::Background(const Want &want, sptr sessio lifecycleState_ = AAFwk::ABILITY_STATE_BACKGROUND_NEW; } +int ExtensionImpl::CreateModalUIExtension(const AAFwk::Want &want) +{ + if (!AAFwk::UIExtensionWrapper::IsUIExtension(extensionType_)) { + TAG_LOGE(AAFwkTag::EXT, "Not UIExtension type: %{public}d", static_cast(extensionType_)); + return ERR_INVALID_VALUE; + } + + if (extension_ == nullptr) { + TAG_LOGE(AAFwkTag::EXT, "null extension_"); + return ERR_INVALID_VALUE; + } + + auto uiExtension = std::static_pointer_cast(extension_); + if (uiExtension == nullptr) { + TAG_LOGE(AAFwkTag::EXT, "Failed to cast extension to UIExtension"); + return ERR_INVALID_VALUE; + } + + return uiExtension->CreateModalUIExtension(want); +} + void ExtensionImpl::ExtensionWindowLifeCycleImpl::AfterForeground() { TAG_LOGD(AAFwkTag::EXT, "called"); diff --git a/frameworks/native/ability/native/fa_ability_thread.cpp b/frameworks/native/ability/native/fa_ability_thread.cpp index 38e740709c..85ae212704 100644 --- a/frameworks/native/ability/native/fa_ability_thread.cpp +++ b/frameworks/native/ability/native/fa_ability_thread.cpp @@ -1465,6 +1465,11 @@ void FAAbilityThread::CallRequest() AbilityManagerClient::GetInstance()->CallRequestDone(token_, retval); } +void FAAbilityThread::ExecuteSkill(const Want &want) +{ + TAG_LOGW(AAFwkTag::FA, "FA model does not support ExecuteSkill"); +} + void FAAbilityThread::HandlePrepareTerminateAbility() { std::unique_lock lock(mutex_); diff --git a/frameworks/native/ability/native/insight_intent_executor/ets_insight_intent_entry.cpp b/frameworks/native/ability/native/insight_intent_executor/ets_insight_intent_entry.cpp index 0d3bedd2ff..7691df5d51 100644 --- a/frameworks/native/ability/native/insight_intent_executor/ets_insight_intent_entry.cpp +++ b/frameworks/native/ability/native/insight_intent_executor/ets_insight_intent_entry.cpp @@ -272,8 +272,11 @@ bool EtsInsightIntentEntry::PrepareExecuteEnvironment(ani_env *env, InsightInten return false; } - if (pageLoader == nullptr) { + if (mode == InsightIntentExecuteMode::UIABILITY_BACKGROUND || + mode == InsightIntentExecuteMode::SERVICE_EXTENSION_ABILITY) { return true; + } else if (pageLoader == nullptr) { + return false; } const char *propertyName = mode == InsightIntentExecuteMode::UIEXTENSION_ABILITY ? diff --git a/frameworks/native/ability/native/insight_intent_executor/ets_insight_intent_func.cpp b/frameworks/native/ability/native/insight_intent_executor/ets_insight_intent_func.cpp index aa08423fa3..54f1d2ac13 100644 --- a/frameworks/native/ability/native/insight_intent_executor/ets_insight_intent_func.cpp +++ b/frameworks/native/ability/native/insight_intent_executor/ets_insight_intent_func.cpp @@ -341,11 +341,31 @@ bool EtsInsightIntentFunc::GetMethodArg(ani_env *env, ani_object wantParams, ani return false; } ani_status status = env->Object_CallMethod_Ref(wantParams, recordGetMethod, &valueRef, key); - if (status != ANI_OK || valueRef == nullptr) { + if (status != ANI_OK) { TAG_LOGE(AAFwkTag::INTENT, "get method param failed, name: %{public}s, status: %{public}d", paramName.c_str(), status); return false; } + + ani_boolean isUndefined = ANI_FALSE; + if (valueRef != nullptr) { + status = env->Reference_IsUndefined(valueRef, &isUndefined); + if (status != ANI_OK) { + TAG_LOGE(AAFwkTag::INTENT, "check undefined failed, name: %{public}s, status: %{public}d", + paramName.c_str(), status); + return false; + } + } + + if (valueRef == nullptr || isUndefined == ANI_TRUE) { + if (methodParamInfo.isRequired) { + TAG_LOGE(AAFwkTag::INTENT, "required param missing, name: %{public}s", paramName.c_str()); + return false; + } + TAG_LOGD(AAFwkTag::INTENT, "optional param not provided, name: %{public}s", paramName.c_str()); + return true; + } + if (!ValidateParamType(env, valueRef, methodParamInfo.type)) { TAG_LOGE(AAFwkTag::INTENT, "param type validation failed, name: %{public}s", paramName.c_str()); return false; @@ -410,9 +430,20 @@ bool EtsInsightIntentFunc::ExecuteInsightIntent(ani_env *env, return ExecuteIntentCheckError(); } + ani_static_method method = nullptr; + ani_status status = env->Class_FindStaticMethod(etsObj_->aniCls, executeParam->methodName_.c_str(), + nullptr, &method); + if (status != ANI_OK || method == nullptr) { + TAG_LOGE(AAFwkTag::INTENT, "find static method failed %{public}d", status); + return ExecuteIntentCheckError(); + } + if (IsVoidReturnType(executeParam)) { - ani_status status = env->Class_CallStaticMethodByName_Void_A( - etsObj_->aniCls, executeParam->methodName_.c_str(), nullptr, args.empty() ? nullptr : args.data()); + if (args.empty()) { + status = env->Class_CallStaticMethod_Void(etsObj_->aniCls, method); + } else { + status = env->Class_CallStaticMethod_Void_A(etsObj_->aniCls, method, args.data()); + } if (status != ANI_OK) { TAG_LOGE(AAFwkTag::INTENT, "call static void method failed %{public}d", status); return ExecuteIntentCheckError(); @@ -421,8 +452,11 @@ bool EtsInsightIntentFunc::ExecuteInsightIntent(ani_env *env, } ani_ref result = nullptr; - ani_status status = env->Class_CallStaticMethodByName_Ref_A(etsObj_->aniCls, executeParam->methodName_.c_str(), - nullptr, &result, args.empty() ? nullptr : args.data()); + if (args.empty()) { + status = env->Class_CallStaticMethod_Ref(etsObj_->aniCls, method, &result); + } else { + status = env->Class_CallStaticMethod_Ref_A(etsObj_->aniCls, method, &result, args.data()); + } if (status != ANI_OK || result == nullptr) { TAG_LOGE(AAFwkTag::INTENT, "call static method failed %{public}d", status); return ExecuteIntentCheckError(); diff --git a/frameworks/native/ability/native/insight_intent_executor/js_insight_intent_utils.cpp b/frameworks/native/ability/native/insight_intent_executor/js_insight_intent_utils.cpp index cfd7757b85..42673c95af 100644 --- a/frameworks/native/ability/native/insight_intent_executor/js_insight_intent_utils.cpp +++ b/frameworks/native/ability/native/insight_intent_executor/js_insight_intent_utils.cpp @@ -38,8 +38,9 @@ napi_value JsInsightIntentUtils::CallJsFunctionWithResult( TAG_LOGE(AAFwkTag::INTENT, "null method"); return nullptr; } - + TAG_LOGI(AAFwkTag::INTENT, "call js function"); auto status = napi_call_function(env, obj, method, argc, argv, &result); + TAG_LOGI(AAFwkTag::INTENT, "call js function end"); if (status != napi_ok) { TAG_LOGE(AAFwkTag::INTENT, "napi call function failed %{public}d", status); return nullptr; diff --git a/frameworks/native/ability/native/js_service_extension.cpp b/frameworks/native/ability/native/js_service_extension.cpp index 3f3559a9fc..da66c15b11 100644 --- a/frameworks/native/ability/native/js_service_extension.cpp +++ b/frameworks/native/ability/native/js_service_extension.cpp @@ -38,6 +38,9 @@ #include "napi_common_configuration.h" #include "napi_common_want.h" #include "napi_remote_object.h" +#include "napi_common_util.h" +#include "skill/skill_execute_param.h" +#include "skill/skill_execute_result.h" #ifdef SUPPORT_GRAPHICS #include "iservice_registry.h" #include "system_ability_definition.h" @@ -563,6 +566,112 @@ bool JsServiceExtension::HandleInsightIntent(const AAFwk::Want &want) return true; } +bool JsServiceExtension::HandleExecuteSkill(const AAFwk::Want &want) +{ + TAG_LOGI(AAFwkTag::SERVICE_EXT, "called"); + auto param = std::make_shared(); + bool ret = AppExecFwk::SkillExecuteParam::GenerateFromWant(want, *param); + if (!ret) { + TAG_LOGE(AAFwkTag::SERVICE_EXT, "GenerateFromWant failed"); + return false; + } + ExecuteSkill(want, param); + return true; +} + +napi_value JsServiceExtension::LoadSkillFunction( + const std::shared_ptr ¶m, napi_value &outJsObj) +{ + napi_env env = jsRuntime_.GetNapiEnv(); + std::unique_ptr moduleRef = nullptr; + napi_value method = nullptr; + for (const auto &srcEntry : param->srcEntries_) { + std::string srcPath(param->moduleName_ + "/" + srcEntry); + auto pos = srcPath.rfind('.'); + if (pos == std::string::npos) { + TAG_LOGW(AAFwkTag::SERVICE_EXT, "skip srcEntry, no extension:%{public}s", srcEntry.c_str()); + continue; + } + srcPath.erase(pos); + srcPath.append(".abc"); + moduleRef = jsRuntime_.LoadModule(param->moduleName_, srcPath, param->hapPath_, true); + if (moduleRef == nullptr) { + TAG_LOGW(AAFwkTag::SERVICE_EXT, "LoadModule failed, path:%{public}s", srcPath.c_str()); + continue; + } + outJsObj = moduleRef->GetNapiValue(); + method = AppExecFwk::GetPropertyValueByPropertyName( + env, outJsObj, param->functionName_.c_str(), napi_valuetype::napi_function); + if (method != nullptr) { + TAG_LOGI(AAFwkTag::SERVICE_EXT, "func found in srcEntry:%{public}s", srcEntry.c_str()); + break; + } + TAG_LOGW(AAFwkTag::SERVICE_EXT, "func not found:%{public}s in srcEntry:%{public}s", + param->functionName_.c_str(), srcEntry.c_str()); + } + return method; +} + +std::vector JsServiceExtension::BuildSkillCallArgs(napi_env env, + const std::shared_ptr ¶m) +{ + TAG_LOGI(AAFwkTag::SERVICE_EXT, "execSkill CallFunc inputArgs:%{public}s", param->skillArgs_->ToString().c_str()); + napi_value info = nullptr; + napi_create_object(env, &info); + napi_value requestCodeVal = nullptr; + napi_create_string_utf8(env, param->requestCode_.c_str(), param->requestCode_.length(), &requestCodeVal); + napi_set_named_property(env, info, "requestCode", requestCodeVal); + napi_value contextObj = nullptr; + if (shellContextRef_ != nullptr) { + contextObj = shellContextRef_->GetNapiValue(); + } + napi_set_named_property(env, info, "context", contextObj); + + std::vector args; + args.push_back(info); + if (param->skillArgs_ != nullptr && !param->skillArgs_->GetParams().empty()) { + napi_value wrappedObj = AppExecFwk::WrapWantParams(env, *param->skillArgs_); + for (const auto &[key, value] : param->skillArgs_->GetParams()) { + napi_value val = nullptr; + napi_get_named_property(env, wrappedObj, key.c_str(), &val); + args.push_back(val); + } + } + return args; +} + +void JsServiceExtension::ExecuteSkill(const AAFwk::Want &want, + const std::shared_ptr ¶m) +{ + TAG_LOGD(AAFwkTag::SERVICE_EXT, "ExecuteSkill requestCode:%{public}s", + param != nullptr ? param->requestCode_.c_str() : ""); + if (param == nullptr) { + TAG_LOGE(AAFwkTag::SERVICE_EXT, "null param"); + return; + } + napi_env env = jsRuntime_.GetNapiEnv(); + if (env == nullptr) { + TAG_LOGE(AAFwkTag::SERVICE_EXT, "null napi env, skill will time out"); + return; + } + napi_value jsObj = nullptr; + napi_value method = LoadSkillFunction(param, jsObj); + if (method == nullptr) { + TAG_LOGE(AAFwkTag::SERVICE_EXT, "func not found in any srcEntry:%{public}s", param->functionName_.c_str()); + return; + } + auto args = BuildSkillCallArgs(env, param); + napi_value result = nullptr; + napi_status status = napi_call_function(env, jsObj, method, args.size(), args.data(), &result); + if (status != napi_ok) { + TAG_LOGE(AAFwkTag::SERVICE_EXT, "napi_call_function failed, status:%{public}d func:%{public}s", + status, param->functionName_.c_str()); + return; + } + TAG_LOGD(AAFwkTag::SERVICE_EXT, + "ExecuteSkill dispatched, requestCode:%{public}s", param->requestCode_.c_str()); +} + napi_value JsServiceExtension::CallObjectMethod(const char* name, napi_value const* argv, size_t argc) { HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, std::string("CallObjectMethod:") + name); diff --git a/frameworks/native/ability/native/js_service_extension_context.cpp b/frameworks/native/ability/native/js_service_extension_context.cpp index 52f3678900..ae69b9a65d 100644 --- a/frameworks/native/ability/native/js_service_extension_context.cpp +++ b/frameworks/native/ability/native/js_service_extension_context.cpp @@ -38,6 +38,9 @@ #include "open_link/napi_common_open_link_options.h" #include "start_options.h" #include "hitrace_meter.h" +#ifdef HIVIEWDFX_RUNTIME_API_METRICS +#include "histogram_plugin_macros.h" +#endif #include "uri.h" namespace OHOS { @@ -682,7 +685,13 @@ private: if (!CheckStartAbilityByCallInputParam(env, info, want, accountId)) { return CreateJsUndefined(env); } - +#ifdef HIVIEWDFX_RUNTIME_API_METRICS + if (accountId == DEFAULT_INVAL_VALUE) { + HISTOGRAM_BOOLEAN("AbilityKit.ServiceExtensionContext.startAbilityByCall", 1); + } else { + HISTOGRAM_BOOLEAN("AbilityKit.ServiceExtensionContext.startAbilityByCallWithAccount", 1); + } +#endif auto calls = std::make_shared(); napi_value result = nullptr; calls->callerCallBack = std::make_shared(); @@ -1029,6 +1038,9 @@ private: { HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__); TAG_LOGD(AAFwkTag::SERVICE_EXT, "called"); +#ifdef HIVIEWDFX_RUNTIME_API_METRICS + HISTOGRAM_BOOLEAN("AbilityKit.ServiceExtensionContext.connectServiceExtensionAbility", 1); +#endif // Check params count if (info.argc < ARGC_TWO) { TAG_LOGE(AAFwkTag::SERVICE_EXT, "invalid argc"); @@ -1074,6 +1086,10 @@ private: { HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__); TAG_LOGI(AAFwkTag::SERVICE_EXT, "ConnectAbilityWithAccount"); +#ifdef HIVIEWDFX_RUNTIME_API_METRICS + HISTOGRAM_BOOLEAN( + "AbilityKit.ServiceExtensionContext.connectServiceExtensionAbilityWithAccount", 1); +#endif // Unwrap want, accountId and connection AAFwk::Want want; int32_t accountId = 0; @@ -1221,6 +1237,9 @@ private: { HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__); TAG_LOGI(AAFwkTag::SERVICE_EXT, "called"); +#ifdef HIVIEWDFX_RUNTIME_API_METRICS + HISTOGRAM_BOOLEAN("AbilityKit.ServiceExtensionContext.startServiceExtensionAbility", 1); +#endif if (info.argc < ARGC_ONE) { TAG_LOGE(AAFwkTag::SERVICE_EXT, "invalid argc"); ThrowTooFewParametersError(env); diff --git a/frameworks/native/ability/native/modular_object_extension/modular_object_extension.cpp b/frameworks/native/ability/native/modular_object_extension/modular_object_extension.cpp index 13793c1e73..eee852107c 100644 --- a/frameworks/native/ability/native/modular_object_extension/modular_object_extension.cpp +++ b/frameworks/native/ability/native/modular_object_extension/modular_object_extension.cpp @@ -17,8 +17,10 @@ #include +#include "ability_handler.h" #include "hilog_tag_wrapper.h" #include "ipc_inner_object.h" +#include "modular_object_extension_info.h" #include "native_runtime.h" #include "securec.h" #include "want_manager.h" @@ -53,6 +55,7 @@ void ModularObjectExtension::Init(const std::shared_ptr &rec moeContext_->type = AppExecFwk::ExtensionAbilityType::MODULAR_OBJECT; auto context = GetContext(); if (context != nullptr) { + context->SetEventHandler(std::static_pointer_cast(handler)); moeContext_->context = context->weak_from_this(); } moeInstance_->context = moeContext_; @@ -92,6 +95,10 @@ void ModularObjectExtension::OnStop() if (moeInstance_ != nullptr && moeInstance_->onDestroyFunc != nullptr) { moeInstance_->onDestroyFunc(moeInstance_.get()); } + if (!threadKey_.empty()) { + ModularObjectWorkerManager::GetInstance().ReleaseWorkerThread(threadKey_); + threadKey_.clear(); + } } sptr ModularObjectExtension::OnConnect(const AAFwk::Want &want) @@ -124,6 +131,50 @@ void ModularObjectExtension::OnDisconnect(const AAFwk::Want &want) } } +std::shared_ptr ModularObjectExtension::GetAbilityHandler( + const std::shared_ptr &abilityInfo) +{ + if (abilityInfo == nullptr) { + TAG_LOGE(AAFwkTag::EXT, "null abilityInfo"); + return nullptr; + } + // Read threadMode from metadata + AAFwk::MoeThreadMode threadMode = AAFwk::MoeThreadMode::BUNDLE; // default + for (const auto &meta : abilityInfo->metadata) { + if (meta.name == "threadMode") { + if (meta.value == "BUNDLE") { + threadMode = AAFwk::MoeThreadMode::BUNDLE; + } else if (meta.value == "TYPE") { + threadMode = AAFwk::MoeThreadMode::TYPE; + } else if (meta.value == "INSTANCE") { + threadMode = AAFwk::MoeThreadMode::INSTANCE; + } + break; + } + } + auto &workerMgr = ModularObjectWorkerManager::GetInstance(); + std::string threadKey; + switch (threadMode) { + case AAFwk::MoeThreadMode::BUNDLE: + threadKey = abilityInfo->bundleName; + break; + case AAFwk::MoeThreadMode::INSTANCE: { + uint32_t instanceId = workerMgr.GenerateInstanceId(); + threadKey = abilityInfo->bundleName + "_" + abilityInfo->name + "_" + std::to_string(instanceId); + break; + } + case AAFwk::MoeThreadMode::TYPE: + default: + threadKey = abilityInfo->bundleName + "_" + abilityInfo->name; + break; + } + auto handler = workerMgr.GetOrCreateWorkerThread(threadKey); + if (handler != nullptr) { + threadKey_ = threadKey; + } + return handler; +} + bool ModularObjectExtension::LoadNativeExtensionModule() { if (moeInstance_ == nullptr || abilityInfo_ == nullptr) { diff --git a/frameworks/native/ability/native/modular_object_extension/modular_object_extension_context_impl.cpp b/frameworks/native/ability/native/modular_object_extension/modular_object_extension_context_impl.cpp index a1aaa89285..29c22da701 100644 --- a/frameworks/native/ability/native/modular_object_extension/modular_object_extension_context_impl.cpp +++ b/frameworks/native/ability/native/modular_object_extension/modular_object_extension_context_impl.cpp @@ -24,6 +24,17 @@ namespace AbilityRuntime { const size_t ModularObjectExtensionContext::CONTEXT_TYPE_ID( std::hash {} ("ModularObjectExtensionContext")); +void ModularObjectExtensionContext::SetEventHandler( + const std::shared_ptr &handler) +{ + handler_ = handler; +} + +std::shared_ptr ModularObjectExtensionContext::GetEventHandler() const +{ + return handler_.lock(); +} + ErrCode ModularObjectExtensionContext::StartSelfUIAbility(const AAFwk::Want &want) const { HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__); diff --git a/frameworks/native/ability/native/modular_object_worker_manager.cpp b/frameworks/native/ability/native/modular_object_worker_manager.cpp new file mode 100644 index 0000000000..199f014e5a --- /dev/null +++ b/frameworks/native/ability/native/modular_object_worker_manager.cpp @@ -0,0 +1,82 @@ +/* + * 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 "modular_object_worker_manager.h" + +#include "hilog_tag_wrapper.h" + +namespace OHOS { +namespace AbilityRuntime { + +ModularObjectWorkerManager &ModularObjectWorkerManager::GetInstance() +{ + static ModularObjectWorkerManager instance; + return instance; +} + +std::shared_ptr ModularObjectWorkerManager::GetOrCreateWorkerThread( + const std::string &threadKey) +{ + std::lock_guard lock(workerMutex_); + auto iter = workerMap_.find(threadKey); + if (iter != workerMap_.end()) { + iter->second.refCount++; + TAG_LOGD(AAFwkTag::EXT, "reuse existing worker thread: %{public}s, refCount=%{public}u", + threadKey.c_str(), iter->second.refCount); + return iter->second.handler; + } + auto runner = AppExecFwk::EventRunner::Create(threadKey); + if (runner == nullptr) { + TAG_LOGE(AAFwkTag::EXT, "failed to create event runner for: %{public}s", threadKey.c_str()); + return nullptr; + } + auto handler = std::make_shared(runner); + if (handler == nullptr) { + TAG_LOGE(AAFwkTag::EXT, "failed to create ability handler for: %{public}s", threadKey.c_str()); + return nullptr; + } + WorkerEntry entry; + entry.handler = handler; + entry.refCount = 1; + workerMap_.emplace(threadKey, std::move(entry)); + TAG_LOGI(AAFwkTag::EXT, "created new worker thread: %{public}s", threadKey.c_str()); + return handler; +} + +void ModularObjectWorkerManager::ReleaseWorkerThread(const std::string &threadKey) +{ + std::lock_guard lock(workerMutex_); + auto iter = workerMap_.find(threadKey); + if (iter == workerMap_.end()) { + TAG_LOGW(AAFwkTag::EXT, "worker thread not found: %{public}s", threadKey.c_str()); + return; + } + iter->second.refCount--; + if (iter->second.refCount == 0) { + workerMap_.erase(iter); + TAG_LOGI(AAFwkTag::EXT, "removed worker thread: %{public}s", threadKey.c_str()); + } else { + TAG_LOGD(AAFwkTag::EXT, "release worker thread: %{public}s, remaining refCount=%{public}u", + threadKey.c_str(), iter->second.refCount); + } +} + +uint32_t ModularObjectWorkerManager::GenerateInstanceId() +{ + return instanceId_.fetch_add(1); +} + +} // namespace AbilityRuntime +} // namespace OHOS diff --git a/frameworks/native/ability/native/recovery/app_recovery.cpp b/frameworks/native/ability/native/recovery/app_recovery.cpp index 830b97070f..59ce9ccf6f 100644 --- a/frameworks/native/ability/native/recovery/app_recovery.cpp +++ b/frameworks/native/ability/native/recovery/app_recovery.cpp @@ -33,6 +33,7 @@ #include "js_runtime.h" #include "js_runtime_utils.h" #include "js_ui_ability.h" +#include "jsnapi_expo.h" #include "mission_info.h" #include "napi/native_api.h" #include "napi/native_common.h" @@ -191,6 +192,9 @@ bool AppRecovery::ScheduleSaveAppState(StateReason reason, uintptr_t ability) { TAG_LOGI(AAFwkTag::RECOVERY, "begin"); bool ret = IsNeedSaveAppState(reason); + if (reason == StateReason::APP_FREEZE) { + panda::JSNApi::NotifyOnANR(); + } if (!ret && this->freezeCallback == nullptr) { return false; } diff --git a/frameworks/native/ability/native/ui_ability_impl.cpp b/frameworks/native/ability/native/ui_ability_impl.cpp index 204f9720f0..cb7443b13e 100644 --- a/frameworks/native/ability/native/ui_ability_impl.cpp +++ b/frameworks/native/ability/native/ui_ability_impl.cpp @@ -15,6 +15,8 @@ #include "ui_ability_impl.h" +#include + #include "ability_handler.h" #include "ability_manager_client.h" #include "context/application_context.h" @@ -258,6 +260,32 @@ void UIAbilityImpl::ExecuteInsightIntentDone(uint64_t intentId, const InsightInt TAG_LOGE(AAFwkTag::UIABILITY, "notify execute done failed"); } } + +bool UIAbilityImpl::HandleExecuteSkill(const AAFwk::Want &want, bool onlyExecuteSkill) +{ + TAG_LOGD(AAFwkTag::UIABILITY, "handle execute skill"); + auto param = std::make_shared(); + if (!SkillExecuteParam::GenerateFromWant(want, *param)) { + TAG_LOGE(AAFwkTag::UIABILITY, "generate skill param from want failed"); + if (!onlyExecuteSkill) { + Background(); + } + return true; + } + + TAG_LOGD(AAFwkTag::UIABILITY, + "skill bundle:%{public}s module:%{public}s name:%{public}s " + "scriptPath:%{public}s func:%{public}s requestCode:%{public}s", + param->bundleName_.c_str(), param->moduleName_.c_str(), + param->skillName_.c_str(), param->scriptPath_.c_str(), + param->functionName_.c_str(), param->requestCode_.c_str()); + ability_->ExecuteSkill(want, param); + if (!onlyExecuteSkill) { + Background(); + } + return true; +} + #ifdef SUPPORT_SCREEN bool UIAbilityImpl::PrepareTerminateAbility(std::function callback, bool &isAsync) { @@ -683,11 +711,14 @@ bool UIAbilityImpl::AbilityTransaction(const AAFwk::Want &want, const AAFwk::Lif } OnWillBackground(); #ifdef SUPPORT_GRAPHICS - if (!InsightIntentExecuteParam::IsInsightIntentExecute(want)) { - Background(); - } else { + if (InsightIntentExecuteParam::IsInsightIntentExecute(want)) { TAG_LOGD(AAFwkTag::UIABILITY, "handleExecuteInsightIntentBackground"); ret = HandleExecuteInsightIntentBackground(want); + } else if (SkillExecuteParam::IsSkillExecute(want)) { + TAG_LOGD(AAFwkTag::UIABILITY, "handleExecuteSkill"); + ret = HandleExecuteSkill(want); + } else { + Background(); } #endif break; @@ -808,12 +839,25 @@ void UIAbilityImpl::ExecuteInsightIntentMoveToForeground(const Want &want, std::unique_ptr callback) { TAG_LOGD(AAFwkTag::INTENT, "called"); + if (ability_ == nullptr) { + TAG_LOGE(AAFwkTag::INTENT, "ability null"); + return; + } { std::lock_guard lock(notifyForegroundLock_); notifyForegroundByWindow_ = false; } + auto oriHideValue = ability_->CheckIsSilentForeground(); + if (localNativeState_ == LocalNativeState::INIT_PRE_FOREGROUND) { + TAG_LOGI(AAFwkTag::UIABILITY, "Native module startPhase is PRE_FOREGROUND, skip OnForeground"); + localNativeState_ = LocalNativeState::HALF_FOREGROUND; + ability_->SetIsSilentForeground(true); + std::lock_guard lock(notifyForegroundLock_); + notifyForegroundByWindow_ = true; + } + auto asyncCallback = [weak = weak_from_this(), intentId = executeParam->insightIntentId_](InsightIntentExecuteResult result) { TAG_LOGD(AAFwkTag::INTENT, "end, intentId %{public}" PRIu64"", intentId); @@ -829,6 +873,7 @@ void UIAbilityImpl::ExecuteInsightIntentMoveToForeground(const Want &want, // private function, no need check ability_ validity. ability_->ExecuteInsightIntentMoveToForeground(want, executeParam, std::move(callback)); + ability_->SetIsSilentForeground(oriHideValue); } void UIAbilityImpl::ExecuteInsightIntentPage(const Want &want, diff --git a/frameworks/native/ability/native/ui_ability_thread.cpp b/frameworks/native/ability/native/ui_ability_thread.cpp index 0001a8db77..140e8f6fa9 100644 --- a/frameworks/native/ability/native/ui_ability_thread.cpp +++ b/frameworks/native/ability/native/ui_ability_thread.cpp @@ -25,6 +25,7 @@ #include "freeze_util.h" #include "hilog_tag_wrapper.h" #include "hitrace_meter.h" +#include "skill/skill_execute_param.h" #include "time_util.h" namespace OHOS { @@ -688,12 +689,36 @@ void UIAbilityThread::OnExecuteIntent(const Want &want) } if (abilityThread->abilityImpl_ != nullptr) { abilityThread->abilityImpl_->HandleExecuteInsightIntentBackground(want, true); - return; } }; abilityHandler_->PostTask(task, "UIAbilityThread:OnExecuteIntent"); } +void UIAbilityThread::ExecuteSkill(const Want &want) +{ + TAG_LOGI(AAFwkTag::UIABILITY, "execute skill"); + if (abilityImpl_ == nullptr) { + TAG_LOGE(AAFwkTag::UIABILITY, "null abilityImpl_"); + return; + } + if (abilityHandler_ == nullptr) { + TAG_LOGE(AAFwkTag::UIABILITY, "null abilityHandler_"); + return; + } + wptr weak = this; + auto task = [weak, want]() { + auto abilityThread = weak.promote(); + if (abilityThread == nullptr) { + TAG_LOGE(AAFwkTag::UIABILITY, "null AbilityThread"); + return; + } + if (abilityThread->abilityImpl_ != nullptr) { + abilityThread->abilityImpl_->HandleExecuteSkill(want, true); + } + }; + abilityHandler_->PostTask(task, "UIAbilityThread:ExecuteSkill"); +} + #ifdef SUPPORT_SCREEN int UIAbilityThread::CreateModalUIExtension(const Want &want) { diff --git a/frameworks/native/ability/native/ui_extension_ability/js_ui_extension.cpp b/frameworks/native/ability/native/ui_extension_ability/js_ui_extension.cpp index 64d1347ae3..d712844b21 100755 --- a/frameworks/native/ability/native/ui_extension_ability/js_ui_extension.cpp +++ b/frameworks/native/ability/native/ui_extension_ability/js_ui_extension.cpp @@ -165,7 +165,9 @@ void JsUIExtension::Init(const std::shared_ptr &record, HandleScope handleScope(jsRuntime_); auto env = jsRuntime_.GetNapiEnv(); - jsObj_ = jsRuntime_.LoadModule( + std::string key = moduleName + "::" + srcPath; + std::unique_ptr moduleObj = nullptr; + jsObj_ = jsRuntime_.PopPreloadObj(key, moduleObj) ? std::move(moduleObj) : jsRuntime_.LoadModule( moduleName, srcPath, abilityInfo_->hapPath, abilityInfo_->compileMode == CompileMode::ES_MODULE, false, abilityInfo_->srcEntrance); if (jsObj_ == nullptr) { diff --git a/frameworks/native/ability/native/ui_extension_ability/ui_extension.cpp b/frameworks/native/ability/native/ui_extension_ability/ui_extension.cpp index 4f010d4e00..58d58e3bbb 100755 --- a/frameworks/native/ability/native/ui_extension_ability/ui_extension.cpp +++ b/frameworks/native/ability/native/ui_extension_ability/ui_extension.cpp @@ -108,6 +108,7 @@ void UIExtension::Init(const std::shared_ptr &record, const sptr &token) { TAG_LOGD(AAFwkTag::UI_EXT, "called"); + handler_ = handler; ExtensionBase::Init(record, application, handler, token); } @@ -408,5 +409,41 @@ void UIExtension::RegisterAbilityConfigUpdateCallback() abilitySptr->OnAbilityConfigurationUpdated(config); }); } + +int UIExtension::CreateModalUIExtension(const AAFwk::Want &want) +{ + auto context = GetContext(); + if (context == nullptr) { + TAG_LOGE(AAFwkTag::UI_EXT, "context is null"); + return ERR_INVALID_VALUE; + } + + if (handler_ == nullptr) { + TAG_LOGE(AAFwkTag::UI_EXT, "null handler_"); + return ERR_INVALID_VALUE; + } + + auto uiExtensionContext = Context::ConvertTo(context); + if (uiExtensionContext == nullptr) { + TAG_LOGE(AAFwkTag::UI_EXT, "uiExtensionContext is null"); + return ERR_INVALID_VALUE; + } + + std::weak_ptr uiExtensionContextWptr = uiExtensionContext; + auto task = [uiExtensionContextWptr, want]() { + std::shared_ptr uiExtensionContextSptr = uiExtensionContextWptr.lock(); + if (uiExtensionContextSptr == nullptr) { + TAG_LOGE(AAFwkTag::UI_EXT, "null uiExtensionContext in task"); + return; + } + (void)uiExtensionContextSptr->CreateModalUIExtensionWithApp(want); + }; + if (!handler_->PostTask(task, "UIExtension:CreateModalUIExtensionWithApp")) { + TAG_LOGE(AAFwkTag::UI_EXT, "PostTask failed"); + return ERR_INVALID_VALUE; + } + + return ERR_OK; +} } } diff --git a/frameworks/native/ability/native/ui_extension_base/js_ui_extension_base.cpp b/frameworks/native/ability/native/ui_extension_base/js_ui_extension_base.cpp index 19ceef32fc..9d3f23c19c 100644 --- a/frameworks/native/ability/native/ui_extension_base/js_ui_extension_base.cpp +++ b/frameworks/native/ability/native/ui_extension_base/js_ui_extension_base.cpp @@ -159,7 +159,9 @@ std::shared_ptr JsUIExtensionBase::Init(const std::shared_ptrname); HandleScope handleScope(jsRuntime_); - jsObj_ = jsRuntime_.LoadModule( + std::string key = moduleName + "::" + srcPath; + std::unique_ptr moduleObj = nullptr; + jsObj_ = jsRuntime_.PopPreloadObj(key, moduleObj) ? std::move(moduleObj) : jsRuntime_.LoadModule( moduleName, srcPath, abilityInfo_->hapPath, abilityInfo_->compileMode == CompileMode::ES_MODULE, false, abilityInfo_->srcEntrance); if (jsObj_ == nullptr) { diff --git a/frameworks/native/ability/native/ui_extension_base/ui_extension_context.cpp b/frameworks/native/ability/native/ui_extension_base/ui_extension_context.cpp index 7fc85ec899..a2734c2328 100755 --- a/frameworks/native/ability/native/ui_extension_base/ui_extension_context.cpp +++ b/frameworks/native/ability/native/ui_extension_base/ui_extension_context.cpp @@ -26,6 +26,7 @@ #include "hitrace_meter.h" #include "string_wrapper.h" #include "ui_content.h" +#include "ui_extension_modal_callback.h" namespace OHOS { namespace AbilityRuntime { @@ -37,6 +38,10 @@ constexpr const char* FLAG_AUTH_READ_URI_PERMISSION = "ability.want.params.uriPe constexpr int32_t TERMINATE_SELF_ANIMATION_TIMEOUT_MS = 2000; namespace { +constexpr const char* DISPOSED_PROHIBIT_BACK = "ohos.disposed.prohibitBack"; +constexpr const char* IS_WINDOWMODE_FOLLOWHOST = "ohos.window.mode.followHost"; +constexpr const char* USE_GLOBAL_UICONTENT = "ohos.uec.params.useGlobalUIContent"; + bool IsEmbeddableStart(int32_t screenMode) { return screenMode == AAFwk::EMBEDDED_FULL_SCREEN_MODE || @@ -956,6 +961,127 @@ void UIExtensionContext::GetFailureInfoByMessage( } } +bool UIExtensionContext::IsUIExtensionExist(const AAFwk::Want &want) +{ + TAG_LOGD(AAFwkTag::UI_EXT, "IsUIExtensionExist call"); + std::lock_guard lock(uiExtensionMutex_); + for (const auto& iter : uiExtensionMap_) { + const auto& wantElement = want.GetElement(); + const auto& iterElement = iter.second.GetElement(); + if (iterElement.GetBundleName() == wantElement.GetBundleName() && + iterElement.GetModuleName() == wantElement.GetModuleName() && + iterElement.GetAbilityName() == wantElement.GetAbilityName()) { + TAG_LOGI(AAFwkTag::UI_EXT, "UIExtension already exists: %{public}s/%{public}s/%{public}s", + wantElement.GetBundleName().c_str(), + wantElement.GetModuleName().c_str(), + wantElement.GetAbilityName().c_str()); + return true; + } + } + return false; +} + +ErrCode UIExtensionContext::CreateModalUIExtensionWithApp(const AAFwk::Want &want) +{ + TAG_LOGD(AAFwkTag::UI_EXT, "UIExtensionContext::CreateModalUIExtensionWithApp call"); + auto uiContent = GetUIContent(); + if (uiContent == nullptr) { + TAG_LOGE(AAFwkTag::UI_EXT, "null uiContent"); + return ERR_INVALID_VALUE; + } + + // Check if UIExtension already exists + if (IsUIExtensionExist(want) && !want.GetBoolParam(USE_GLOBAL_UICONTENT, false)) { + TAG_LOGD(AAFwkTag::UI_EXT, "UIExtension already exists"); + return ERR_OK; + } + + // Create modal callback + auto modalCallback = std::make_shared(); + + // Setup Ace callbacks + Ace::ModalUIExtensionCallbacks callback = SetupModalCallbacks(modalCallback); + + // Configure modal UIExtension + Ace::ModalUIExtensionConfig config; + config.prohibitedRemoveByRouter = true; // Prevent router from removing + + // Support additional configuration parameters + if (want.GetBoolParam(DISPOSED_PROHIBIT_BACK, false)) { + config.isProhibitBack = true; + } + if (want.HasParameter(IS_WINDOWMODE_FOLLOWHOST)) { + config.isWindowModeFollowHost = want.GetBoolParam(IS_WINDOWMODE_FOLLOWHOST, false); + } + + // Create modal UIExtension + int32_t sessionId = uiContent->CreateModalUIExtension(want, callback, config); + if (sessionId == 0) { + TAG_LOGE(AAFwkTag::UI_EXT, "CreateModalUIExtension failed"); + return ERR_INVALID_VALUE; + } + + // Initialize callback with session info + modalCallback->SetSessionId(sessionId); + modalCallback->SetUIContent(uiContent); + + auto contextWeak = std::static_pointer_cast(shared_from_this()); + modalCallback->SetUIExtensionContext(contextWeak); + + // Track the session in map + { + std::lock_guard lock(uiExtensionMutex_); + uiExtensionMap_.emplace(sessionId, want); + } + + TAG_LOGI(AAFwkTag::UI_EXT, "Created modal UIExtension, sessionId: %{public}d", sessionId); + return ERR_OK; +} + +Ace::ModalUIExtensionCallbacks UIExtensionContext::SetupModalCallbacks( + std::shared_ptr modalCallback) +{ + Ace::ModalUIExtensionCallbacks callback; + callback.onError = [modalCallback](int32_t code, const std::string &str1, const std::string &str2) { + TAG_LOGE(AAFwkTag::UI_EXT, "Modal UIExtension error: %{public}d, msg: %{public}s", + code, str2.c_str()); + modalCallback->OnError(); + }; + callback.onRelease = [modalCallback](int32_t code) { + TAG_LOGD(AAFwkTag::UI_EXT, "Modal UIExtension release: %{public}d", code); + modalCallback->OnRelease(); + }; + callback.onResult = [modalCallback](int32_t code, const AAFwk::Want &resultWant) { + TAG_LOGD(AAFwkTag::UI_EXT, "Modal UIExtension result: %{public}d", code); + }; + callback.onDestroy = [modalCallback]() { + TAG_LOGD(AAFwkTag::UI_EXT, "Modal UIExtension destroy"); + modalCallback->OnDestroy(); + }; + callback.onReceive = [modalCallback](const AAFwk::WantParams& data) { + TAG_LOGD(AAFwkTag::UI_EXT, "Modal UIExtension onReceive"); + modalCallback->OnReceive(data); + }; + return callback; +} + +ErrCode UIExtensionContext::EraseUIExtension(int32_t sessionId) +{ + TAG_LOGD(AAFwkTag::UI_EXT, "EraseUIExtension: %{public}d", sessionId); + + std::lock_guard lock(uiExtensionMutex_); + auto it = uiExtensionMap_.find(sessionId); + if (it != uiExtensionMap_.end()) { + uiExtensionMap_.erase(it); + TAG_LOGI(AAFwkTag::UI_EXT, "Erased UIExtension: %{public}d, remaining: %{public}zu", + sessionId, uiExtensionMap_.size()); + } else { + TAG_LOGW(AAFwkTag::UI_EXT, "UIExtension not found: %{public}d", sessionId); + } + + return ERR_OK; +} + int32_t UIExtensionContext::curRequestCode_ = 0; std::mutex UIExtensionContext::requestCodeMutex_; } // namespace AbilityRuntime diff --git a/frameworks/native/ability/native/ui_extension_base/ui_extension_modal_callback.cpp b/frameworks/native/ability/native/ui_extension_base/ui_extension_modal_callback.cpp new file mode 100644 index 0000000000..1dd60cfbd7 --- /dev/null +++ b/frameworks/native/ability/native/ui_extension_base/ui_extension_modal_callback.cpp @@ -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 "ui_extension_modal_callback.h" +#include "ui_extension_context.h" +#include "want.h" + +namespace OHOS { +namespace AbilityRuntime { +namespace { +constexpr const char* EMBEDDABLE_SERVICE_EXIT = "ohos.param.exitEmbeddableUIExtension"; +} + +void UIExtensionModalCallback::OnRelease() +{ + TAG_LOGD(AAFwkTag::UI_EXT, "UIExtensionModalCallback::OnRelease, sessionId: %{public}d", sessionId_); + + auto context = contextWeak_.lock(); + if (context == nullptr) { + TAG_LOGE(AAFwkTag::UI_EXT, "Context already destroyed in OnRelease"); + return; + } + + context->EraseUIExtension(sessionId_); + TAG_LOGI(AAFwkTag::UI_EXT, "Erased modal UIExtension on release: %{public}d", sessionId_); + +#ifdef SUPPORT_SCREEN + // Close the modal UIExtension + if (uiContent_ != nullptr) { + uiContent_->CloseModalUIExtension(sessionId_); + TAG_LOGI(AAFwkTag::UI_EXT, "Closed modal UIExtension: %{public}d", sessionId_); + } else { + TAG_LOGE(AAFwkTag::UI_EXT, "null uiContent_ in OnRelease"); + } +#endif // SUPPORT_SCREEN +} + +void UIExtensionModalCallback::OnError() +{ + TAG_LOGE(AAFwkTag::UI_EXT, "UIExtensionModalCallback::OnError, sessionId: %{public}d", sessionId_); + + auto context = contextWeak_.lock(); + if (context == nullptr) { + TAG_LOGE(AAFwkTag::UI_EXT, "Context already destroyed in OnError"); + return; + } + + // Erase from context's map + context->EraseUIExtension(sessionId_); + +#ifdef SUPPORT_SCREEN + // Close the modal UIExtension on error + if (uiContent_ != nullptr) { + uiContent_->CloseModalUIExtension(sessionId_); + TAG_LOGI(AAFwkTag::UI_EXT, "Closed modal UIExtension on error: %{public}d", sessionId_); + } else { + TAG_LOGE(AAFwkTag::UI_EXT, "null uiContent_ in OnError"); + } +#endif // SUPPORT_SCREEN +} + +void UIExtensionModalCallback::OnDestroy() +{ + TAG_LOGD(AAFwkTag::UI_EXT, "UIExtensionModalCallback::OnDestroy, sessionId: %{public}d", sessionId_); + + auto context = contextWeak_.lock(); + if (context == nullptr) { + TAG_LOGE(AAFwkTag::UI_EXT, "Context already destroyed in OnDestroy"); + return; + } + + // Only erase from context's map + // UIExtension is already closed by the system + context->EraseUIExtension(sessionId_); + TAG_LOGI(AAFwkTag::UI_EXT, "Erased modal UIExtension on destroy: %{public}d", sessionId_); +} + +void UIExtensionModalCallback::OnReceive(const AAFwk::WantParams& data) +{ + TAG_LOGD(AAFwkTag::UI_EXT, "UIExtensionModalCallback::OnReceive, sessionId: %{public}d", sessionId_); + + auto context = contextWeak_.lock(); + if (context == nullptr) { + TAG_LOGE(AAFwkTag::UI_EXT, "Context already destroyed in OnReceive"); + return; + } + + if (data.HasParam(EMBEDDABLE_SERVICE_EXIT)) { + bool shouldExit = data.GetIntParam(EMBEDDABLE_SERVICE_EXIT, 0); + if (shouldExit == 1) { + TAG_LOGI(AAFwkTag::UI_EXT, "Modal dialog notified embeddable exit, sessionId: %{public}d", sessionId_); + context->TerminateSelfWithAnimation(nullptr); + return; + } + } +} +} // namespace AbilityRuntime +} // namespace OHOS diff --git a/frameworks/native/appkit/BUILD.gn b/frameworks/native/appkit/BUILD.gn index 39455ad0f3..48f0cffe17 100644 --- a/frameworks/native/appkit/BUILD.gn +++ b/frameworks/native/appkit/BUILD.gn @@ -97,6 +97,7 @@ ohos_shared_library("appkit_native") { "${ability_runtime_napi_path}/inner/napi_common/ani_common", "${ability_runtime_utils_path}/global/constant", "${ability_runtime_path}/interfaces/inner_api/app_manager", + "${ability_runtime_path}/interfaces/kits/c/ability_runtime", ] configs = [ @@ -184,6 +185,7 @@ ohos_shared_library("appkit_native") { "${ability_runtime_path}/js_environment/frameworks/js_environment:js_environment", "${ability_runtime_path}/utils/global/common:runtime_utils", "${ability_runtime_path}/utils/global/freeze:freeze_util", + "${ability_runtime_path}/utils/server/startup:startup_util", "${ability_runtime_services_path}/common:app_util", "${ability_runtime_services_path}/common:hisysevent_report", "${ability_runtime_services_path}/common:task_handler_wrap", @@ -895,7 +897,10 @@ ohos_shared_library("appkit_child") { debug = false } - include_dirs = [] + include_dirs = [ + "${ability_runtime_path}/interfaces/kits/c/ability_runtime", + "${ability_runtime_path}/interfaces/inner_api/connect_server_manager/include", + ] configs = [ ":appkit_config", diff --git a/frameworks/native/appkit/ability_runtime/context/ability_native_thread.cpp b/frameworks/native/appkit/ability_runtime/context/ability_native_thread.cpp index 0fe6a59abe..42bbc1c030 100644 --- a/frameworks/native/appkit/ability_runtime/context/ability_native_thread.cpp +++ b/frameworks/native/appkit/ability_runtime/context/ability_native_thread.cpp @@ -76,14 +76,15 @@ bool AbilityNativeThread::LoadNativeModule(const AAFwk::NativeAbilityMetaData& m TAG_LOGI(AAFwkTag::ABILITY, "OHMain function found: %{public}s", metaData.nativeModuleFunc.c_str()); // Find the PostAbility function (optional but recommended) - auto rawPostAbility = reinterpret_cast(dlsym(moduleHandle_, "PostAbility")); + auto rawPostAbility = + reinterpret_cast(dlsym(moduleHandle_, "PostAbility")); if (rawPostAbility != nullptr) { postAbilityFunc_ = rawPostAbility; TAG_LOGI(AAFwkTag::ABILITY, "PostAbility function found"); } // Find the DestroyAbility function (optional) - auto rawDestroyAbility = reinterpret_cast( + auto rawDestroyAbility = reinterpret_cast( dlsym(moduleHandle_, "DestroyAbility")); if (rawDestroyAbility != nullptr) { destroyAbilityFunc_ = rawDestroyAbility; @@ -126,7 +127,7 @@ void AbilityNativeThread::RunMain() TAG_LOGI(AAFwkTag::ABILITY, "Native thread created"); } -void AbilityNativeThread::PostAbility(const NativeAbilityWrapper* nativeAbilityWrapper) +void AbilityNativeThread::PostAbility(const AbilityRuntime_NativeAbilityWrapper* nativeAbilityWrapper) { if (nativeAbilityWrapper == nullptr) { TAG_LOGE(AAFwkTag::ABILITY, "NativeAbilityWrapper is null"); @@ -145,7 +146,7 @@ void AbilityNativeThread::PostAbility(const NativeAbilityWrapper* nativeAbilityW postAbilityFunc_(nativeAbilityWrapper); } -void AbilityNativeThread::DestroyAbility(const NativeAbilityWrapper* nativeAbilityWrapper) +void AbilityNativeThread::DestroyAbility(const AbilityRuntime_NativeAbilityWrapper* nativeAbilityWrapper) { if (destroyAbilityFunc_ == nullptr) { TAG_LOGE(AAFwkTag::ABILITY, "DestroyAbility function is null"); @@ -196,7 +197,7 @@ LIBHANDLE AbilityNativeThread::OpenNativeLibrary(const std::string& bundleModule auto libName = fileName; auto pos = fileName.find_last_of('/'); if (pos != std::string::npos) { - libName = fileName.substr(pos); + libName = fileName.substr(pos + 1); } nativeHandle = dlopen_ns(&ns, libName.c_str(), RTLD_LAZY); return nativeHandle; diff --git a/frameworks/native/appkit/ability_runtime/context/application_context.cpp b/frameworks/native/appkit/ability_runtime/context/application_context.cpp index 782ac2fab2..97f324da5f 100644 --- a/frameworks/native/appkit/ability_runtime/context/application_context.cpp +++ b/frameworks/native/appkit/ability_runtime/context/application_context.cpp @@ -16,9 +16,13 @@ #include "application_context.h" #include +#include +#include +#include "ability_manager_client.h" #include "ability_manager_errors.h" #include "ability_util.h" +#include "app_mgr_client.h" #include "app_image_observer_manager.h" #include "configuration_convertor.h" #include "exit_reason.h" @@ -935,6 +939,59 @@ int32_t ApplicationContext::RestartApp(const AAFwk::Want& want) return (contextImpl_ != nullptr) ? contextImpl_->RestartApp(want) : ERR_INVALID_VALUE; } +int32_t ApplicationContext::EnableDelayedProcessExit() +{ + auto appMgrClient = DelayedSingleton::GetInstance(); + if (appMgrClient == nullptr) { + return ERR_INVALID_VALUE; + } + auto ret = appMgrClient->EnableDelayedProcessExit(getpid(), true); + if (ret == ERR_OK) { + std::lock_guard lock(delayedProcessExitStateLock_); + delayedProcessExitEnabled_ = true; + } + return ret; +} + +int32_t ApplicationContext::DisableDelayedProcessExit() +{ + auto appMgrClient = DelayedSingleton::GetInstance(); + if (appMgrClient == nullptr) { + return ERR_INVALID_VALUE; + } + + auto ret = appMgrClient->EnableDelayedProcessExit(getpid(), false); + if (ret == ERR_OK) { + std::lock_guard lock(delayedProcessExitStateLock_); + delayedProcessExitEnabled_ = false; + } + return ret; +} + +int32_t ApplicationContext::StartSelfUIAbility(const AAFwk::Want &want) +{ + if (!IsDelayedProcessExitPending()) { + TAG_LOGE(AAFwkTag::APPKIT, "delayed process exit is not pending"); + return AAFwk::ERR_DELAYED_PROCESS_EXIT_NOT_PENDING; + } + auto abilityMgrClient = AAFwk::AbilityManagerClient::GetInstance(); + if (abilityMgrClient == nullptr) { + return ERR_INVALID_VALUE; + } + auto errCode = abilityMgrClient->StartSelfUIAbilityByAppContext(want); + if (errCode != ERR_OK) { + TAG_LOGE(AAFwkTag::APPKIT, "StartSelfUIAbility failed, errCode:%{public}d", errCode); + return errCode; + } + return ERR_OK; +} + +bool ApplicationContext::IsDelayedProcessExitPending() +{ + std::lock_guard lock(delayedProcessExitStateLock_); + return delayedProcessExitEnabled_; +} + std::string ApplicationContext::GetDistributedFilesDir() { return (contextImpl_ != nullptr) ? contextImpl_->GetDistributedFilesDir() : ""; @@ -1325,13 +1382,14 @@ std::shared_ptr ApplicationContext::GetNativeTh return abilityNativeThread_; } -void ApplicationContext::AddNativeAbility(const std::string &instanceId, std::shared_ptr wrapper) +void ApplicationContext::AddNativeAbility( + const std::string &instanceId, std::shared_ptr wrapper) { std::lock_guard lock(nativeMutex_); nativeAbilities_[instanceId] = wrapper; } -std::shared_ptr ApplicationContext::GetNativeAbility(const std::string &instanceId) +std::shared_ptr ApplicationContext::GetNativeAbility(const std::string &instanceId) { std::lock_guard lock(nativeMutex_); auto it = nativeAbilities_.find(instanceId); @@ -1347,7 +1405,8 @@ void ApplicationContext::RemoveNativeAbility(const std::string &instanceId) nativeAbilities_.erase(instanceId); } -void ApplicationContext::PostAbility(const std::string &instanceId, std::shared_ptr wrapper) +void ApplicationContext::PostAbility( + const std::string &instanceId, std::shared_ptr wrapper) { if (wrapper == nullptr) { TAG_LOGE(AAFwkTag::ABILITY, "NativeAbilityWrapper is null"); diff --git a/frameworks/native/appkit/ability_runtime/context/js_application_context_utils.cpp b/frameworks/native/appkit/ability_runtime/context/js_application_context_utils.cpp index 4c24d83fe7..c911e79b49 100644 --- a/frameworks/native/appkit/ability_runtime/context/js_application_context_utils.cpp +++ b/frameworks/native/appkit/ability_runtime/context/js_application_context_utils.cpp @@ -618,6 +618,116 @@ napi_value JsApplicationContextUtils::OnRestartApp(napi_env env, NapiCallbackInf return CreateJsUndefined(env); } +napi_value JsApplicationContextUtils::EnableDelayedProcessExit(napi_env env, napi_callback_info info) +{ + GET_NAPI_INFO_WITH_NAME_AND_CALL(env, info, JsApplicationContextUtils, + OnEnableDelayedProcessExit, APPLICATION_CONTEXT_NAME); +} + +napi_value JsApplicationContextUtils::OnEnableDelayedProcessExit(napi_env env, NapiCallbackInfo& info) +{ + if (info.argc != ARGC_ZERO) { + ThrowInvalidParamError(env, "No parameters are supported."); + return CreateJsUndefined(env); + } + auto innerErrCode = std::make_shared(ERR_OK); + NapiAsyncTask::ExecuteCallback execute = [applicationContext = applicationContext_, innerErrCode]() { + auto context = applicationContext.lock(); + if (!context) { + *innerErrCode = ERR_ABILITY_RUNTIME_EXTERNAL_CONTEXT_NOT_EXIST; + return; + } + *innerErrCode = context->EnableDelayedProcessExit(); + }; + NapiAsyncTask::CompleteCallback complete = [innerErrCode](napi_env env, NapiAsyncTask& task, int32_t status) { + HandleScope handleScope(env); + if (*innerErrCode == ERR_OK) { + task.ResolveWithNoError(env, CreateJsUndefined(env)); + return; + } + task.Reject(env, CreateJsErrorByNativeErr(env, *innerErrCode)); + }; + napi_value result = nullptr; + NapiAsyncTask::Schedule("JsApplicationContextUtils::OnEnableDelayedProcessExit", + env, CreateAsyncTaskWithLastParam(env, nullptr, std::move(execute), std::move(complete), &result)); + return result; +} + +napi_value JsApplicationContextUtils::DisableDelayedProcessExit(napi_env env, napi_callback_info info) +{ + GET_NAPI_INFO_WITH_NAME_AND_CALL(env, info, JsApplicationContextUtils, + OnDisableDelayedProcessExit, APPLICATION_CONTEXT_NAME); +} + +napi_value JsApplicationContextUtils::OnDisableDelayedProcessExit(napi_env env, NapiCallbackInfo& info) +{ + if (info.argc != ARGC_ZERO) { + ThrowInvalidParamError(env, "No parameters are supported."); + return CreateJsUndefined(env); + } + auto innerErrCode = std::make_shared(ERR_OK); + NapiAsyncTask::ExecuteCallback execute = [applicationContext = applicationContext_, innerErrCode]() { + auto context = applicationContext.lock(); + if (!context) { + *innerErrCode = ERR_ABILITY_RUNTIME_EXTERNAL_CONTEXT_NOT_EXIST; + return; + } + *innerErrCode = context->DisableDelayedProcessExit(); + }; + NapiAsyncTask::CompleteCallback complete = [innerErrCode](napi_env env, NapiAsyncTask& task, int32_t status) { + HandleScope handleScope(env); + if (*innerErrCode == ERR_OK) { + task.ResolveWithNoError(env, CreateJsUndefined(env)); + return; + } + task.Reject(env, CreateJsErrorByNativeErr(env, *innerErrCode)); + }; + napi_value result = nullptr; + NapiAsyncTask::Schedule("JsApplicationContextUtils::OnDisableDelayedProcessExit", + env, CreateAsyncTaskWithLastParam(env, nullptr, std::move(execute), std::move(complete), &result)); + return result; +} + +napi_value JsApplicationContextUtils::StartSelfUIAbility(napi_env env, napi_callback_info info) +{ + GET_NAPI_INFO_WITH_NAME_AND_CALL(env, info, JsApplicationContextUtils, + OnStartSelfUIAbility, APPLICATION_CONTEXT_NAME); +} + +napi_value JsApplicationContextUtils::OnStartSelfUIAbility(napi_env env, NapiCallbackInfo& info) +{ + if (info.argc != ARGC_ONE) { + ThrowInvalidParamError(env, "Exactly one want parameter is required."); + return CreateJsUndefined(env); + } + AAFwk::Want want; + if (!AppExecFwk::UnwrapWant(env, info.argv[INDEX_ZERO], want)) { + ThrowInvalidParamError(env, "Parse param want failed, want must be Want."); + return CreateJsUndefined(env); + } + auto innerErrCode = std::make_shared(ERR_OK); + NapiAsyncTask::ExecuteCallback execute = [applicationContext = applicationContext_, want, innerErrCode]() { + auto context = applicationContext.lock(); + if (!context) { + *innerErrCode = ERR_ABILITY_RUNTIME_EXTERNAL_CONTEXT_NOT_EXIST; + return; + } + *innerErrCode = context->StartSelfUIAbility(want); + }; + NapiAsyncTask::CompleteCallback complete = [innerErrCode](napi_env env, NapiAsyncTask& task, int32_t status) { + HandleScope handleScope(env); + if (*innerErrCode == ERR_OK) { + task.ResolveWithNoError(env, CreateJsUndefined(env)); + return; + } + task.Reject(env, CreateJsErrorByNativeErr(env, *innerErrCode)); + }; + napi_value result = nullptr; + NapiAsyncTask::Schedule("JsApplicationContextUtils::OnStartSelfUIAbility", + env, CreateAsyncTaskWithLastParam(env, nullptr, std::move(execute), std::move(complete), &result)); + return result; +} + napi_value JsApplicationContextUtils::GetBundleCodeDir(napi_env env, napi_callback_info info) { TAG_LOGD(AAFwkTag::APPKIT, "called"); @@ -2178,6 +2288,12 @@ void JsApplicationContextUtils::BindNativeApplicationContextTwo(napi_env env, na BindNativeFunction(env, object, "getAllWindowStages", MD_NAME, JsApplicationContextUtils::GetAllWindowStages); BindNativeFunction(env, object, "getUIAbilityByInstanceId", MD_NAME, JsApplicationContextUtils::GetUIAbilityByInstanceId); + BindNativeFunction(env, object, "enableDelayedProcessExit", MD_NAME, + JsApplicationContextUtils::EnableDelayedProcessExit); + BindNativeFunction(env, object, "disableDelayedProcessExit", MD_NAME, + JsApplicationContextUtils::DisableDelayedProcessExit); + BindNativeFunction(env, object, "startSelfUIAbility", MD_NAME, + JsApplicationContextUtils::StartSelfUIAbility); } } // namespace AbilityRuntime } // namespace OHOS diff --git a/frameworks/native/appkit/app/child_main_thread.cpp b/frameworks/native/appkit/app/child_main_thread.cpp index 6c07c55cb5..cd30db6ed2 100644 --- a/frameworks/native/appkit/app/child_main_thread.cpp +++ b/frameworks/native/appkit/app/child_main_thread.cpp @@ -27,6 +27,7 @@ #include "load_ability_callback_manager.h" #include "js_runtime.h" #include "native_lib_util.h" +#include "native_runtime.h" #include "sys_mgr_client.h" #include "system_ability_definition.h" #include "parameters.h" @@ -299,9 +300,16 @@ void ChildMainThread::HandleLoadNative() return; } ChildProcessManager &childProcessMgr = ChildProcessManager::GetInstance(); + AbilityRuntime::Runtime::DebugOption debugOption; + childProcessMgr.SetAppSpawnForkDebugOption(debugOption, processInfo_); + TAG_LOGD(AAFwkTag::APPKIT, "StartDebugMode, isStartWithDebug is %{public}d, processName is %{public}s, " + "isDebugApp is %{public}d, isStartWithNative is %{public}d", processInfo_->isStartWithDebug, + processInfo_->processName.c_str(), processInfo_->isDebugApp, processInfo_->isStartWithNative); + AbilityRuntime::NativeRuntime::StartDebugMode(debugOption, processInfo_->bundleName); childProcessMgr.LoadNativeLibWithArgs(nativeLibModuleName_, processInfo_->srcEntry, processInfo_->entryFunc, processArgs_); TAG_LOGD(AAFwkTag::APPKIT, "HandleLoadNative end."); + AbilityRuntime::NativeRuntime::StopDebugMode(); ExitProcessSafely(); } @@ -404,7 +412,14 @@ void ChildMainThread::HandleRunNativeProc(const sptr &mainProcess } ChildProcessManager &childProcessMgr = ChildProcessManager::GetInstance(); + AbilityRuntime::Runtime::DebugOption debugOption; + childProcessMgr.SetAppSpawnForkDebugOption(debugOption, processInfo_); + TAG_LOGD(AAFwkTag::APPKIT, "StartDebugMode, isStartWithDebug is %{public}d, processName is %{public}s, " + "isDebugApp is %{public}d, isStartWithNative is %{public}d", processInfo_->isStartWithDebug, + processInfo_->processName.c_str(), processInfo_->isDebugApp, processInfo_->isStartWithNative); + AbilityRuntime::NativeRuntime::StartDebugMode(debugOption, processInfo_->bundleName); childProcessMgr.LoadNativeLib(nativeLibModuleName_, processInfo_->srcEntry, mainProcessCb); + AbilityRuntime::NativeRuntime::StopDebugMode(); ExitProcessSafely(); } diff --git a/frameworks/native/appkit/app/dump_runtime_helper.cpp b/frameworks/native/appkit/app/dump_runtime_helper.cpp index 48c8bf9af2..cb5ef3c7c5 100644 --- a/frameworks/native/appkit/app/dump_runtime_helper.cpp +++ b/frameworks/native/appkit/app/dump_runtime_helper.cpp @@ -35,6 +35,9 @@ #ifdef CJ_FRONTEND #include "cj_runtime.h" #endif +#if defined(NWEB) +#include "nweb_helper.h" +#endif namespace OHOS { namespace AppExecFwk { @@ -69,6 +72,8 @@ static constexpr uint32_t BUF_SIZE_256 = 256; static constexpr int DECIMAL_BASE = 10; static constexpr int KB_PER_MB = 1024; static constexpr size_t MEM_LEAK_MAX_SIZE = 100; +static constexpr int JSVM_SNAPSHOT = 0; +static constexpr int JSVM_RAW = 1; enum { INDEX_DELIVERY_TS = 0, @@ -349,6 +354,12 @@ void DumpRuntimeHelper::DumpMem(const OHOS::AppExecFwk::MemDumpInfo &info, std:: if (info.dumpType == MemDumpType::KMP_KOTLIN) { DumpKmpKotlinHeap(info); } + if (info.dumpType == MemDumpType::JSVM) { + DumpJsvmHeap(info); + } + if (info.dumpType == MemDumpType::ARKWEB_JS) { + DumpArkwebJsHeap(info); + } } void DumpRuntimeHelper::DumpNativeHeap(const OHOS::AppExecFwk::MemDumpInfo &info, std::string &dumpResult) @@ -453,6 +464,55 @@ void DumpRuntimeHelper::DumpKmpKotlinHeap(const OHOS::AppExecFwk::MemDumpInfo &i close(fd); } +void DumpRuntimeHelper::DumpJsvmHeap(const OHOS::AppExecFwk::MemDumpInfo &info) +{ + TAG_LOGI(AAFwkTag::APPKIT, "dump jsvm heaps, tid:%{public}d, needRaw:%{public}d", info.tid, info.needRaw); + void* jsvmHandle = dlopen("libjsvm.so", RTLD_LAZY); + if (jsvmHandle == nullptr) { + TAG_LOGE(AAFwkTag::APPKIT, "jsvm dlopen failed"); + return; + } + using jsvmFunc = int (*)(uint32_t, int); + auto jsvmDump = reinterpret_cast(dlsym(jsvmHandle, "jsvm_dump_heapsnapshot")); + if (jsvmDump == nullptr) { + TAG_LOGE(AAFwkTag::APPKIT, "jsvm dlsym failed"); + dlclose(jsvmHandle); + return; + } + int dumpType = info.needRaw ? JSVM_RAW : JSVM_SNAPSHOT; + int ret = jsvmDump(info.tid, dumpType); + if (ret != 0) { + TAG_LOGE(AAFwkTag::APPKIT, "jsvm dump failed"); + dlclose(jsvmHandle); + return; + } + dlclose(jsvmHandle); +} + +void DumpRuntimeHelper::DumpArkwebJsHeap(const OHOS::AppExecFwk::MemDumpInfo &info) +{ +#if defined(NWEB) + TAG_LOGI(AAFwkTag::APPKIT, "dump arkwebjs heaps, renderPid:%{public}u, needDump:%{public}d, " + "needGc:%{public}d, needRaw:%{public}d", info.renderPid, info.needDump, info.needGc, info.needRaw); + int32_t fd = -1; + if (info.needDump) { + struct FaultLoggerdRequest request{}; + request.type = info.needRaw ? static_cast(FaultLoggerType::ARKWEB_JS_RAW_SNAPSHOT) : + static_cast(FaultLoggerType::ARKWEB_JS_HEAP_SNAPSHOT); + request.pid = info.pid; + request.tid = info.renderPid; + request.time = GetCurrentTimestamp(); + fd = RequestFileDescriptorEx(&request); + } + + OHOS::NWeb::NWebHelper &nWebHelper = OHOS::NWeb::NWebHelper::Instance(); + nWebHelper.DumpArkWebJSHeap(fd, info.renderPid, info.needDump, info.needGc, info.needRaw); + if (fd > 0) { + close(fd); + } +#endif +} + void DumpRuntimeHelper::GetCheckList(const std::unique_ptr &runtime, std::string &checkList) { if (runtime->GetLanguage() != AbilityRuntime::Runtime::Language::JS) { diff --git a/frameworks/native/appkit/app/main_thread.cpp b/frameworks/native/appkit/app/main_thread.cpp index 5f96551921..54f25caa77 100644 --- a/frameworks/native/appkit/app/main_thread.cpp +++ b/frameworks/native/appkit/app/main_thread.cpp @@ -96,6 +96,7 @@ #include "parameters.h" #include "resource_manager.h" #include "runtime.h" +#include "startup_util.h" #include "sys_mgr_client.h" #include "system_ability_definition.h" #include "task_handler_client.h" @@ -1858,6 +1859,7 @@ void MainThread::HandleLaunchApplication(const AppLaunchData &appLaunchData, con options.isErrorInfoEnhance = appLaunchData.GetErrorInfoEnhance(); } options.jitEnabled = appLaunchData.IsJITEnabled(); + options.isMainProcess = appLaunchData.GetMainProcess(); #ifdef SUPPORT_CHILD_PROCESS AbilityRuntime::ChildProcessManager::GetInstance().SetForkProcessJITEnabled(appLaunchData.IsJITEnabled()); TAG_LOGD(AAFwkTag::APPKIT, "isStartWithDebug:%{public}d, debug:%{public}d, isNativeStart:%{public}d", @@ -1865,10 +1867,6 @@ void MainThread::HandleLaunchApplication(const AppLaunchData &appLaunchData, con AbilityRuntime::ChildProcessManager::GetInstance().SetForkProcessDebugOption(appInfo.bundleName, appLaunchData.GetDebugApp(), appInfo.debug, appLaunchData.isNativeStart()); AbilityRuntime::ChildProcessManager::GetInstance().SetApplication(application_); - AbilityRuntime::ChildProcessManager::GetInstance().SetArkChildProcessSupported( - appLaunchData.IsArkChildProcessSupported()); - AbilityRuntime::ChildProcessManager::GetInstance().SetNativeChildProcessSupported( - appLaunchData.IsNativeChildProcessSupported()); #endif // SUPPORT_CHILD_PROCESS if (!pluginBundleInfos.empty()) { for (auto &pluginBundleInfo : pluginBundleInfos) { @@ -2142,7 +2140,7 @@ void MainThread::HandleLaunchApplication(const AppLaunchData &appLaunchData, con if (!IsEtsAPP(appInfo) && (appLaunchData.IsNeedPreloadModule() || appLaunchData.GetAppPreloadMode() == AppExecFwk::PreloadMode::PRELOAD_MODULE)) { - PreloadModule(entryHapModuleInfo, application_->GetRuntime()); + PreloadModule(bundleInfo, appLaunchData, entryHapModuleInfo, application_->GetRuntime()); if (appMgr_ == nullptr) { TAG_LOGE(AAFwkTag::APPKIT, "null appMgr"); return; @@ -2350,22 +2348,46 @@ void MainThread::ProcessMainAbility(const AbilityInfo &info, const std::unique_p runtime->PreloadMainAbility(moduleName, srcPath, info.hapPath, isEsmode, info.srcEntrance); } -void MainThread::PreloadModule(const AppExecFwk::HapModuleInfo &entryHapModuleInfo, - const std::unique_ptr &runtime) +void MainThread::PreloadModule(const BundleInfo &bundleInfo, const AppLaunchData &appLaunchData, + const AppExecFwk::HapModuleInfo &entryHapModuleInfo, const std::unique_ptr &runtime) { - TAG_LOGI(AAFwkTag::APPKIT, "preload module %{public}s", entryHapModuleInfo.moduleName.c_str()); + HITRACE_METER_NAME(HITRACE_TAG_APP, __PRETTY_FUNCTION__); + const AppExecFwk::HapModuleInfo* preloadModuleInfo = nullptr; + for (const auto& hapModuleInfo: bundleInfo.hapModuleInfos) { + if (hapModuleInfo.moduleName == appLaunchData.GetPreloadModuleName()) { + preloadModuleInfo = &hapModuleInfo; + break; + } + } + if (preloadModuleInfo == nullptr) { + preloadModuleInfo = &entryHapModuleInfo; + } + std::string preloadAbilityName = appLaunchData.GetPreloadAbilityName(); + if (preloadAbilityName.empty()) { + preloadAbilityName = preloadModuleInfo->mainAbility; + } + TAG_LOGI(AAFwkTag::APPKIT, "preload module %{public}s", preloadModuleInfo->moduleName.c_str()); auto callback = []() {}; bool isAsyncCallback = false; - application_->AddAbilityStage(entryHapModuleInfo, callback, isAsyncCallback); + application_->AddAbilityStage(*preloadModuleInfo, callback, isAsyncCallback); if (isAsyncCallback) { return; } - for (const auto &info : entryHapModuleInfo.abilityInfos) { - if (info.name == entryHapModuleInfo.mainAbility) { + for (const auto& info : preloadModuleInfo->abilityInfos) { + if (info.name == preloadAbilityName) { ProcessMainAbility(info, runtime); return; } } + for (auto& extensionInfo : preloadModuleInfo->extensionInfos) { + if (extensionInfo.name == preloadAbilityName) { + AbilityInfo abilityInfo; + AbilityRuntime::StartupUtil::InitAbilityInfoFromExtension( + const_cast(extensionInfo), abilityInfo); + ProcessMainAbility(abilityInfo, runtime); + return; + } + } } #ifdef ABILITY_LIBRARY_LOADER @@ -3180,7 +3202,12 @@ void MainThread::Init(const std::shared_ptr &runner) HITRACE_METER_NAME(HITRACE_TAG_APP, __PRETTY_FUNCTION__); TAG_LOGD(AAFwkTag::APPKIT, "Start"); mainHandler_ = std::make_shared(runner, this); - watchdog_ = std::make_shared(); + bool isRescueMode = (system::GetParameter("soc.boot.mode", "") == "rescue"); + if (!isRescueMode) { + TAG_LOGE(AAFwkTag::APPKIT, "is not in rescue mode"); + watchdog_ = std::make_shared(); + } + extensionConfigMgr_ = std::make_shared(); wptr weak = this; auto task = [weak]() { @@ -3196,7 +3223,11 @@ void MainThread::Init(const std::shared_ptr &runner) } TaskTimeoutDetected(runner); - watchdog_->Init(mainHandler_); + if (!isRescueMode) { + TAG_LOGE(AAFwkTag::APPKIT, "is not in rescue mode"); + watchdog_->Init(mainHandler_); + } + AppExecFwk::AppfreezeInner::GetInstance()->SetMainHandler(mainHandler_); } diff --git a/frameworks/native/appkit/app_startup/ext_native_startup_manager.cpp b/frameworks/native/appkit/app_startup/ext_native_startup_manager.cpp index 1ea2fe0070..096ce57785 100644 --- a/frameworks/native/appkit/app_startup/ext_native_startup_manager.cpp +++ b/frameworks/native/appkit/app_startup/ext_native_startup_manager.cpp @@ -230,6 +230,21 @@ int32_t ExtNativeStartupManager::RegisterExtStartupTask( } std::lock_guard guard(mutex_); extNativeStartupTasks_[phase].push_back(extNativeStartupTask); + if (phaseFlag_ >= phase) { + TAG_LOGD(AAFwkTag::STARTUP, "delayed run task:%{public}s", extNativeStartupTask->GetName().c_str()); + std::map> nativeStartupTasks; + std::shared_ptr startupTask; + int32_t res = BuildExtStartupTask(extNativeStartupTask, startupTask); + if (res != ERR_OK || startupTask == nullptr) { + TAG_LOGE(AAFwkTag::STARTUP, "failed to build task: %{public}d", res); + return ERR_STARTUP_INTERNAL_ERROR; + } + nativeStartupTasks.emplace(startupTask->GetName(), startupTask); + auto runTaskInitCallback = [nativeStartupTasks]() { + RunNativeStartupTask(nativeStartupTasks); + }; + ffrt::submit(runTaskInitCallback); + } return ERR_OK; } @@ -237,6 +252,7 @@ int32_t ExtNativeStartupManager::RunPhaseTasks(const SchedulerPhase phase) { TAG_LOGD(AAFwkTag::STARTUP, "call"); std::lock_guard guard(mutex_); + phaseFlag_ = phase; auto findRes = extNativeStartupTasks_.find(phase); if (findRes == extNativeStartupTasks_.end()) { TAG_LOGD(AAFwkTag::STARTUP, "no phase task"); diff --git a/frameworks/native/appkit/dfr/appfreeze_inner.cpp b/frameworks/native/appkit/dfr/appfreeze_inner.cpp index 05a794fead..7337763172 100644 --- a/frameworks/native/appkit/dfr/appfreeze_inner.cpp +++ b/frameworks/native/appkit/dfr/appfreeze_inner.cpp @@ -59,6 +59,8 @@ constexpr int APP_INPUT_BLOCK_TYPE = 4; constexpr int BUSSINESS_THREAD_BLOCK_3S_TYPE = 5; constexpr int BUSSINESS_THREAD_BLOCK_6S_TYPE = 6; constexpr int BUSINESS_INPUT_BLOCK_TYPE = 7; +constexpr int DUMP_MAIN_STACK_TIMEOUT = 1; // s +constexpr int LAST_SAVE_MAIN_STACK_TIME = 3000; // ms } std::weak_ptr AppfreezeInner::appMainHandler_; std::shared_ptr AppfreezeInner::instance_ = nullptr; @@ -287,6 +289,46 @@ int AppfreezeInner::TransformHicollieFaultNumber(const std::string& faultName) return -1; } +std::string AppfreezeInner::GetMainStackDump(int32_t pid) +{ + int64_t now = std::chrono::duration_cast(std::chrono:: + system_clock::now().time_since_epoch()).count(); + if (!lastMainStack_.empty() && now - lastMainStackTime_ < LAST_SAVE_MAIN_STACK_TIME) { + { + std::lock_guard lock(mainStackMutex_); + return lastMainStack_; + } + } + auto task = [pid, this]() { + std::string startTime = "\nDump main thread stack start time: " + + AbilityRuntime::TimeUtil::DefaultCurrentTimeStr() + "\n"; + std::string mainStack; + if (HiviewDFX::GetBacktraceStringByTidWithMix(mainStack, pid, 0, true)) { + mainStack = startTime + mainStack + "\nDump main thread stack end time: " + + AbilityRuntime::TimeUtil::DefaultCurrentTimeStr() + "\n"; + } else { + TAG_LOGE(AAFwkTag::APPDFR, "get main stack failed, mainStack=%{public}s", mainStack.c_str()); + } + lastMainStackTime_ = std::chrono::duration_cast(std::chrono:: + system_clock::now().time_since_epoch()).count(); + std::unique_lock lock(mainStackMutex_); + lastMainStack_ = mainStack; + mainStackCv_.notify_one(); + }; + ffrt::submit_h(task); + + { + std::unique_lock lock(mainStackMutex_); + if (mainStackCv_.wait_for(lock, std::chrono::seconds(DUMP_MAIN_STACK_TIMEOUT)) == std::cv_status::timeout) { + TAG_LOGW(AAFwkTag::APPDFR, "get main stack has been extecting more than 1s"); + } else { + TAG_LOGI(AAFwkTag::APPDFR, "get main stack has finished less than 1s"); + return lastMainStack_; + } + } + return ""; +} + void AppfreezeInner::ChangeFaultDateInfo(FaultData& faultData, const std::string& msgContent) { faultData.errorObject.message += msgContent; @@ -307,13 +349,7 @@ void AppfreezeInner::ChangeFaultDateInfo(FaultData& faultData, const std::string } int32_t pid = IPCSkeleton::GetCallingPid(); int32_t uid = IPCSkeleton::GetCallingUid(); - std::string mainStack = ""; - std::string startTime = "\nDump main thread stack start time: " + - AbilityRuntime::TimeUtil::DefaultCurrentTimeStr() + "\n"; - if (HiviewDFX::GetBacktraceStringByTidWithMix(mainStack, pid, 0, true)) { - faultData.errorObject.mainStack = startTime + mainStack + "\nDump main thread stack end time: " + - AbilityRuntime::TimeUtil::DefaultCurrentTimeStr() + "\n"; - } + faultData.errorObject.mainStack = GetMainStackDump(pid); bool isExit = IsExitApp(faultData.errorObject.name) && faultData.needKillProcess; if (isExit) { faultData.forceExit = true; @@ -325,7 +361,7 @@ void AppfreezeInner::ChangeFaultDateInfo(FaultData& faultData, const std::string exitReason.killMsg = reason; exitReason.innerMsg = reason; auto result = AbilityManagerClient::GetInstance()->RecordAppWithReason(pid, uid, exitReason); - TAG_LOGI(AAFwkTag::ABILITYMGR, "Record result=%{public}d, pid=%{public}d, uid=%{public}d, " + TAG_LOGI(AAFwkTag::APPDFR, "Record result=%{public}d, pid=%{public}d, uid=%{public}d, " "killId=%{public}d", result, pid, uid, exitReason.killId); } NotifyANR(faultData); @@ -381,7 +417,7 @@ void AppfreezeInner::EnableFreezeSample(FaultData& newFaultData) std::string eventName = newFaultData.errorObject.name; newFaultData.isInForeground = GetAppInForeground(); if (eventName == AppFreezeType::THREAD_BLOCK_3S || eventName == AppFreezeType::LIFECYCLE_HALF_TIMEOUT) { - OHOS::HiviewDFX::Watchdog::GetInstance().StartSample(HALF_DURATION, HALF_INTERVAL); + newFaultData.appfreezeInfo = OHOS::HiviewDFX::Watchdog::GetInstance().StartSample(HALF_DURATION, HALF_INTERVAL); TAG_LOGI(AAFwkTag::APPDFR, "start to sample freeze stack, eventName:%{public}s", eventName.c_str()); return; } diff --git a/frameworks/native/child_process/BUILD.gn b/frameworks/native/child_process/BUILD.gn index 967f685c81..b67ab11958 100644 --- a/frameworks/native/child_process/BUILD.gn +++ b/frameworks/native/child_process/BUILD.gn @@ -73,4 +73,5 @@ ohos_shared_library("child_process") { install_images = [ "system" ] subsystem_name = "ability" part_name = "ability_runtime" + version_script = "child_process.version_script" } diff --git a/frameworks/native/child_process/child_process.version_script b/frameworks/native/child_process/child_process.version_script new file mode 100644 index 0000000000..f965f99f41 --- /dev/null +++ b/frameworks/native/child_process/child_process.version_script @@ -0,0 +1,19 @@ +# 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. + +{ + global: + *OH_*; + local: + "*"; +}; \ No newline at end of file diff --git a/frameworks/native/runtime/ets_runtime.cpp b/frameworks/native/runtime/ets_runtime.cpp index b40fb9e270..24b35fcba9 100644 --- a/frameworks/native/runtime/ets_runtime.cpp +++ b/frameworks/native/runtime/ets_runtime.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 Huawei Device Co., Ltd. + * Copyright (c) 2025-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 @@ -67,6 +67,7 @@ constexpr char SANDBOX_SHARED_BUNDLE_ARK_CACHE_PATH[] = "/data/service/el1/public/for-all-app/shared_bundles_ark_cache/"; constexpr char MERGE_ABC_PATH[] = "/ets/modules_static.abc"; const std::string SYS_HSP_FILE_PATH_PREFIX = "/system/app/"; +const std::string PREINSTALL_HOST_AOT_ARK_CACHE_PATH = "/system/app/ark_cache/"; const std::string ARK_CACHE_NATIVE_PATH = "arm64/"; const char *ETS_ENV_LIBNAME = "libets_environment.z.so"; @@ -129,6 +130,30 @@ private: AppLibPathMap appLibPathMap_; }; std::shared_ptr g_etsAppLibNamespaceMgr; + +bool IsFileExists(const std::string &path) +{ + std::error_code errorCode; + bool isRegularFile = std::filesystem::is_regular_file(path, errorCode); + if (errorCode) { + const std::string errorMessage = errorCode.message(); + TAG_LOGW(AAFwkTag::ETSRUNTIME, + "check file exists failed, path: %{public}s, errorCode: %{public}d, " + "category: %{public}s, message: %{public}s", + path.c_str(), errorCode.value(), errorCode.category().name(), errorMessage.c_str()); + } + return isRegularFile; +} + +std::string GetPreinstallHostAotAnPath(const std::string &bundleName, const std::string &moduleName) +{ + if (bundleName.empty() || moduleName.empty()) { + return ""; + } + return PREINSTALL_HOST_AOT_ARK_CACHE_PATH + bundleName + + std::string(AbilityBase::Constants::FILE_SEPARATOR) + moduleName + ".an"; +} + } // namespace std::unique_ptr ETSRuntime::PreFork(const Options &options, @@ -176,6 +201,15 @@ std::string ETSRuntime::GetAotPath(const Options &options) for (const auto& status: options.aotCompileStatusMap) { if (IsAotCompiledSuccess(status.second)) { aotFiles.push_back(SANDBOX_ARK_CACHE_PATH + ARK_CACHE_NATIVE_PATH + status.first + ".an"); + } else { + std::string anPath = GetPreinstallHostAotAnPath(options.bundleName, status.first); + if (!IsFileExists(anPath)) { + TAG_LOGW(AAFwkTag::ETSRUNTIME, "preinstall host AOT file is unavailable, skip loading: %{public}s", + anPath.c_str()); + continue; + } + TAG_LOGI(AAFwkTag::ETSRUNTIME, "load preinstall host aot an: %{public}s", anPath.c_str()); + aotFiles.push_back(anPath); } } @@ -466,6 +500,20 @@ void ETSRuntime::XGC() TAG_LOGE(AAFwkTag::APPKIT, "Class_CallStaticMethod_Long failed, status: %{public}d", status); return; } + ani_namespace imageNameSpace; + if ((status = env->FindNamespace("std.core.GC", &imageNameSpace)) != ANI_OK) { + TAG_LOGE(AAFwkTag::APPKIT, "FindNamespace failed, status: %{public}d", status); + return; + } + ani_function gcFunc {}; + if ((status = env->Namespace_FindFunction(imageNameSpace, "waitForFinishGC", "l:", &gcFunc)) != ANI_OK) { + TAG_LOGE(AAFwkTag::APPKIT, "Namespace_FindFunction failed, status: %{public}d", status); + return; + } + if ((status = env->Function_Call_Void(gcFunc, longnum)) != ANI_OK) { + TAG_LOGE(AAFwkTag::APPKIT, "Function_Call_Void failed, status: %{public}d", status); + return; + } } void ETSRuntime::FinishPreload() diff --git a/frameworks/native/runtime/js_runtime.cpp b/frameworks/native/runtime/js_runtime.cpp index 41ea51ca92..e5c72a9360 100644 --- a/frameworks/native/runtime/js_runtime.cpp +++ b/frameworks/native/runtime/js_runtime.cpp @@ -91,6 +91,7 @@ constexpr char MERGE_ABC_PATH[] = "/ets/modules.abc"; constexpr char BUNDLE_INSTALL_PATH[] = "/data/storage/el1/bundle/"; constexpr const char* PERMISSION_RUN_ANY_CODE = "ohos.permission.RUN_ANY_CODE"; constexpr const char* PERMISSION_LOAD_INDEPENDENT_LIBRARY = "ohos.permission.kernel.LOAD_INDEPENDENT_LIBRARY"; +constexpr const char* PERMISSION_LOAD_CERTSIGN_LIBRARY = "ohos.permission.kernel.LOAD_CERTSIGN_LIBRARY_FOR_WEB"; const std::string CONFIG_PATH = "/etc/system_kits_config.json"; const std::string SYSTEM_KITS_CONFIG_PATH = "/system/etc/system_kits_config.json"; @@ -764,6 +765,7 @@ bool JsRuntime::Initialize(const Options& options) LoadAotFile(options); panda::JSNApi::SetBundle(vm, options.isBundle); panda::JSNApi::SetBundleName(vm, options.bundleName); + panda::JSNApi::SetIsMainProcess(options.isMainProcess); panda::JSNApi::SetHostResolveBufferTracker( vm, JsModuleReader(options.bundleName, options.hapPath, options.isUnique)); isModular = !panda::JSNApi::IsBundle(vm); @@ -977,7 +979,10 @@ void JsRuntime::CreatePluginDefaultNamespace(const std::string &lddictionaries) Security::AccessToken::AccessTokenID selfToken = IPCSkeleton::GetSelfTokenID(); int result = Security::AccessToken::AccessTokenKit::VerifyAccessToken(selfToken, PERMISSION_LOAD_INDEPENDENT_LIBRARY); - if (result != Security::AccessToken::PermissionState::PERMISSION_GRANTED) { + int resultWeb = Security::AccessToken::AccessTokenKit::VerifyAccessToken(selfToken, + PERMISSION_LOAD_CERTSIGN_LIBRARY); + if (result != Security::AccessToken::PermissionState::PERMISSION_GRANTED && + resultWeb != Security::AccessToken::PermissionState::PERMISSION_GRANTED) { TAG_LOGE(AAFwkTag::JSRUNTIME, "verify access token failed: %{public}d", result); return; } @@ -1534,6 +1539,7 @@ bool JsRuntime::PopPreloadObj(const std::string& key, std::unique_ptr + #include "hilog_tag_wrapper.h" #include "module_manager/native_module_manager.h" +#include "connect_server_manager.h" +#include "hdc_register.h" +#include "parameters.h" +#include "constants.h" +#include "runtime.h" +#include "bundle_constants.h" namespace OHOS { namespace AbilityRuntime { @@ -24,6 +32,17 @@ const std::string DEFAULT_NAMESPACE = "default"; const char *OH_ABILITY_RUNTIME_ON_NATIVE_EXTENSION_CREATE = "OH_AbilityRuntime_OnNativeExtensionCreate"; using CreateFuncType = void(*)(AbilityRuntime_ExtensionInstanceHandle, const char*); +void DebuggerConnectionHandler(const std::string &bundleName) +{ + int32_t instanceId = static_cast(getproctid()); + int32_t tid = instanceId; + if (!ConnectServerManager::Get().StoreInstanceMessage(tid, instanceId, bundleName)) { + ConnectServerManager::Get().RemoveInstance(instanceId); + ConnectServerManager::Get().StoreInstanceMessage(tid, instanceId, bundleName); + } + ConnectServerManager::Get().SendInstanceMessage(tid, instanceId, bundleName); +} + bool NativeRuntime::LoadModule(const std::string& bundleModuleName, const std::string& fileName, const std::string& abilityName, AbilityRuntime_ExtensionInstance& instance) { @@ -61,5 +80,69 @@ bool NativeRuntime::LoadModule(const std::string& bundleModuleName, const std::s func(handle, abilityName.c_str()); return true; } + +void NativeRuntime::StartDebugMode(const Runtime::DebugOption &dOption, const std::string &bundleName) +{ + TAG_LOGD(AAFwkTag::JSRUNTIME, "localDebug %{public}d, isDebugApp %{public}d, bundleName %{public}s", + dOption.isDebugFromLocal, dOption.isDebugApp, bundleName.c_str()); + if (!dOption.isDebugFromLocal && !dOption.isDeveloperMode) { + TAG_LOGE(AAFwkTag::JSRUNTIME, "developer Mode false"); + return; + } + + bool isStartWithDebug = dOption.isStartWithDebug; + TAG_LOGD(AAFwkTag::JSRUNTIME, "Native is starting debug mode [%{public}s]", isStartWithDebug ? "break" : "normal"); + bool isDebugApp = dOption.isDebugApp; + std::string appProvisionType = dOption.appProvisionType; + std::string inputProcessName = bundleName != dOption.processName ? dOption.processName : ""; + HdcRegister::DebugRegisterMode debugMode = HdcRegister::DebugRegisterMode::HDC_DEBUG_REG; + if (dOption.isDebugFromLocal && dOption.isDeveloperMode) { + debugMode = HdcRegister::DebugRegisterMode::BOTH_REG; + } else if (dOption.isDebugFromLocal) { + debugMode = HdcRegister::DebugRegisterMode::LOCAL_DEBUG_REG; + } + + TAG_LOGD(AAFwkTag::JSRUNTIME, "inputProcessName %{public}s, debugMode:%{public}d, appProvisionType:%{public}s", + inputProcessName.c_str(), static_cast(debugMode), appProvisionType.c_str()); + HdcRegister::Get().StartHdcRegister(bundleName, inputProcessName, isDebugApp, debugMode, + [bundleName, isStartWithDebug, isDebugApp, appProvisionType](int socketFd, std::string option) { + TAG_LOGI(AAFwkTag::JSRUNTIME, + "HdcRegister msg, fd %{public}d, option %{public}s, isStartWithDebug %{public}d, isDebugApp %{public}d", + socketFd, option.c_str(), isStartWithDebug, isDebugApp); + // system is unlocked when const.boot.oemmode is rd + std::string oemmode = OHOS::system::GetParameter("const.boot.oemmode", ""); + bool unlocked = "rd" == oemmode; + TAG_LOGI(AAFwkTag::JSRUNTIME, "unlocked= %{public}d, oemmode= %{public}s", unlocked, oemmode.c_str()); + // Don't start any server if (system is locked) and app is release version + // Starting ConnectServer in release app on debuggable system is only for debug mode, not for profiling mode. + if ((!unlocked) && appProvisionType == AppExecFwk::Constants::APP_PROVISION_TYPE_RELEASE) { + TAG_LOGE(AAFwkTag::JSRUNTIME, "not support release app"); + return; + } + if (option.find(DEBUGGER) == std::string::npos) { + TAG_LOGD(AAFwkTag::JSRUNTIME, "stop old connect server"); + // if has old connect server, stop it + ConnectServerManager::Get().StopConnectServer(false); + ConnectServerManager::Get().SendDebuggerInfo(isStartWithDebug, isDebugApp); + ConnectServerManager::Get().StartConnectServer(bundleName, socketFd, false); + } else { + TAG_LOGE(AAFwkTag::JSRUNTIME, "debugger service unexpected option: %{public}s", option.c_str()); + } + }); + + if (isDebugApp && appProvisionType != AppExecFwk::Constants::APP_PROVISION_TYPE_RELEASE) { + TAG_LOGD(AAFwkTag::JSRUNTIME, "start connect server"); + ConnectServerManager::Get().StartConnectServer(bundleName, -1, true); + } + DebuggerConnectionHandler(bundleName); + TAG_LOGD(AAFwkTag::JSRUNTIME, "StartDebugMode end"); +} + +void NativeRuntime::StopDebugMode() +{ + int32_t instanceId = static_cast(getproctid()); + ConnectServerManager::Get().RemoveInstance(instanceId); + TAG_LOGD(AAFwkTag::JSRUNTIME, "StopDebugMode end, instanceId=%{public}d", instanceId); +} } // namespace AbilityRuntime } // namespace OHOS \ No newline at end of file diff --git a/interfaces/inner_api/ability_manager/BUILD.gn b/interfaces/inner_api/ability_manager/BUILD.gn index 4b46dd1251..95d2b7d943 100644 --- a/interfaces/inner_api/ability_manager/BUILD.gn +++ b/interfaces/inner_api/ability_manager/BUILD.gn @@ -27,6 +27,7 @@ config("ability_manager_public_config") { "include/", "include/aa_tools", "include/insight_intent", + "include/skill", "include/status_bar_delegate", "include/ui_extension", "${ability_runtime_path}/interfaces/kits/native/ability/native", @@ -89,6 +90,7 @@ ohos_shared_library("ability_manager") { "${ability_runtime_services_path}/abilitymgr/src/insight_intent/insight_intent_execute_result.cpp", "${ability_runtime_services_path}/abilitymgr/src/insight_intent/insight_intent_info_for_query.cpp", "${ability_runtime_services_path}/abilitymgr/src/insight_intent/insight_intent_query_entity_param.cpp", + "${ability_runtime_native_path}/ability/native/insight_intent_host_client.cpp", "${ability_runtime_services_path}/abilitymgr/src/intent_exemption_info.cpp", "${ability_runtime_services_path}/abilitymgr/src/keep_alive/keep_alive_info.cpp", "${ability_runtime_services_path}/abilitymgr/src/kiosk_status.cpp", @@ -107,6 +109,10 @@ ohos_shared_library("ability_manager") { "${ability_runtime_services_path}/abilitymgr/src/rule.cpp", "${ability_runtime_services_path}/abilitymgr/src/sa_interceptor_proxy.cpp", "${ability_runtime_services_path}/abilitymgr/src/sa_interceptor_stub.cpp", + "${ability_runtime_services_path}/abilitymgr/src/skill/skill_execute_callback_proxy.cpp", + "${ability_runtime_services_path}/abilitymgr/src/skill/skill_execute_callback_stub.cpp", + "${ability_runtime_services_path}/abilitymgr/src/skill/skill_execute_param.cpp", + "${ability_runtime_services_path}/abilitymgr/src/skill/skill_execute_result.cpp", "${ability_runtime_services_path}/abilitymgr/src/start_params_by_SCB.cpp", "${ability_runtime_services_path}/abilitymgr/src/start_specified_ability_params.cpp", "${ability_runtime_services_path}/abilitymgr/src/system_ability_token_callback_stub.cpp", diff --git a/interfaces/inner_api/ability_manager/include/ability_manager_client.h b/interfaces/inner_api/ability_manager/include/ability_manager_client.h index 190678220f..c25939492f 100644 --- a/interfaces/inner_api/ability_manager/include/ability_manager_client.h +++ b/interfaces/inner_api/ability_manager/include/ability_manager_client.h @@ -55,6 +55,14 @@ public: */ ErrCode StartSelfUIAbility(const Want &want); + /** + * StartSelfUIAbility from ApplicationContext and force launch in current process. + * + * @param want, the want of the ability to start. + * @return Returns ERR_OK on success, others on failure. + */ + ErrCode StartSelfUIAbilityByAppContext(const Want &want); + /** * StartSelfUIAbility with want and startOptions, start self uiability only on 2-in-1 devices. * @@ -1782,6 +1790,17 @@ public: */ ErrCode QueryEntityInfo(uint64_t key, sptr callerToken, const InsightIntentQueryParam ¶m); + + /** + * @brief Execute intent with result synchronously. + * @param callerToken Caller ability token. + * @param param The Intent execute param. + * @param result The Intent execute result output. + * @param timeoutMs Timeout in milliseconds, default 30000ms. + * @return Returns ERR_OK on success, others on failure. + */ + ErrCode ExecuteIntentWithResult(const InsightIntentExecuteParam ¶m, InsightIntentExecuteResult &result, + int32_t timeoutMs = 30000); /** * @brief Called when insight intent execute finished. @@ -2187,6 +2206,15 @@ public: */ ErrCode NotifyCompleteGamePreLaunch(const sptr callerToken); + /** + * Start self UIAbility in child process. + * @param want Ability want. + * @param specifiedFlag specified flag. + * @return Returns ERR_OK on success, others on failure. + */ + ErrCode StartSelfUIAbilityInChildProcess( + const Want &want, const std::string &specifiedFlag, sptr callerToken); + /** * Check if the app is restart-limited. * @return Returns true on being limited. @@ -2249,6 +2277,21 @@ public: */ int32_t SetAppRecoveryFlag(const sptr& token, int flag); + ErrCode ExecuteInAppSkill(const std::string &bundleName, const std::string &moduleName, + const std::string &skillName, const std::string &arkTSPath = "", + const std::string &funcName = "", + const std::shared_ptr &skillArgs = nullptr, + const sptr &callback = nullptr); + + ErrCode ExecuteInAppSkillWithTokenId(const AppExecFwk::SkillExecuteRequest &request, + const sptr &callback); + + ErrCode ExecuteSkillDone(sptr token, const std::string &requestCode, + int32_t resultCode, const AppExecFwk::SkillExecuteResult &result); + + ErrCode QuerySkillType(const std::string &bundleName, const std::string &moduleName, + const std::string &skillName, int32_t &skillType); + private: AbilityManagerClient(); DISALLOW_COPY_AND_MOVE(AbilityManagerClient); diff --git a/interfaces/inner_api/ability_manager/include/ability_manager_errors.h b/interfaces/inner_api/ability_manager/include/ability_manager_errors.h index 1a5417774c..c6300f2d8d 100644 --- a/interfaces/inner_api/ability_manager/include/ability_manager_errors.h +++ b/interfaces/inner_api/ability_manager/include/ability_manager_errors.h @@ -1144,6 +1144,27 @@ enum { ERR_INVALID_DISTRIBUTION_TYPE = 2099414, + // Result (2099420) for Cross-device execution intent connection failed. + ERR_INTENT_CONNECTION_FAILED = 2099420, + + // Result (2099421) for Device disconnected during cross-device intent execution. + ERR_INTENT_DEVICE_DISCONNECTED = 2099421, + + /** + * Result (2099414) for moe connection number per caller pid reached upper limit. + */ + ERR_MOE_CONNECTION_LIMIT = 2099415, + + /** + * Result (2099415) for moe instance number reached upper limit. + */ + ERR_MOE_INSTANCE_LIMIT = 2099416, + + /** + * Result (2099417) for IN_PROCESS mode does not allow cross-application connection. + */ + ERR_MOE_CROSS_APP_IN_PROCESS = 2099417, + /** * Native error(3000000) for target bundle not exist. */ @@ -1416,6 +1437,22 @@ enum NativeFreeInstallError { */ ERR_NOT_GAME_PRELOAD_STATE = 29360227, + /* codes 29360270 - 29360280 are reserved for StartSelfUIAbility by delayed process exit */ + /* + * Result (29360270) The current process has no UIAbility, cannot be called. + */ + ERR_DELAYED_PROCESS_EXIT_NO_UIABILITY = 29360270, + + /* + * Result (29360271) Delayed process exit is not pending in the current process, cannot be called. + */ + ERR_DELAYED_PROCESS_EXIT_NOT_PENDING = 29360271, + + /* + * Result (29360272) The current process still has another UIAbility. + */ + ERR_DELAYED_PROCESS_EXIT_HAS_OTHER_UIABILITY = 29360272, + /** * Undefine error code. */ diff --git a/interfaces/inner_api/ability_manager/include/ability_manager_interface.h b/interfaces/inner_api/ability_manager/include/ability_manager_interface.h index 5b987197d3..d45d1f46c6 100644 --- a/interfaces/inner_api/ability_manager/include/ability_manager_interface.h +++ b/interfaces/inner_api/ability_manager/include/ability_manager_interface.h @@ -41,6 +41,10 @@ #include "iacquire_share_data_callback_interface.h" #include "insight_intent/insight_intent_execute_param.h" #include "insight_intent/insight_intent_execute_result.h" +#include "skill/skill_execute_param.h" +#include "skill/skill_execute_result.h" +#include "skill/skill_execute_callback_interface.h" + #include "insight_intent/insight_intent_info_for_query.h" #include "insight_intent/insight_intent_query_param.h" #include "iprepare_terminate_callback_interface.h" @@ -93,6 +97,7 @@ using AutoStartupInfo = AbilityRuntime::AutoStartupInfo; using InsightIntentExecuteParam = AppExecFwk::InsightIntentExecuteParam; using InsightIntentExecuteResult = AppExecFwk::InsightIntentExecuteResult; using InsightIntentQueryParam = AppExecFwk::InsightIntentQueryParam; +using SkillExecuteParam = AppExecFwk::SkillExecuteParam; using UIExtensionAbilityConnectInfo = AbilityRuntime::UIExtensionAbilityConnectInfo; using UIExtensionHostInfo = AbilityRuntime::UIExtensionHostInfo; using UIExtensionSessionInfo = AbilityRuntime::UIExtensionSessionInfo; @@ -126,6 +131,17 @@ public: return 0; } + /** + * StartSelfUIAbility from ApplicationContext and force launch in current process. + * + * @param want, the want of the ability to start. + * @return Returns ERR_OK on success, others on failure. + */ + virtual int StartSelfUIAbilityByAppContext(const Want &want) + { + return 0; + } + /** * StartSelfUIAbility with want and startOptions, start self uiability only on 2-in-1 devices. * @@ -1221,7 +1237,7 @@ public: */ virtual int StartAbilityByCall(const Want &want, const sptr &connect, const sptr &callerToken, int32_t accountId = DEFAULT_INVAL_VALUE, bool isSilent = false, - bool promotePriority = false, bool isVisible = false) = 0; + bool promotePriority = false, bool isVisible = false, uint64_t specifiedFullTokenId = 0) = 0; /** * Start Ability, connect session with common ability. @@ -1237,8 +1253,9 @@ public: * @return Returns ERR_OK on success, others on failure. */ virtual int StartAbilityByCallWithErrMsg(const Want &want, const sptr &connect, - const sptr &callerToken, int32_t accountId, std::string &errMsg, bool isSilent = false, - bool promotePriority = false, bool isVisible = false) + const sptr &callerToken, int32_t accountId, std::string &errMsg, + bool isSilent = false, bool promotePriority = false, + bool isVisible = false, uint64_t specifiedFullTokenId = 0) { return 0; }; @@ -2700,6 +2717,19 @@ public: return ERR_OK; } + /** + * Start self UIAbility in child process. + * @param want Ability want. + * @param specifiedFlag specified flag. + * @param callerToken The caller ability token. + * @return Returns ERR_OK on success, others on failure. + */ + virtual ErrCode StartSelfUIAbilityInChildProcess( + const Want &want, const std::string &specifiedFlag, sptr callerToken) + { + return ERR_OK; + } + /** * Check if the app is restart-limited. * @return Returns true on being limited. @@ -2789,6 +2819,57 @@ public: { return 0; } + + /** + * @brief Start skill by HDC, launch target ability. + * @param bundleName The target bundle name. + * @param moduleName The target module name. + * @param skillName The skill name to execute. + * @param arkTSPath The target ArkTS file path. + * @param funcName The target function name. + * @param argv The arguments for skill execution. + * @return Returns ERR_OK on success, others on failure. + */ + virtual int32_t ExecuteInAppSkill(const std::string &bundleName, const std::string &moduleName, + const std::string &skillName, const std::string &arkTSPath = "", + const std::string &funcName = "", + const std::shared_ptr &skillArgs = nullptr, + const sptr &callback = nullptr) + { + return ERR_OK; + } + + /** + * @brief Execute in-app skill with explicit caller tokenId (for SA-to-SA calls). + * @param request The skill execute request parameters. + * @param callback The callback for skill execution result. + * @return Returns ERR_OK on success, others on failure. + */ + virtual int32_t ExecuteInAppSkillWithTokenId(const AppExecFwk::SkillExecuteRequest &request, + const sptr &callback) + { + return ERR_OK; + } + + /** + * @brief Query the type of a skill (independent or in-app). + * @param bundleName The bundle name of the target application. + * @param moduleName The module name of the target application. + * @param skillName The skill name identifier. + * @param skillType Output the skill type. + * @return Returns ERR_OK on success, others on failure. + */ + virtual int32_t QuerySkillType(const std::string &bundleName, const std::string &moduleName, + const std::string &skillName, int32_t &skillType) + { + return ERR_OK; + } + + virtual int32_t ExecuteSkillDone(const sptr &token, const std::string &requestCode, + int32_t resultCode, const AppExecFwk::SkillExecuteResult &result) + { + return ERR_OK; + } }; } // namespace AAFwk } // namespace OHOS diff --git a/interfaces/inner_api/ability_manager/include/ability_manager_ipc_interface_code.h b/interfaces/inner_api/ability_manager/include/ability_manager_ipc_interface_code.h index ee8371a2d4..6eb8bb959b 100644 --- a/interfaces/inner_api/ability_manager/include/ability_manager_ipc_interface_code.h +++ b/interfaces/inner_api/ability_manager/include/ability_manager_ipc_interface_code.h @@ -744,6 +744,12 @@ enum class AbilityManagerInterfaceCode { // query entity INSIGHT_INTENT_QUERY_ENTITY = 6165, + // execute in-app skill + EXECUTE_IN_APP_SKILL = 6169, + + // query skill type (independent or in-app) + QUERY_SKILL_TYPE = 6171, + // start self ability with token START_SELF = 6166, @@ -752,6 +758,18 @@ enum class AbilityManagerInterfaceCode { // start self uiability with start options and caller token START_SELF_UI_ABILITY_WITH_OPTIONS_AND_TOKEN = 6168, + + // start self uiability by application context in current process + START_SELF_UI_ABILITY_BY_APP_CONTEXT = 6169, + + // execute skill done with token for identity verification + EXECUTE_SKILL_DONE_WITH_TOKEN = 6172, + + // execute in-app skill with explicit caller tokenId + EXECUTE_IN_APP_SKILL_WITH_TOKEN_ID = 6173, + + // start self uiability in child process + START_SELF_UI_ABILITY_IN_CHILD_PROCESS = 6174, }; } // namespace AAFwk } // namespace OHOS diff --git a/interfaces/inner_api/ability_manager/include/ability_scheduler_interface.h b/interfaces/inner_api/ability_manager/include/ability_scheduler_interface.h index 242f7419e5..1cb3c2ce89 100644 --- a/interfaces/inner_api/ability_manager/include/ability_scheduler_interface.h +++ b/interfaces/inner_api/ability_manager/include/ability_scheduler_interface.h @@ -318,6 +318,12 @@ public: virtual void ScheduleAbilitiesRequestDone(const std::string &requestKey, int32_t resultCode) = 0; + /** + * @brief Called when a skill needs to be executed on an already-started ability. + * @param want The Want containing skill execution parameters. + */ + virtual void ExecuteSkill(const Want &want) {} + enum { // ipc id for scheduling ability to a state of life cycle SCHEDULE_ABILITY_TRANSACTION = 0, @@ -411,6 +417,8 @@ public: SCHEDULE_ONEXECUTE_INTENT, + SCHEDULE_EXECUTE_SKILL, + CREATE_MODAL_UI_EXTENSION, UPDATE_SESSION_TOKEN, diff --git a/interfaces/inner_api/ability_manager/include/ability_window_configuration.h b/interfaces/inner_api/ability_manager/include/ability_window_configuration.h index e5039c9597..fc5cebae02 100644 --- a/interfaces/inner_api/ability_manager/include/ability_window_configuration.h +++ b/interfaces/inner_api/ability_manager/include/ability_window_configuration.h @@ -45,7 +45,7 @@ enum AbilityWindowConfiguration { /** * Indicates that the Page ability is displayed in split-screen mode. */ - MULTI_WINDOW_DISPLAY_SPLIT = 103 + MULTI_WINDOW_DISPLAY_SPLIT = 105 }; } // namespace AAFwk } // namespace OHOS diff --git a/interfaces/inner_api/ability_manager/include/insight_intent/insight_intent_execute_param.h b/interfaces/inner_api/ability_manager/include/insight_intent/insight_intent_execute_param.h index 1110ca11a9..b9040f6bcf 100644 --- a/interfaces/inner_api/ability_manager/include/insight_intent/insight_intent_execute_param.h +++ b/interfaces/inner_api/ability_manager/include/insight_intent/insight_intent_execute_param.h @@ -86,6 +86,10 @@ constexpr char INSIGHT_INTENT_DECORATOR_CLASS[] = "ohos.insightIntent.decoratorC constexpr char INSIGHT_INTENT_QUERY_ENTITY_CLASS_NAME[] = "ohos.insightIntent.queryEntity.className"; constexpr char INSIGHT_INTENT_QUERY_TYPE[] = "ohos.insightIntent.queryEntity.queryType"; constexpr char INSIGHT_INTENT_QUERY_ENTITY_PARAM_PARAM[] = "ohos.insightIntent.queryEntity.param"; +constexpr char INSIGHT_INTENT_PARAM_USER_ID[] = "ohos.insightIntent.param.userId"; +constexpr char INSIGHT_INTENT_DISTRIBUTED_CALLBACK_KEY[] = "ohos.insightIntent.distributed.callbackKey"; +constexpr char INSIGHT_INTENT_DISTRIBUTED_SRC_DEVICE_ID[] = "ohos.insightIntent.distributed.srcDeviceId"; +constexpr char INSIGHT_INTENT_DISTRIBUTED_REQUEST_CODE[] = "ohos.insightIntent.distributed.requestCode"; constexpr int32_t INVALID_DISPLAY_ID = -1; constexpr char METHOD_PARAM_SEPARATOR = '\x1f'; @@ -139,6 +143,9 @@ public: std::string queryType_; std::string queryEntityClassName_; std::shared_ptr queryParams_; + + // distributed intent params + std::string deviceId_; }; } // namespace AppExecFwk } // namespace OHOS diff --git a/interfaces/inner_api/ability_manager/include/insight_intent/insight_intent_execute_result.h b/interfaces/inner_api/ability_manager/include/insight_intent/insight_intent_execute_result.h index 56fcd10216..aef5895c60 100644 --- a/interfaces/inner_api/ability_manager/include/insight_intent/insight_intent_execute_result.h +++ b/interfaces/inner_api/ability_manager/include/insight_intent/insight_intent_execute_result.h @@ -45,6 +45,8 @@ public: bool ReadFromParcel(Parcel &parcel); bool Marshalling(Parcel &parcel) const override; static InsightIntentExecuteResult *Unmarshalling(Parcel &parcel); + void FromJsonString(const std::string &jsonStr); + std::string ToJsonString() const; // Check result returned by intent executor static bool CheckResult(std::shared_ptr result); }; diff --git a/interfaces/inner_api/ability_manager/include/skill/skill_execute_callback_interface.h b/interfaces/inner_api/ability_manager/include/skill/skill_execute_callback_interface.h new file mode 100644 index 0000000000..45cf10ba20 --- /dev/null +++ b/interfaces/inner_api/ability_manager/include/skill/skill_execute_callback_interface.h @@ -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_SKILL_EXECUTE_CALLBACK_INTERFACE_H +#define OHOS_ABILITY_RUNTIME_SKILL_EXECUTE_CALLBACK_INTERFACE_H + +#include "iremote_broker.h" +#include "skill/skill_execute_result.h" + +namespace OHOS { +namespace AAFwk { +class ISkillExecuteCallback : public OHOS::IRemoteBroker { +public: + DECLARE_INTERFACE_DESCRIPTOR(u"ohos.AAFwk.SkillExecuteCallback"); + + virtual void OnExecuteDone(const std::string &requestCode, int32_t resultCode, + const AppExecFwk::SkillExecuteResult &result) = 0; + + enum { + ON_SKILL_EXECUTE_DONE = 1, + }; +}; +} // namespace AAFwk +} // namespace OHOS +#endif // OHOS_ABILITY_RUNTIME_SKILL_EXECUTE_CALLBACK_INTERFACE_H diff --git a/interfaces/inner_api/ability_manager/include/skill/skill_execute_callback_proxy.h b/interfaces/inner_api/ability_manager/include/skill/skill_execute_callback_proxy.h new file mode 100644 index 0000000000..be72f624e2 --- /dev/null +++ b/interfaces/inner_api/ability_manager/include/skill/skill_execute_callback_proxy.h @@ -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. + */ + +#ifndef OHOS_ABILITY_RUNTIME_SKILL_EXECUTE_CALLBACK_PROXY_H +#define OHOS_ABILITY_RUNTIME_SKILL_EXECUTE_CALLBACK_PROXY_H + +#include "iremote_proxy.h" +#include "skill/skill_execute_callback_interface.h" + +namespace OHOS { +namespace AAFwk { +class SkillExecuteCallbackProxy : public IRemoteProxy { +public: + explicit SkillExecuteCallbackProxy(const sptr &impl) + : IRemoteProxy(impl) {} + ~SkillExecuteCallbackProxy() override = default; + + void OnExecuteDone(const std::string &requestCode, int32_t resultCode, + const AppExecFwk::SkillExecuteResult &result) override; + +private: + static inline BrokerDelegator delegator_; +}; +} // namespace AAFwk +} // namespace OHOS +#endif // OHOS_ABILITY_RUNTIME_SKILL_EXECUTE_CALLBACK_PROXY_H diff --git a/interfaces/inner_api/ability_manager/include/skill/skill_execute_callback_stub.h b/interfaces/inner_api/ability_manager/include/skill/skill_execute_callback_stub.h new file mode 100644 index 0000000000..83d56400f0 --- /dev/null +++ b/interfaces/inner_api/ability_manager/include/skill/skill_execute_callback_stub.h @@ -0,0 +1,36 @@ +/* + * 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_SKILL_EXECUTE_CALLBACK_STUB_H +#define OHOS_ABILITY_RUNTIME_SKILL_EXECUTE_CALLBACK_STUB_H + +#include "iremote_stub.h" +#include "skill/skill_execute_callback_interface.h" + +namespace OHOS { +namespace AAFwk { +class SkillExecuteCallbackStub : public IRemoteStub { +public: + SkillExecuteCallbackStub(); + ~SkillExecuteCallbackStub(); + int32_t OnRemoteRequest( + uint32_t code, MessageParcel &data, MessageParcel &reply, MessageOption &option) override; + +private: + int32_t OnExecuteDoneInner(MessageParcel &data, MessageParcel &reply); +}; +} // namespace AAFwk +} // namespace OHOS +#endif // OHOS_ABILITY_RUNTIME_SKILL_EXECUTE_CALLBACK_STUB_H diff --git a/interfaces/inner_api/ability_manager/include/skill/skill_execute_param.h b/interfaces/inner_api/ability_manager/include/skill/skill_execute_param.h new file mode 100644 index 0000000000..a91f4389dc --- /dev/null +++ b/interfaces/inner_api/ability_manager/include/skill/skill_execute_param.h @@ -0,0 +1,86 @@ +/* + * 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_SKILL_EXECUTE_PARAM_H +#define OHOS_ABILITY_RUNTIME_SKILL_EXECUTE_PARAM_H + +#include +#include + +#include "parcel.h" +#include "want.h" +#include "want_params.h" + +namespace OHOS { +namespace AppExecFwk { + +using WantParams = OHOS::AAFwk::WantParams; + +struct SkillExecuteRequest { + uint32_t callerTokenId = 0; + std::string bundleName; + std::string moduleName; + std::string skillName; + std::string scriptPath; + std::string functionName; + std::shared_ptr skillArgs; +}; + +// Want parameter keys for skill execution +constexpr char SKILL_EXECUTE_PARAM_BUNDLE_NAME[] = "ohos.skill.executeParam.bundleName"; +constexpr char SKILL_EXECUTE_PARAM_MODULE_NAME[] = "ohos.skill.executeParam.moduleName"; +constexpr char SKILL_EXECUTE_PARAM_SKILL_NAME[] = "ohos.skill.executeParam.skillName"; +constexpr char SKILL_EXECUTE_PARAM_SCRIPT_PATH[] = "ohos.skill.executeParam.scriptPath"; +constexpr char SKILL_EXECUTE_PARAM_FUNCTION_NAME[] = "ohos.skill.executeParam.functionName"; +constexpr char SKILL_EXECUTE_PARAM_ARGS_KEYS[] = "ohos.skill.executeParam.argsKeys"; +constexpr char SKILL_EXECUTE_PARAM_ARGS_PREFIX[] = "ohos.skill.executeParam.args."; +constexpr char SKILL_EXECUTE_PARAM_SRC_ENTRIES_COUNT[] = "ohos.skill.executeParam.srcEntriesCount"; +constexpr char SKILL_EXECUTE_PARAM_SRC_ENTRY_PREFIX[] = "ohos.skill.executeParam.srcEntry."; +constexpr char SKILL_EXECUTE_PARAM_HAP_PATH[] = "ohos.skill.executeParam.hapPath"; +constexpr char SKILL_EXECUTE_PARAM_REQUEST_CODE[] = "ohos.skill.executeParam.requestCode"; + +class SkillExecuteParam : public Parcelable { +public: + SkillExecuteParam() = default; + ~SkillExecuteParam() = default; + + bool ReadFromParcel(Parcel &parcel); + virtual bool Marshalling(Parcel &parcel) const override; + static SkillExecuteParam *Unmarshalling(Parcel &parcel); + + static bool IsSkillExecute(const AAFwk::Want &want); + static bool GenerateFromWant(const AAFwk::Want &want, SkillExecuteParam ¶m); + static bool RemoveSkillParam(AAFwk::Want &want); + static void WriteToWant(AAFwk::Want &want, const std::string &bundleName, + const std::string &moduleName, const std::string &skillName, + const std::string &scriptPath = "", const std::string &functionName = "", + const std::shared_ptr &skillArgs = nullptr, + const std::vector &srcEntries = {}, + const std::string &requestCode = "", const std::string &hapPath = ""); + + std::string bundleName_; + std::string moduleName_; + std::string skillName_; + std::string scriptPath_; + std::string functionName_; + std::shared_ptr skillArgs_; + std::vector srcEntries_; + std::string hapPath_; + std::string requestCode_; +}; + +} // namespace AppExecFwk +} // namespace OHOS +#endif // OHOS_ABILITY_RUNTIME_SKILL_EXECUTE_PARAM_H diff --git a/interfaces/inner_api/ability_manager/include/skill/skill_execute_result.h b/interfaces/inner_api/ability_manager/include/skill/skill_execute_result.h new file mode 100644 index 0000000000..5735e2ee47 --- /dev/null +++ b/interfaces/inner_api/ability_manager/include/skill/skill_execute_result.h @@ -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_SKILL_EXECUTE_RESULT_H +#define OHOS_ABILITY_RUNTIME_SKILL_EXECUTE_RESULT_H + +#include +#include + +#include "parcel.h" +#include "want_params.h" + +namespace OHOS { +namespace AppExecFwk { + +using WantParams = OHOS::AAFwk::WantParams; + +struct SkillExecuteResult : public Parcelable { +public: + int32_t code = 0; + std::shared_ptr result = nullptr; + std::vector uris; + uint32_t flags = 0; + + bool ReadFromParcel(Parcel &parcel); + bool Marshalling(Parcel &parcel) const override; + static SkillExecuteResult *Unmarshalling(Parcel &parcel); +}; + +} // namespace AppExecFwk +} // namespace OHOS +#endif // OHOS_ABILITY_RUNTIME_SKILL_EXECUTE_RESULT_H diff --git a/interfaces/inner_api/ability_manager/include/skill/skill_query_info.h b/interfaces/inner_api/ability_manager/include/skill/skill_query_info.h new file mode 100644 index 0000000000..15fb2774bf --- /dev/null +++ b/interfaces/inner_api/ability_manager/include/skill/skill_query_info.h @@ -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_SKILL_QUERY_INFO_H +#define OHOS_ABILITY_RUNTIME_SKILL_QUERY_INFO_H + +#include +#include + +#include "parcel.h" + +namespace OHOS { +namespace AppExecFwk { + +struct SkillQueryInfo : public Parcelable { + std::string bundleName; + std::string moduleName; + std::string skillName; + std::string abilityName; + int32_t type = 0; + std::vector srcEntries; + std::vector permissions; + + bool ReadFromParcel(Parcel &parcel); + bool Marshalling(Parcel &parcel) const override; + static SkillQueryInfo *Unmarshalling(Parcel &parcel); +}; + +} // namespace AppExecFwk +} // namespace OHOS +#endif // OHOS_ABILITY_RUNTIME_SKILL_QUERY_INFO_H diff --git a/interfaces/inner_api/ani_base_context/BUILD.gn b/interfaces/inner_api/ani_base_context/BUILD.gn index 2176de5b7a..20df9f6f4c 100644 --- a/interfaces/inner_api/ani_base_context/BUILD.gn +++ b/interfaces/inner_api/ani_base_context/BUILD.gn @@ -45,6 +45,7 @@ ohos_shared_library("ani_base_context") { "bundle_framework:appexecfwk_base", "bundle_framework:appexecfwk_core", "hilog:libhilog", + "icu:shared_icuuc", "ipc:ipc_single", "runtime_core:ani", ] diff --git a/interfaces/inner_api/app_manager/include/appmgr/app_launch_data.h b/interfaces/inner_api/app_manager/include/appmgr/app_launch_data.h index 8b61548f47..d62c293d23 100644 --- a/interfaces/inner_api/app_manager/include/appmgr/app_launch_data.h +++ b/interfaces/inner_api/app_manager/include/appmgr/app_launch_data.h @@ -241,6 +241,16 @@ public: preloadModuleName_ = moduleName; } + inline std::string GetPreloadAbilityName() const + { + return preloadAbilityName_; + } + + inline void SetPreloadAbilityName(const std::string& abilityName) + { + preloadAbilityName_ = abilityName; + } + inline std::string GetInstanceKey() const { return instanceKey_; @@ -368,25 +378,8 @@ public: */ bool GetDebugFromLocal() const; - inline void SetArkChildProcessSupported(bool supported) - { - isArkChildProcessSupported_ = supported; - } - - inline bool IsArkChildProcessSupported() const - { - return isArkChildProcessSupported_; - } - - inline void SetNativeChildProcessSupported(bool supported) - { - isNativeChildProcessSupported_ = supported; - } - - inline bool IsNativeChildProcessSupported() const - { - return isNativeChildProcessSupported_; - } + void SetMainProcess(bool isMainProcess); + bool GetMainProcess() const; private: bool debugApp_ = false; @@ -409,10 +402,10 @@ private: std::string appRunningUniqueId_; std::string instanceKey_; std::string preloadModuleName_; + std::string preloadAbilityName_; // ability name for preloading specific ability (for image creation) bool isDebugFromLocal_; + bool isMainProcess_ = true; std::shared_ptr startupTaskData_ = nullptr; - bool isArkChildProcessSupported_ = false; - bool isNativeChildProcessSupported_ = false; }; } // namespace AppExecFwk } // namespace OHOS diff --git a/interfaces/inner_api/app_manager/include/appmgr/app_mem_dump_info.h b/interfaces/inner_api/app_manager/include/appmgr/app_mem_dump_info.h index 0456b46e4e..16581104e6 100644 --- a/interfaces/inner_api/app_manager/include/appmgr/app_mem_dump_info.h +++ b/interfaces/inner_api/app_manager/include/appmgr/app_mem_dump_info.h @@ -26,15 +26,20 @@ enum class MemDumpType : uint32_t { NATIVE = 1, JSVM = 2, KMP_KOTLIN = 3, + ARKWEB_JS = 4, }; struct MemDumpInfo : public Parcelable { MemDumpType dumpType = MemDumpType::INVALID; bool needLeakobj = false; + bool needRaw = false; uint32_t pid = 0; uint32_t tid = 0; + uint32_t renderPid = 0; bool isSync = false; bool mayReportToOEM = false; + bool needDump = true; + bool needGc = false; virtual bool Marshalling(Parcel &parcel) const override; static MemDumpInfo *Unmarshalling(Parcel &parcel); }; diff --git a/interfaces/inner_api/app_manager/include/appmgr/app_mgr_client.h b/interfaces/inner_api/app_manager/include/appmgr/app_mgr_client.h index 0f0a942bd4..502f87c0bb 100644 --- a/interfaces/inner_api/app_manager/include/appmgr/app_mgr_client.h +++ b/interfaces/inner_api/app_manager/include/appmgr/app_mgr_client.h @@ -1001,6 +1001,8 @@ public: int32_t IsProcessCacheSupported(int32_t pid, bool &isSupported); + int32_t IsChildProcessSupported(bool isNative, bool &isSupported); + int32_t SetProcessCacheEnable(int32_t pid, bool enable); int32_t LockProcessCache(int32_t pid, bool isLock); @@ -1163,6 +1165,9 @@ public: */ int32_t GetAllAbilityInfos(const int32_t pid, std::vector &infos); + virtual int32_t EnableDelayedProcessExit(int32_t pid, bool enabled) const; + + virtual void CancelDelayedExitTask(int32_t pid) const; private: void SetServiceManager(std::unique_ptr serviceMgr); /** diff --git a/interfaces/inner_api/app_manager/include/appmgr/app_mgr_interface.h b/interfaces/inner_api/app_manager/include/appmgr/app_mgr_interface.h index 8d5a7eed91..89311ba16b 100644 --- a/interfaces/inner_api/app_manager/include/appmgr/app_mgr_interface.h +++ b/interfaces/inner_api/app_manager/include/appmgr/app_mgr_interface.h @@ -977,6 +977,12 @@ public: virtual int32_t IsProcessCacheSupported(int32_t pid, bool &isSupported) = 0; + virtual int32_t IsChildProcessSupported(bool isNative, bool &isSupported) + { + isSupported = false; + return 0; + } + virtual int32_t SetProcessCacheEnable(int32_t pid, bool enable) = 0; virtual int32_t LockProcessCache(int32_t pid, bool isLock) @@ -1203,6 +1209,13 @@ public: { return 0; } + + virtual int32_t EnableDelayedProcessExit(int32_t pid, bool enabled) + { + return ERR_OK; + } + + virtual void CancelDelayedExitTask(int32_t pid) {} }; } // namespace AppExecFwk } // namespace OHOS diff --git a/interfaces/inner_api/app_manager/include/appmgr/app_mgr_ipc_interface_code.h b/interfaces/inner_api/app_manager/include/appmgr/app_mgr_ipc_interface_code.h index cdf162f956..f2fa5bdde9 100644 --- a/interfaces/inner_api/app_manager/include/appmgr/app_mgr_ipc_interface_code.h +++ b/interfaces/inner_api/app_manager/include/appmgr/app_mgr_ipc_interface_code.h @@ -156,7 +156,10 @@ enum class AppMgrInterfaceCode { DUMP_MEM_PROCESS = 131, UPDATE_FREEZE_EXCLUDED_PID = 132, GET_PROCESS_RUNNING_INFOS_BY_ACCESS_TOKEN_ID = 133, - SET_TERMINATE_TIMEOUT_FLAG = 134 + SET_TERMINATE_TIMEOUT_FLAG = 134, + ENABLE_DELAYED_PROCESS_EXIT = 135, + CANCEL_DELAYED_EXIT_TASK = 136, + IS_CHILD_PROCESS_SUPPORTED = 137 }; } // AppExecFwk } // OHOS diff --git a/interfaces/inner_api/app_manager/include/appmgr/app_mgr_proxy.h b/interfaces/inner_api/app_manager/include/appmgr/app_mgr_proxy.h index 333aec71d7..426342a0b6 100644 --- a/interfaces/inner_api/app_manager/include/appmgr/app_mgr_proxy.h +++ b/interfaces/inner_api/app_manager/include/appmgr/app_mgr_proxy.h @@ -818,6 +818,8 @@ public: int32_t IsProcessCacheSupported(int32_t pid, bool &isSupported) override; + int32_t IsChildProcessSupported(bool isNative, bool &isSupported) override; + int32_t SetProcessCacheEnable(int32_t pid, bool enable) override; int32_t LockProcessCache(int32_t pid, bool isLock) override; @@ -968,6 +970,16 @@ public: * @return Returns ERR_OK on success, others on failure. */ virtual int32_t GetAllAbilityInfos(const int32_t pid, std::vector &infos) override; + + /** + * Enable delayed process exit. + * + * @param pid Process id. + * @param enabled Whether enable delayed process exit. + * @return Returns ERR_OK on success, others on failure. + */ + int32_t EnableDelayedProcessExit(int32_t pid, bool enabled) override; + void CancelDelayedExitTask(int32_t pid) override; private: bool SendTransactCmd(AppMgrInterfaceCode code, MessageParcel &data, MessageParcel &reply); bool WriteInterfaceToken(MessageParcel &data); diff --git a/interfaces/inner_api/app_manager/include/appmgr/app_mgr_stub.h b/interfaces/inner_api/app_manager/include/appmgr/app_mgr_stub.h index 151b302106..3345ca9dff 100644 --- a/interfaces/inner_api/app_manager/include/appmgr/app_mgr_stub.h +++ b/interfaces/inner_api/app_manager/include/appmgr/app_mgr_stub.h @@ -176,6 +176,7 @@ private: int32_t HandleSetSupportedProcessCacheSelf(MessageParcel &data, MessageParcel &reply); int32_t HandleSetSupportedProcessCache(MessageParcel &data, MessageParcel &reply); int32_t HandleIsProcessCacheSupported(MessageParcel &data, MessageParcel &reply); + int32_t HandleIsChildProcessSupported(MessageParcel &data, MessageParcel &reply); int32_t HandleSetProcessCacheEnable(MessageParcel &data, MessageParcel &reply); int32_t HandleLockProcessCache(MessageParcel &data, MessageParcel &reply); int32_t HandleSaveBrowserChannel(MessageParcel &data, MessageParcel &reply); @@ -223,6 +224,8 @@ private: int32_t HandleSetProcessPrepareExit(MessageParcel &data, MessageParcel &reply); int32_t HandleSetTerminateTimeOutFlag(MessageParcel &data, MessageParcel &reply); int32_t HandleGetAllAbilityInfos(MessageParcel &data, MessageParcel &reply); + int32_t HandleEnableDelayedProcessExit(MessageParcel &data, MessageParcel &reply); + int32_t HandleCancelDelayedExitTask(MessageParcel &data, MessageParcel &reply); DISALLOW_COPY_AND_MOVE(AppMgrStub); }; } // namespace AppExecFwk diff --git a/interfaces/inner_api/app_manager/include/appmgr/image_process_state_data.h b/interfaces/inner_api/app_manager/include/appmgr/image_process_state_data.h index a23f353878..c81a5ebf16 100644 --- a/interfaces/inner_api/app_manager/include/appmgr/image_process_state_data.h +++ b/interfaces/inner_api/app_manager/include/appmgr/image_process_state_data.h @@ -50,6 +50,7 @@ struct ImageProcessStateData : public Parcelable { int32_t uid = -1; int32_t state = -1; std::string bundleName; + std::string abilityName; }; } // namespace AppExecFwk } // namespace OHOS diff --git a/interfaces/inner_api/app_manager/src/appmgr/app_launch_data.cpp b/interfaces/inner_api/app_manager/src/appmgr/app_launch_data.cpp index 372be89b18..89aa01bc7b 100644 --- a/interfaces/inner_api/app_manager/src/appmgr/app_launch_data.cpp +++ b/interfaces/inner_api/app_manager/src/appmgr/app_launch_data.cpp @@ -136,18 +136,15 @@ bool AppLaunchData::MarshallingExtend(Parcel &parcel) const return false; } + if (!parcel.WriteString(preloadAbilityName_)) { + TAG_LOGE(AAFwkTag::APPMGR, "Marshalling, Failed to write preloadAbilityName."); + return false; + } + if (!parcel.WriteBool(isDebugFromLocal_)) { TAG_LOGE(AAFwkTag::APPMGR, "Marshalling, Failed to write isDebugFromLocal"); return false; } - if (!parcel.WriteBool(isArkChildProcessSupported_)) { - TAG_LOGE(AAFwkTag::APPMGR, "Marshalling, Failed to write isArkChildProcessSupported"); - return false; - } - if (!parcel.WriteBool(isNativeChildProcessSupported_)) { - TAG_LOGE(AAFwkTag::APPMGR, "Marshalling, Failed to write isNativeChildProcessSupported"); - return false; - } bool hasStartupTaskData = startupTaskData_ ? true : false; if (!parcel.WriteBool(hasStartupTaskData)) { TAG_LOGE(AAFwkTag::APPMGR, "Failed to write the hasStartupTaskData"); @@ -161,6 +158,10 @@ bool AppLaunchData::MarshallingExtend(Parcel &parcel) const TAG_LOGE(AAFwkTag::APPMGR, "Marshalling, Failed to write imageProcessType"); return false; } + if (!parcel.WriteBool(isMainProcess_)) { + TAG_LOGE(AAFwkTag::APPMGR, "Marshalling, Failed to write isMainProcess"); + return false; + } return true; } @@ -210,13 +211,13 @@ bool AppLaunchData::ReadFromParcel(Parcel &parcel) appPreloadMode_ = static_cast(parcel.ReadInt32()); isAllowedNWebPreload_ = parcel.ReadBool(); preloadModuleName_ = parcel.ReadString(); + preloadAbilityName_ = parcel.ReadString(); isDebugFromLocal_ = parcel.ReadBool(); - isArkChildProcessSupported_ = parcel.ReadBool(); - isNativeChildProcessSupported_ = parcel.ReadBool(); if (!ReadStartupTaskDataFromParcel(parcel)) { return false; } imageProcessType_ = parcel.ReadInt32(); + isMainProcess_ = parcel.ReadBool(); return true; } @@ -362,6 +363,16 @@ bool AppLaunchData::GetDebugFromLocal() const return isDebugFromLocal_; } +void AppLaunchData::SetMainProcess(bool isMainProcess) +{ + isMainProcess_ = isMainProcess; +} + +bool AppLaunchData::GetMainProcess() const +{ + return isMainProcess_; +} + bool StartupTaskData::Marshalling(Parcel &parcel) const { if (!parcel.WriteString(action)) { diff --git a/interfaces/inner_api/app_manager/src/appmgr/app_mem_dump_info.cpp b/interfaces/inner_api/app_manager/src/appmgr/app_mem_dump_info.cpp index 46c60c7d2f..065b1626aa 100644 --- a/interfaces/inner_api/app_manager/src/appmgr/app_mem_dump_info.cpp +++ b/interfaces/inner_api/app_manager/src/appmgr/app_mem_dump_info.cpp @@ -27,18 +27,30 @@ bool MemDumpInfo::Marshalling(Parcel &parcel) const if (!parcel.WriteBool(needLeakobj)) { return false; } + if (!parcel.WriteBool(needRaw)) { + return false; + } if (!parcel.WriteUint32(pid)) { return false; } if (!parcel.WriteUint32(tid)) { return false; } + if (!parcel.WriteUint32(renderPid)) { + return false; + } if (!parcel.WriteBool(isSync)) { return false; } if (!parcel.WriteBool(mayReportToOEM)) { return false; } + if (!parcel.WriteBool(needDump)) { + return false; + } + if (!parcel.WriteBool(needGc)) { + return false; + } return true; } @@ -61,6 +73,10 @@ MemDumpInfo *MemDumpInfo::Unmarshalling(Parcel &parcel) delete info; return nullptr; } + if (!parcel.ReadBool(info->needRaw)) { + delete info; + return nullptr; + } if (!parcel.ReadUint32(info->pid)) { delete info; return nullptr; @@ -73,11 +89,16 @@ MemDumpInfo *MemDumpInfo::Unmarshalling(Parcel &parcel) delete info; return nullptr; } + if (!parcel.ReadUint32(info->renderPid)) { + delete info; + return nullptr; + } if (!parcel.ReadBool(info->isSync)) { delete info; return nullptr; } - if (!parcel.ReadBool(info->mayReportToOEM)) { + if (!parcel.ReadBool(info->mayReportToOEM) || !parcel.ReadBool(info->needDump) || + !parcel.ReadBool(info->needGc)) { delete info; return nullptr; } diff --git a/interfaces/inner_api/app_manager/src/appmgr/app_mgr_client.cpp b/interfaces/inner_api/app_manager/src/appmgr/app_mgr_client.cpp index 7dd357d777..be6b4b8381 100644 --- a/interfaces/inner_api/app_manager/src/appmgr/app_mgr_client.cpp +++ b/interfaces/inner_api/app_manager/src/appmgr/app_mgr_client.cpp @@ -808,6 +808,26 @@ int32_t AppMgrClient::GetRunningProcessInfoByChildProcessPid(const pid_t childPi return service->GetRunningProcessInfoByChildProcessPid(childPid, info); } +int32_t AppMgrClient::EnableDelayedProcessExit(int32_t pid, bool enabled) const +{ + sptr service = iface_cast(mgrHolder_->GetRemoteObject()); + if (service == nullptr) { + TAG_LOGE(AAFwkTag::APPMGR, "Service is nullptr."); + return AppMgrResultCode::ERROR_SERVICE_NOT_CONNECTED; + } + return service->EnableDelayedProcessExit(pid, enabled); +} + +void AppMgrClient::CancelDelayedExitTask(int32_t pid) const +{ + sptr service = iface_cast(mgrHolder_->GetRemoteObject()); + if (service == nullptr) { + TAG_LOGE(AAFwkTag::APPMGR, "Service is nullptr."); + return; + } + service->CancelDelayedExitTask(pid); +} + void AppMgrClient::SetAbilityForegroundingFlagToAppRecord(const pid_t pid) const { sptr service = iface_cast(mgrHolder_->GetRemoteObject()); @@ -1626,6 +1646,21 @@ int32_t AppMgrClient::IsProcessCacheSupported(int32_t pid, bool &isSupported) return service->IsProcessCacheSupported(pid, isSupported); } +int32_t AppMgrClient::IsChildProcessSupported(bool isNative, bool &isSupported) +{ + TAG_LOGD(AAFwkTag::APPMGR, "IsChildProcessSupported called"); + if (mgrHolder_ == nullptr) { + TAG_LOGE(AAFwkTag::APPMGR, "mgrHolder_ is nullptr."); + return AppMgrResultCode::ERROR_SERVICE_NOT_READY; + } + sptr service = iface_cast(mgrHolder_->GetRemoteObject()); + if (service == nullptr) { + TAG_LOGE(AAFwkTag::APPMGR, "Service is nullptr."); + return AppMgrResultCode::ERROR_SERVICE_NOT_CONNECTED; + } + return service->IsChildProcessSupported(isNative, isSupported); +} + int32_t AppMgrClient::SetProcessCacheEnable(int32_t pid, bool enable) { TAG_LOGD(AAFwkTag::APPMGR, "SetProcessCacheEnable called"); diff --git a/interfaces/inner_api/app_manager/src/appmgr/app_mgr_proxy.cpp b/interfaces/inner_api/app_manager/src/appmgr/app_mgr_proxy.cpp index b9702290d8..579b5cab0e 100644 --- a/interfaces/inner_api/app_manager/src/appmgr/app_mgr_proxy.cpp +++ b/interfaces/inner_api/app_manager/src/appmgr/app_mgr_proxy.cpp @@ -2363,6 +2363,24 @@ int32_t AppMgrProxy::IsProcessCacheSupported(int32_t pid, bool &isSupported) return reply.ReadInt32(); } +int32_t AppMgrProxy::IsChildProcessSupported(bool isNative, bool &isSupported) +{ + TAG_LOGD(AAFwkTag::APPMGR, "IsChildProcessSupported called"); + MessageParcel data; + if (!WriteInterfaceToken(data)) { + TAG_LOGE(AAFwkTag::APPMGR, "Write interface token failed."); + return AAFwk::ERR_WRITE_INTERFACE_TOKEN_FAILED; + } + PARCEL_UTIL_WRITE_RET_INT(data, Bool, isNative); + + MessageParcel reply; + MessageOption option; + + PARCEL_UTIL_SENDREQ_RET_INT(AppMgrInterfaceCode::IS_CHILD_PROCESS_SUPPORTED, data, reply, option); + isSupported = reply.ReadBool(); + return reply.ReadInt32(); +} + int32_t AppMgrProxy::SetProcessCacheEnable(int32_t pid, bool enable) { TAG_LOGD(AAFwkTag::APPMGR, "SetProcessCacheEnable called"); @@ -2986,5 +3004,37 @@ int32_t AppMgrProxy::GetAllAbilityInfos(const int32_t pid, std::vector(AppMgrInterfaceCode::REGISTER_IMAGE_PROCESS_STATE_OBSERVER): return HandleRegisterImageProcessStateObserver(data, reply); + case static_cast(AppMgrInterfaceCode::IS_CHILD_PROCESS_SUPPORTED): + return HandleIsChildProcessSupported(data, reply); } return INVALID_FD; } @@ -462,6 +464,10 @@ int32_t AppMgrStub::OnRemoteRequestInnerNinth(uint32_t code, MessageParcel &data return HandleUnregisterImageProcessStateObserver(data, reply); case static_cast(AppMgrInterfaceCode::SET_TERMINATE_TIMEOUT_FLAG): return HandleSetTerminateTimeOutFlag(data, reply); + case static_cast(AppMgrInterfaceCode::ENABLE_DELAYED_PROCESS_EXIT): + return HandleEnableDelayedProcessExit(data, reply); + case static_cast(AppMgrInterfaceCode::CANCEL_DELAYED_EXIT_TASK): + return HandleCancelDelayedExitTask(data, reply); } return INVALID_FD; } @@ -2045,6 +2051,23 @@ int32_t AppMgrStub::HandleIsProcessCacheSupported(MessageParcel &data, MessagePa return NO_ERROR; } +int32_t AppMgrStub::HandleIsChildProcessSupported(MessageParcel &data, MessageParcel &reply) +{ + TAG_LOGD(AAFwkTag::APPMGR, "HandleIsChildProcessSupported called"); + bool isNative = data.ReadBool(); + bool isSupported = false; + auto ret = IsChildProcessSupported(isNative, isSupported); + if (!reply.WriteBool(isSupported)) { + TAG_LOGE(AAFwkTag::APPMGR, "Write isSupported error."); + return AAFwk::ERR_WRITE_BOOL_FAILED; + } + if (!reply.WriteInt32(ret)) { + TAG_LOGE(AAFwkTag::APPMGR, "Write ret error."); + return AAFwk::ERR_WRITE_RESULT_CODE_FAILED; + } + return NO_ERROR; +} + int32_t AppMgrStub::HandleSetProcessCacheEnable(MessageParcel &data, MessageParcel &reply) { TAG_LOGD(AAFwkTag::APPMGR, "HandleSetProcessCacheEnable called"); @@ -2473,5 +2496,25 @@ int32_t AppMgrStub::HandleGetAllAbilityInfos(MessageParcel &data, MessageParcel } return NO_ERROR; } + +int32_t AppMgrStub::HandleEnableDelayedProcessExit(MessageParcel &data, MessageParcel &reply) +{ + pid_t pid = data.ReadInt32(); + bool enabled = data.ReadBool(); + auto result = EnableDelayedProcessExit(pid, enabled); + if (!reply.WriteInt32(result)) { + TAG_LOGE(AAFwkTag::APPMGR, "write result fail"); + return AAFwk::ERR_WRITE_RESULT_CODE_FAILED; + } + return NO_ERROR; +} + +int32_t AppMgrStub::HandleCancelDelayedExitTask(MessageParcel &data, MessageParcel &reply) +{ + TAG_LOGD(AAFwkTag::APPMGR, "HandleCancelDelayedExitTask call"); + pid_t pid = data.ReadInt32(); + CancelDelayedExitTask(pid); + return NO_ERROR; +} } // namespace AppExecFwk } // namespace OHOS diff --git a/interfaces/inner_api/app_manager/src/appmgr/image_process_state_data.cpp b/interfaces/inner_api/app_manager/src/appmgr/image_process_state_data.cpp index 83b1b24148..b177174644 100644 --- a/interfaces/inner_api/app_manager/src/appmgr/image_process_state_data.cpp +++ b/interfaces/inner_api/app_manager/src/appmgr/image_process_state_data.cpp @@ -23,7 +23,8 @@ namespace AppExecFwk { bool ImageProcessStateData::Marshalling(Parcel &parcel) const { return (parcel.WriteInt32(imagePid) && parcel.WriteUint64(checkpointId) && parcel.WriteInt32(originalPid) - && parcel.WriteInt32(uid) && parcel.WriteInt32(state) && parcel.WriteString(bundleName)); + && parcel.WriteInt32(uid) && parcel.WriteInt32(state) && parcel.WriteString(bundleName) + && parcel.WriteString(abilityName)); } bool ImageProcessStateData::ReadFromParcel(Parcel &parcel) @@ -34,6 +35,7 @@ bool ImageProcessStateData::ReadFromParcel(Parcel &parcel) uid = parcel.ReadInt32(); state = parcel.ReadInt32(); bundleName = parcel.ReadString(); + abilityName = parcel.ReadString(); return true; } diff --git a/interfaces/inner_api/child_process_manager/include/child_process_manager.h b/interfaces/inner_api/child_process_manager/include/child_process_manager.h index e1daabe1a0..4ed3a59319 100644 --- a/interfaces/inner_api/child_process_manager/include/child_process_manager.h +++ b/interfaces/inner_api/child_process_manager/include/child_process_manager.h @@ -46,8 +46,6 @@ public: bool IsChildProcessBySelfFork(); bool IsArkChildProcessSupported(); bool IsNativeChildProcessSupported(); - void SetArkChildProcessSupported(bool supported); - void SetNativeChildProcessSupported(bool supported); ChildProcessManagerErrorCode StartChildProcessBySelfFork(const std::string &srcEntry, pid_t &pid, bool isStaticChildProcess = false); ChildProcessManagerErrorCode StartChildProcessByAppSpawnFork(const std::string &srcEntry, pid_t &pid, @@ -100,8 +98,6 @@ private: static bool signalRegistered_; bool isChildProcessBySelfFork_ = false; std::atomic childProcessCount_ = 0; - bool isArkChildProcessSupported_ = false; - bool isNativeChildProcessSupported_ = false; mutable std::mutex appMutex_; std::weak_ptr application_; diff --git a/interfaces/inner_api/runtime/include/native_runtime.h b/interfaces/inner_api/runtime/include/native_runtime.h index ffffa5c6d2..bbba261d92 100644 --- a/interfaces/inner_api/runtime/include/native_runtime.h +++ b/interfaces/inner_api/runtime/include/native_runtime.h @@ -18,6 +18,7 @@ #include #include "extension_ability.h" +#include "runtime.h" namespace OHOS { namespace AbilityRuntime { @@ -25,6 +26,8 @@ class NativeRuntime { public: static bool LoadModule(const std::string& bundleModuleName, const std::string& fileName, const std::string& abilityName, AbilityRuntime_ExtensionInstance& instance); + static void StartDebugMode(const Runtime::DebugOption &dOption, const std::string &bundleName); + static void StopDebugMode(); }; } // namespace AbilityRuntime } // namespace OHOS diff --git a/interfaces/inner_api/runtime/include/runtime.h b/interfaces/inner_api/runtime/include/runtime.h index 001107e5ee..1f3cc3a351 100644 --- a/interfaces/inner_api/runtime/include/runtime.h +++ b/interfaces/inner_api/runtime/include/runtime.h @@ -91,6 +91,7 @@ public: uint32_t versionCode = 0; bool enableWarmStartupSmartGC = false; std::string arkTSMode; + bool isMainProcess = true; }; struct DebugOption { diff --git a/interfaces/kits/c/ability_runtime/ability_runtime_common.h b/interfaces/kits/c/ability_runtime/ability_runtime_common.h index 795fb48195..0b942c0e39 100644 --- a/interfaces/kits/c/ability_runtime/ability_runtime_common.h +++ b/interfaces/kits/c/ability_runtime/ability_runtime_common.h @@ -190,31 +190,37 @@ typedef enum { */ ABILITY_RUNTIME_ERROR_CODE_UPPER_CONNECTION_NUMBER_LIMIT = 16000172, /** - * @error The requested property, method, enum, or struct member is not found. + * The requested property, method, enum, or struct member is not found. * * @since 26.0.0 */ ABILITY_RUNTIME_ERROR_CODE_PROPERTY_NOT_FOUND = 16000173, /** - * @error The runtime value type does not match the expected metadata type. + * The runtime value type does not match the expected metadata type. * * @since 26.0.0 */ ABILITY_RUNTIME_ERROR_CODE_TYPE_MISMATCH = 16000174, /** - * @error The IPC request to the remote service fails. + * The IPC request to the remote service fails. * * @since 26.0.0 */ ABILITY_RUNTIME_ERROR_CODE_SEND_REQUEST_FAILED = 16000175, /** - * The distribution type of application is invalid. + * Caller and target are not in the same application for IN_PROCESS mode. * * @since 26.0.0 */ - ABILITY_RUNTIME_ERROR_CODE_INVALID_DISTRIBUTION_TYPE = 16000176, + ABILITY_RUNTIME_ERROR_CODE_CROSS_APP_IN_PROCESS = 16000176, /** - * @error The type library metadata is invalid, e.g. missing or duplicate main interface. + * The native ability wrapper is invalid or incomplete. + * + * @since 26.0.0 + */ + ABILITY_RUNTIME_ERROR_CODE_ABILITY_WRAPPER_INVALID = 16000177, + /** + * The type library metadata is invalid, e.g. missing or duplicate main interface. * * @since 26.0.0 */ diff --git a/interfaces/kits/c/ability_runtime/modular_object_extension_context.h b/interfaces/kits/c/ability_runtime/modular_object_extension_context.h index 0007bfc5f0..e9f5199f58 100644 --- a/interfaces/kits/c/ability_runtime/modular_object_extension_context.h +++ b/interfaces/kits/c/ability_runtime/modular_object_extension_context.h @@ -155,6 +155,41 @@ AbilityRuntime_ErrorCode OH_AbilityRuntime_ModObjExtensionContext_StartSelfUIAbi AbilityRuntime_ErrorCode OH_AbilityRuntime_ModObjExtensionContext_TerminateSelf( OH_AbilityRuntime_ModObjExtensionContextHandle context); +/** + * @brief Creates an OHIPCRemoteStub object with callbacks running on the extension's designated thread. + * The requestCallback and destroyCallback are invoked serially on the thread determined by the + * extension's {@link OH_AbilityRuntime_ThreadMode}. After calling + * {@link OH_AbilityRuntime_ModObjExtensionContext_DestroyIPCRemoteStub}, no new requestCallback + * invocations will occur, and any in-flight requestCallback will complete before destroyCallback is invoked. + * + * The caller is responsible for destroying the returned object by calling + * {@link OH_AbilityRuntime_ModObjExtensionContext_DestroyIPCRemoteStub} to avoid memory leaks. + * + * @param context Represents a pointer to a modular object extension ability context. + * @param descriptor Pointer to the descriptor of the OHIPCRemoteStub object to create. It cannot be NULL. + * The string is copied internally during creation, so the caller may release the descriptor + * after this function returns. + * @param requestCallback Callback used to process the data request. It cannot be NULL. + * @param destroyCallback Callback to be invoked when the object is destroyed. It can be NULL. + * @param userData Pointer to the user data. It can be NULL. Must remain valid before the object is destroyed. + * @return Returns the pointer to the OHIPCRemoteStub object created if the operation is successful; + * returns NULL otherwise. + * @since 26.0.0 + */ +OHIPCRemoteStub* OH_AbilityRuntime_ModObjExtensionContext_CreateIPCRemoteStub( + OH_AbilityRuntime_ModObjExtensionContextHandle context, const char *descriptor, + OH_OnRemoteRequestCallback requestCallback, OH_OnRemoteDestroyCallback destroyCallback, void *userData); + +/** + * @brief Destroys an OHIPCRemoteStub object. + * + * @param context Represents a pointer to a modular object extension ability context. + * @param stub Pointer to the OHIPCRemoteStub object to destroy. + * @since 26.0.0 + */ +void OH_AbilityRuntime_ModObjExtensionContext_DestroyIPCRemoteStub( + OH_AbilityRuntime_ModObjExtensionContextHandle context, OHIPCRemoteStub *stub); + #ifdef __cplusplus } #endif diff --git a/interfaces/kits/c/ability_runtime/modular_object_extension_manager.h b/interfaces/kits/c/ability_runtime/modular_object_extension_manager.h index ec518acc91..f1c61396f8 100644 --- a/interfaces/kits/c/ability_runtime/modular_object_extension_manager.h +++ b/interfaces/kits/c/ability_runtime/modular_object_extension_manager.h @@ -276,8 +276,8 @@ AbilityRuntime_ErrorCode OH_AbilityRuntime_GetModObjExtensionInfoByIndex( * {@link ABILITY_RUNTIME_ERROR_CODE_UPPER_RATE_LIMIT} The API call frequency is too high and * exceeds 20 times per second. * {@link ABILITY_RUNTIME_ERROR_CODE_UPPER_CONNECTION_NUMBER_LIMIT} The number of connections exceeds five. - * {@link ABILITY_RUNTIME_ERROR_CODE_INVALID_DISTRIBUTION_TYPE} The distribution type of - * application is invalid. + * {@link ABILITY_RUNTIME_ERROR_CODE_CROSS_APP_IN_PROCESS} Caller and target are not in the + * same application for IN_PROCESS mode. * @since 26.0.0 */ AbilityRuntime_ErrorCode OH_AbilityRuntime_ConnectModularObjectExtensionAbility(AbilityBase_Want *want, diff --git a/interfaces/kits/c/ability_runtime/native_ability_wrapper.h b/interfaces/kits/c/ability_runtime/native_ability_wrapper.h index 6d2c25f429..2e9aecfecf 100644 --- a/interfaces/kits/c/ability_runtime/native_ability_wrapper.h +++ b/interfaces/kits/c/ability_runtime/native_ability_wrapper.h @@ -17,16 +17,15 @@ * @addtogroup AbilityRuntime * @{ * - * @brief Provide the definition of the C interface for the native ability wrapper + * @brief Provides the definition of the C interface for the native ability wrapper * - * @syscap SystemCapability.Ability.AbilityRuntime.Core * @since 26.0.0 */ /** * @file native_ability_wrapper.h * - * @brief Define the native ability wrapper APIs. + * @brief Declares the native ability wrapper APIs. * * @library libability_runtime.so * @kit AbilityKit @@ -45,7 +44,12 @@ extern "C" { #endif -typedef struct NativeAbilityWrapper NativeAbilityWrapper; +/** + * @brief Defines the AbilityRuntime_NativeAbilityWrapper structure type. + * + * @since 26.0.0 + */ +typedef struct AbilityRuntime_NativeAbilityWrapper AbilityRuntime_NativeAbilityWrapper; /** * @brief Get ability instance ID from NativeAbilityWrapper. @@ -61,40 +65,43 @@ typedef struct NativeAbilityWrapper NativeAbilityWrapper; * @since 26.0.0 */ AbilityRuntime_ErrorCode OH_AbilityRuntime_GetAbilityInstanceId( - const NativeAbilityWrapper* nativeAbilityWrapper, char* buffer, const int32_t bufferSize); + const AbilityRuntime_NativeAbilityWrapper* nativeAbilityWrapper, char* buffer, const int32_t bufferSize); /** * @brief Get ability name from NativeAbilityWrapper. * * @param nativeAbilityWrapper The native ability wrapper pointer. * @param buffer A pointer to a buffer that receives the ability name. - * @param bufferSize The length of the buffer. - * @param writeLength The string length actually written to the buffer, - * when returning {@link ABILITY_RUNTIME_ERROR_CODE_NO_ERROR}. + * Pass nullptr to query the ability name length. + * @param bufferSize The length of the buffer. Make sure the buffer has at least one more byte for '\0'. + * @param writeLength Outputs the ability name string length. * @return The error code. * {@link ABILITY_RUNTIME_ERROR_CODE_NO_ERROR} if the operation is successful. - * {@link ABILITY_RUNTIME_ERROR_CODE_PARAM_INVALID} if the nativeAbilityWrapper, buffer, or writeLength is null, - * or the buffer size is less than the minimum buffer size. - * {@link ABILITY_RUNTIME_ERROR_CODE_CONTEXT_NOT_EXIST} if the ability context does not exist. + * {@link ABILITY_RUNTIME_ERROR_CODE_PARAM_INVALID} if the nativeAbilityWrapper or writeLength is null, + * or the buffer is too small for the ability name. + * {@link ABILITY_RUNTIME_ERROR_CODE_ABILITY_WRAPPER_INVALID} if the native ability wrapper is invalid or + * incomplete. * {@link ABILITY_RUNTIME_ERROR_CODE_INTERNAL} inner error. * @since 26.0.0 */ AbilityRuntime_ErrorCode OH_AbilityRuntime_GetAbilityName( - const NativeAbilityWrapper* nativeAbilityWrapper, char* buffer, const int32_t bufferSize, int32_t* writeLength); + const AbilityRuntime_NativeAbilityWrapper *nativeAbilityWrapper, char *buffer, const int32_t bufferSize, + int32_t *writeLength); /** * @brief Get napi_env from NativeAbilityWrapper. * * @param nativeAbilityWrapper The native ability wrapper pointer. - * @param env A pointer to the napi environment. + * @param env A pointer to the receive napi_env value. napi_env is valid until the process terminates. * @return The error code. * {@link ABILITY_RUNTIME_ERROR_CODE_NO_ERROR} if the operation is successful. * {@link ABILITY_RUNTIME_ERROR_CODE_PARAM_INVALID} if the nativeAbilityWrapper or env is null. - * {@link ABILITY_RUNTIME_ERROR_CODE_CONTEXT_NOT_EXIST} if the ability context does not exist. + * {@link ABILITY_RUNTIME_ERROR_CODE_ABILITY_WRAPPER_INVALID} if the native ability wrapper is invalid or + * incomplete. * @since 26.0.0 */ AbilityRuntime_ErrorCode OH_AbilityRuntime_GetEnv( - const NativeAbilityWrapper* nativeAbilityWrapper, napi_env* env); + const AbilityRuntime_NativeAbilityWrapper* nativeAbilityWrapper, napi_env* env); #ifdef __cplusplus } diff --git a/interfaces/kits/native/ability/ability_runtime/ability_context.h b/interfaces/kits/native/ability/ability_runtime/ability_context.h index 81f09f81f4..63a9e8bcd6 100644 --- a/interfaces/kits/native/ability/ability_runtime/ability_context.h +++ b/interfaces/kits/native/ability/ability_runtime/ability_context.h @@ -549,6 +549,11 @@ public: */ virtual ErrCode NotifyCompleteGamePreLaunch() = 0; + virtual ErrCode StartSelfUIAbilityInChildProcess(const AAFwk::Want &want, const std::string &specifiedFlag) + { + return ERR_INVALID_VALUE; + } + protected: bool IsContext(size_t contextTypeId) override { diff --git a/interfaces/kits/native/ability/ability_runtime/ability_context_impl.h b/interfaces/kits/native/ability/ability_runtime/ability_context_impl.h index f1eccc2821..ba2785a93c 100644 --- a/interfaces/kits/native/ability/ability_runtime/ability_context_impl.h +++ b/interfaces/kits/native/ability/ability_runtime/ability_context_impl.h @@ -418,6 +418,8 @@ public: ErrCode StartSelfUIAbilityInCurrentProcess(const AAFwk::Want &want, const std::string &specifiedFlag, const AAFwk::StartOptions &startOptions, bool hasOptions) override; + ErrCode StartSelfUIAbilityInChildProcess(const AAFwk::Want &want, const std::string &specifiedFlag) override; + private: sptr token_ = nullptr; std::shared_ptr abilityInfo_ = nullptr; diff --git a/interfaces/kits/native/ability/native/ability_business_error/ability_business_error.h b/interfaces/kits/native/ability/native/ability_business_error/ability_business_error.h index 86b93349d0..edafb507d9 100644 --- a/interfaces/kits/native/ability/native/ability_business_error/ability_business_error.h +++ b/interfaces/kits/native/ability/native/ability_business_error/ability_business_error.h @@ -218,6 +218,9 @@ enum class AbilityErrorCode { // The specified agentId does not exist. ERROR_CODE_AGENT_ID_NOT_EXIST = 35600001, + // The number of AgentCards in the bundle reaches the limit. + ERROR_CODE_AGENT_CARD_LIST_OUT_OF_RANGE = 35600008, + // Maximum connections from the same caller have been reached. Please disconnect at least one agent extension // beforehand. ERROR_CODE_MAX_CONNECTIONS_REACHED = 35600003, @@ -316,8 +319,18 @@ enum class AbilityErrorCode { ERROR_CODE_SELF_REDIRECTION_DISALLOWED = 16000136, + ERROR_CODE_INTENT_CONNECTION_FAILED = 16000137, + + ERROR_CODE_INTENT_DEVICE_DISCONNECTED = 16000138, + ERROR_CODE_SEND_REQUEST_TO_SYSTEM_FAIL = 16000150, + ERROR_CODE_DELAYED_PROCESS_EXIT_NO_UIABILITY = 16000151, + + ERROR_CODE_DELAYED_PROCESS_EXIT_NOT_PENDING = 16000161, + + ERROR_CODE_DELAYED_PROCESS_EXIT_HAS_OTHER_UIABILITY = 16000162, + // target bundle is not in u1 ERROR_CODE_NO_U1 = 16000204, diff --git a/interfaces/kits/native/ability/native/ability_runtime/js_ability_context.h b/interfaces/kits/native/ability/native/ability_runtime/js_ability_context.h index 191c3d69f5..66128cd722 100644 --- a/interfaces/kits/native/ability/native/ability_runtime/js_ability_context.h +++ b/interfaces/kits/native/ability/native/ability_runtime/js_ability_context.h @@ -85,6 +85,7 @@ public: static napi_value NotifyCancelGamePreLaunch(napi_env env, napi_callback_info info); static napi_value NotifyCompleteGamePreLaunch(napi_env env, napi_callback_info info); static napi_value StartSelf(napi_env env, napi_callback_info info); + static napi_value StartSelfUIAbilityInChildProcess(napi_env env, napi_callback_info info); static napi_value RestartAppWithWindow(napi_env env, napi_callback_info info); static napi_value SetMissionWindowIcon(napi_env env, napi_callback_info info); @@ -176,6 +177,7 @@ private: napi_value OnStartSelfUIAbilityInCurrentProcess(napi_env env, NapiCallbackInfo &info); napi_value OnNotifyCancelGamePreLaunch(napi_env env, NapiCallbackInfo &info); napi_value OnNotifyCompleteGamePreLaunch(napi_env env, NapiCallbackInfo &info); + napi_value OnStartSelfUIAbilityInChildProcess(napi_env env, NapiCallbackInfo &info); napi_value OnSetMissionWindowIcon(napi_env env, NapiCallbackInfo &info); static bool UnWrapWant(napi_env env, napi_value argv, AAFwk::Want& want); diff --git a/interfaces/kits/native/ability/native/ability_runtime/js_ui_ability.h b/interfaces/kits/native/ability/native/ability_runtime/js_ui_ability.h index 43584899e7..8f682f50aa 100644 --- a/interfaces/kits/native/ability/native/ability_runtime/js_ui_ability.h +++ b/interfaces/kits/native/ability/native/ability_runtime/js_ui_ability.h @@ -379,6 +379,19 @@ public: */ int32_t OnCollaborate(WantParams &wantParams) override; + /** + * @brief Execute skill by loading ArkTS script and calling the target function. + * + * @param want Want. + * @param param Skill execute param containing abc path, function name and arguments. + */ + void ExecuteSkill(const AAFwk::Want &want, + const std::shared_ptr ¶m) override; + napi_value LoadSkillFunction(const std::shared_ptr ¶m, + napi_value &outJsObj); + std::vector BuildSkillCallArgs(napi_env env, + const std::shared_ptr ¶m); + /** * @brief Called when startAbility request failed. * @param requestId, the requestId. diff --git a/interfaces/kits/native/ability/native/extension.h b/interfaces/kits/native/ability/native/extension.h index 26c0bc9b17..e51a0f6a98 100644 --- a/interfaces/kits/native/ability/native/extension.h +++ b/interfaces/kits/native/ability/native/extension.h @@ -46,6 +46,17 @@ public: Extension() = default; virtual ~Extension() = default; + /** + * @brief Get the ability handler for this extension. + * Override to provide a custom handler (e.g., shared worker thread). + * Default returns nullptr, meaning the caller should create handler by itself. + * + * @param abilityInfo The ability info for handler creation. + * @return The shared ability handler, or nullptr to use default behavior. + */ + virtual std::shared_ptr GetAbilityHandler( + const std::shared_ptr &abilityInfo); + /** * @brief Init the extension. * @@ -267,6 +278,8 @@ public: virtual void OnExtensionAbilityRequestSuccess(const std::string &requestId, const AppExecFwk::ElementName &element, const std::string &message); + virtual bool HandleExecuteSkill(const AAFwk::Want &want); + std::shared_ptr abilityInfo_ = nullptr; protected: std::shared_ptr handler_ = nullptr; diff --git a/interfaces/kits/native/ability/native/extension_ability_thread.h b/interfaces/kits/native/ability/native/extension_ability_thread.h index 36c55cf967..c11a25e4d7 100644 --- a/interfaces/kits/native/ability/native/extension_ability_thread.h +++ b/interfaces/kits/native/ability/native/extension_ability_thread.h @@ -137,6 +137,8 @@ public: void ScheduleAbilitiesRequestDone(const std::string &requestKey, int32_t resultCode) override; + void ExecuteSkill(const Want &want) override; + void HandleNativeExtensionAttach( const std::shared_ptr &abilityRecord, const std::string &abilityName); @@ -146,6 +148,8 @@ public: const std::shared_ptr &mainRunner, const std::string &abilityName); + int CreateModalUIExtension(const Want &want) override; + private: /** @@ -238,10 +242,14 @@ private: */ void HandleInsightIntent(const Want &want); + void HandleExecuteSkill(const Want &want); + void ScheduleCommandAbilityInner(const Want &want, bool restart, int32_t startId); void ScheduleInsightIntentInner(const Want &want); + void ScheduleSkillExecuteInner(const Want &want); + std::shared_ptr extensionImpl_ = nullptr; std::shared_ptr currentExtension_ = nullptr; static std::shared_ptr contentEmbedEventRunner_; diff --git a/interfaces/kits/native/ability/native/extension_impl.h b/interfaces/kits/native/ability/native/extension_impl.h index 0b2adad32c..3b29825f63 100644 --- a/interfaces/kits/native/ability/native/extension_impl.h +++ b/interfaces/kits/native/ability/native/extension_impl.h @@ -140,6 +140,13 @@ public: */ bool HandleInsightIntent(const Want &want); + /** + * @brief Handle skill execution. + * + * @param want The Want object with skill execute params to handle. + */ + bool HandleExecuteSkill(const Want &want); + void CommandExtensionWindow(const Want &want, const sptr &sessionInfo, AAFwk::WindowCommand winCmd); @@ -164,6 +171,13 @@ public: void ScheduleAbilityRequestSuccess(const std::string &requestId, const AppExecFwk::ElementName &element); + /** + * @brief Create modal UIExtension. + * @param want The want of the modal UIExtension to create. + * @return Returns ERR_OK on success, error code on failure. + */ + int CreateModalUIExtension(const AAFwk::Want &want); + protected: /** * @brief Toggles the lifecycle status of Extension to AAFwk::ABILITY_STATE_INACTIVE. And notifies the application diff --git a/interfaces/kits/native/ability/native/fa_ability_thread.h b/interfaces/kits/native/ability/native/fa_ability_thread.h index eb08357f29..90d4bda7a5 100644 --- a/interfaces/kits/native/ability/native/fa_ability_thread.h +++ b/interfaces/kits/native/ability/native/fa_ability_thread.h @@ -324,6 +324,8 @@ public: */ void CallRequest() override; + void ExecuteSkill(const Want &want) override; + /** * @brief Performs batch operations on the database * @param operations Indicates a list of database operations on the database. diff --git a/interfaces/kits/native/ability/native/insight_intent_callback_interface.h b/interfaces/kits/native/ability/native/insight_intent_callback_interface.h index 48bc82d395..8f5a23c4af 100644 --- a/interfaces/kits/native/ability/native/insight_intent_callback_interface.h +++ b/interfaces/kits/native/ability/native/insight_intent_callback_interface.h @@ -22,6 +22,8 @@ namespace AbilityRuntime { class InsightIntentExecuteCallbackInterface { public: + virtual ~InsightIntentExecuteCallbackInterface() = default; + /** * @brief Process the InsightIntent execute. * @param resultCode, ERR_OK on success, others on failure. diff --git a/interfaces/kits/native/ability/native/js_service_extension.h b/interfaces/kits/native/ability/native/js_service_extension.h index c2371bf1e9..d0cf123671 100644 --- a/interfaces/kits/native/ability/native/js_service_extension.h +++ b/interfaces/kits/native/ability/native/js_service_extension.h @@ -27,6 +27,8 @@ #include "window_manager.h" #endif #include "service_extension.h" +#include "skill/skill_execute_param.h" +#include "skill/skill_execute_result.h" class NativeReference; @@ -135,6 +137,8 @@ public: */ bool HandleInsightIntent(const AAFwk::Want &want) override; + bool HandleExecuteSkill(const AAFwk::Want &want) override; + /** * @brief Called when this extension enters the STATE_STOP state. * @@ -183,6 +187,13 @@ private: void AddLifecycleEventForJSCall(const std::string &eventStr); + void ExecuteSkill(const AAFwk::Want &want, + const std::shared_ptr ¶m); + napi_value LoadSkillFunction(const std::shared_ptr ¶m, + napi_value &outJsObj); + std::vector BuildSkillCallArgs(napi_env env, + const std::shared_ptr ¶m); + bool HasScreenDensityBeenSet(std::shared_ptr resourceManager); JsRuntime& jsRuntime_; diff --git a/interfaces/kits/native/ability/native/modular_object_extension/modular_object_extension.h b/interfaces/kits/native/ability/native/modular_object_extension/modular_object_extension.h index 33e799c2bd..a0d0405692 100644 --- a/interfaces/kits/native/ability/native/modular_object_extension/modular_object_extension.h +++ b/interfaces/kits/native/ability/native/modular_object_extension/modular_object_extension.h @@ -19,6 +19,7 @@ #include "extension_base.h" #include "modular_object_extension_context_impl.h" #include "modular_object_extension_types.h" +#include "modular_object_worker_manager.h" #ifdef __cplusplus @@ -63,6 +64,9 @@ public: void OnDisconnect(const AAFwk::Want &want) override; + std::shared_ptr GetAbilityHandler( + const std::shared_ptr &abilityInfo) override; + private: bool LoadNativeExtensionModule(); bool BuildCWant(const AAFwk::Want &want, AbilityBase_Want &cWant, AbilityBase_Element &element) const; @@ -71,6 +75,7 @@ private: std::shared_ptr moeInstance_; std::shared_ptr moeContext_; + std::string threadKey_; }; } // namespace AbilityRuntime } // namespace OHOS diff --git a/interfaces/kits/native/ability/native/modular_object_extension/modular_object_extension_context_impl.h b/interfaces/kits/native/ability/native/modular_object_extension/modular_object_extension_context_impl.h index 9b53861c0e..baff3582db 100644 --- a/interfaces/kits/native/ability/native/modular_object_extension/modular_object_extension_context_impl.h +++ b/interfaces/kits/native/ability/native/modular_object_extension/modular_object_extension_context_impl.h @@ -16,6 +16,9 @@ #ifndef OHOS_ABILITY_RUNTIME_MODULAR_OBJECT_EXTENSION_CONTEXT_IMPL_H #define OHOS_ABILITY_RUNTIME_MODULAR_OBJECT_EXTENSION_CONTEXT_IMPL_H +#include + +#include "event_handler.h" #include "extension_context.h" #include "start_options.h" #include "want.h" @@ -27,6 +30,10 @@ public: ModularObjectExtensionContext() = default; ~ModularObjectExtensionContext() override = default; + void SetEventHandler(const std::shared_ptr &handler); + + std::shared_ptr GetEventHandler() const; + ErrCode StartSelfUIAbility(const AAFwk::Want &want) const; ErrCode StartSelfUIAbilityWithStartOptions(const AAFwk::Want &want, const AAFwk::StartOptions &startOptions) const; @@ -40,6 +47,9 @@ protected: { return contextTypeId == CONTEXT_TYPE_ID || ExtensionContext::IsContext(contextTypeId); } + +private: + std::weak_ptr handler_; }; } // namespace AbilityRuntime } // namespace OHOS diff --git a/interfaces/kits/native/ability/native/modular_object_worker_manager.h b/interfaces/kits/native/ability/native/modular_object_worker_manager.h new file mode 100644 index 0000000000..8c287a2175 --- /dev/null +++ b/interfaces/kits/native/ability/native/modular_object_worker_manager.h @@ -0,0 +1,53 @@ +/* + * 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_MODULAR_OBJECT_WORKER_MANAGER_H +#define OHOS_ABILITY_RUNTIME_MODULAR_OBJECT_WORKER_MANAGER_H + +#include +#include +#include +#include + +#include "ability_handler.h" +#include "event_handler.h" + +namespace OHOS { +namespace AbilityRuntime { + +struct WorkerEntry { + std::shared_ptr handler; + uint32_t refCount = 0; +}; + +class ModularObjectWorkerManager { +public: + ModularObjectWorkerManager() = default; + ~ModularObjectWorkerManager() = default; + static ModularObjectWorkerManager &GetInstance(); + + std::shared_ptr GetOrCreateWorkerThread(const std::string &threadKey); + void ReleaseWorkerThread(const std::string &threadKey); + uint32_t GenerateInstanceId(); + +private: + std::mutex workerMutex_; + std::unordered_map workerMap_; + std::atomic instanceId_{0}; +}; + +} // namespace AbilityRuntime +} // namespace OHOS +#endif // OHOS_ABILITY_RUNTIME_MODULAR_OBJECT_WORKER_MANAGER_H diff --git a/interfaces/kits/native/ability/native/ui_ability.h b/interfaces/kits/native/ability/native/ui_ability.h index 8b9ab4df77..e5cbffcd71 100644 --- a/interfaces/kits/native/ability/native/ui_ability.h +++ b/interfaces/kits/native/ability/native/ui_ability.h @@ -29,6 +29,7 @@ #include "iability_callback.h" #include "recovery_param.h" #include "resource_config_helper.h" +#include "skill/skill_execute_param.h" #include "want.h" #ifdef SUPPORT_SCREEN @@ -47,6 +48,7 @@ class LifeCycle; class ContinuationHandlerStage; class ContinuationManagerStage; class InsightIntentExecuteParam; +class SkillExecuteParam; struct BaseDelegatorAbilityProperty; struct InsightIntentExecuteResult; using InsightIntentExecutorAsyncCallback = AbilityTransactionCallbackInfo; @@ -61,6 +63,7 @@ class Runtime; using InsightIntentExecuteResult = AppExecFwk::InsightIntentExecuteResult; using InsightIntentExecuteParam = AppExecFwk::InsightIntentExecuteParam; using InsightIntentExecutorAsyncCallback = AppExecFwk::InsightIntentExecutorAsyncCallback; +using SkillExecuteParam = AppExecFwk::SkillExecuteParam; class UIAbility : public AppExecFwk::AbilityContext, public AppExecFwk::ILifeCycle, public AppExecFwk::IAbilityCallback, @@ -704,6 +707,15 @@ public: virtual void OnAbilityRequestSuccess(const std::string &requestId, const AppExecFwk::ElementName &element, const std::string &message); + /** + * @brief Execute skill by loading ArkTS script and calling the target function. + * + * @param want Want. + * @param param Skill execute param containing abc path, function name and arguments. + */ + virtual void ExecuteSkill(const AAFwk::Want &want, + const std::shared_ptr ¶m) {} + protected: class UIAbilityDisplayListener : public OHOS::Rosen::IDisplayInfoChangedListener { public: diff --git a/interfaces/kits/native/ability/native/ui_ability_impl.h b/interfaces/kits/native/ability/native/ui_ability_impl.h index 9f8fd0d950..db9dd2c973 100644 --- a/interfaces/kits/native/ability/native/ui_ability_impl.h +++ b/interfaces/kits/native/ability/native/ui_ability_impl.h @@ -18,6 +18,7 @@ #include "insight_intent_execute_result.h" #include "native_ability_util.h" +#include "skill/skill_execute_param.h" #include "ui_ability.h" namespace OHOS { @@ -177,6 +178,8 @@ public: */ bool HandleExecuteInsightIntentBackground(const AAFwk::Want &want, bool onlyExecuteIntent = false); + bool HandleExecuteSkill(const AAFwk::Want &want, bool onlyExecuteSkill = false); + void SetAbilityRecordId(int32_t abilityRecordId) { if (ability_ != nullptr) { @@ -273,7 +276,7 @@ private: inline void ExecuteInsightIntentRepeateForeground(const Want &want, const std::shared_ptr &executeParam, std::unique_ptr callback); - inline void ExecuteInsightIntentMoveToForeground(const Want &want, + void ExecuteInsightIntentMoveToForeground(const Want &want, const std::shared_ptr &executeParam, std::unique_ptr callback); inline void ExecuteInsightIntentPage(const Want &want, diff --git a/interfaces/kits/native/ability/native/ui_ability_thread.h b/interfaces/kits/native/ability/native/ui_ability_thread.h index d205a46060..07a3c91729 100644 --- a/interfaces/kits/native/ability/native/ui_ability_thread.h +++ b/interfaces/kits/native/ability/native/ui_ability_thread.h @@ -140,6 +140,8 @@ public: void OnExecuteIntent(const Want &want) override; + void ExecuteSkill(const Want &want) override; + /** * @brief create modal UIExtension. * @param want Create modal UIExtension with want object. diff --git a/interfaces/kits/native/ability/native/ui_extension_ability/ui_extension.h b/interfaces/kits/native/ability/native/ui_extension_ability/ui_extension.h index 31a8e1b044..0a0b3772c3 100755 --- a/interfaces/kits/native/ability/native/ui_extension_ability/ui_extension.h +++ b/interfaces/kits/native/ability/native/ui_extension_ability/ui_extension.h @@ -92,6 +92,13 @@ public: void RegisterUiExtensionDelayResultCallback(uint64_t intentId, const sptr &sessionInfo, bool isDecorator = false); + /** + * @brief Create modal UIExtension. + * @param want The want of the modal UIExtension to create. + * @return Returns ERR_OK on success, error code on failure. + */ + virtual int CreateModalUIExtension(const AAFwk::Want &want); + protected: virtual void ForegroundWindow(const AAFwk::Want &want, const sptr &sessionInfo); virtual void BackgroundWindow(const sptr &sessionInfo); @@ -104,6 +111,7 @@ protected: std::map> uiWindowMap_; std::set foregroundWindows_; uint64_t intentId_; + std::shared_ptr handler_ = nullptr; }; } // namespace AbilityRuntime } // namespace OHOS diff --git a/interfaces/kits/native/ability/native/ui_extension_base/ui_extension_context.h b/interfaces/kits/native/ability/native/ui_extension_base/ui_extension_context.h index ed135a2827..e51b6910ea 100755 --- a/interfaces/kits/native/ability/native/ui_extension_base/ui_extension_context.h +++ b/interfaces/kits/native/ability/native/ui_extension_base/ui_extension_context.h @@ -36,7 +36,12 @@ namespace AppExecFwk { class EventHandler; } +namespace Ace { +struct ModalUIExtensionCallbacks; +} + namespace AbilityRuntime { +class UIExtensionModalCallback; using RuntimeTask = std::function; using AbilityConfigUpdateCallback = std::function; using TerminateSelfWithAnimationCallback = std::function; @@ -253,6 +258,28 @@ public: */ ErrCode StartAbilityByType(const std::string &type, AAFwk::WantParams &wantParam, const std::shared_ptr &uiExtensionCallbacks); + + /** + * @brief Create modal UIExtension with app (consistent with AbilityContext). + * @param want The want of the modal UIExtension to create. + * @return Returns ERR_OK on success, error code on failure. + */ + ErrCode CreateModalUIExtensionWithApp(const AAFwk::Want &want); + + /** + * @brief Check if a modal UIExtension with the same component already exists. + * @param want The want of the modal UIExtension to check. + * @return Returns true if exists, false otherwise. + */ + bool IsUIExtensionExist(const AAFwk::Want &want); + + /** + * @brief Erase modal UIExtension from the tracking map. + * @param sessionId The session ID of the modal UIExtension to erase. + * @return Returns ERR_OK on success. + */ + ErrCode EraseUIExtension(int32_t sessionId); + bool IsTerminating(); void SetTerminating(bool state); @@ -327,6 +354,10 @@ private: // ====== Timeout management (only for embeddable mode) ====== std::shared_ptr eventHandler_; + std::map uiExtensionMap_; + std::mutex uiExtensionMutex_; + + Ace::ModalUIExtensionCallbacks SetupModalCallbacks(std::shared_ptr modalCallbackWeak); }; } // namespace AbilityRuntime } // namespace OHOS diff --git a/interfaces/kits/native/ability/native/ui_extension_base/ui_extension_modal_callback.h b/interfaces/kits/native/ability/native/ui_extension_base/ui_extension_modal_callback.h new file mode 100644 index 0000000000..a99e75b165 --- /dev/null +++ b/interfaces/kits/native/ability/native/ui_extension_base/ui_extension_modal_callback.h @@ -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. + */ + +#ifndef OHOS_ABILITY_RUNTIME_UI_EXTENSION_MODAL_CALLBACK_H +#define OHOS_ABILITY_RUNTIME_UI_EXTENSION_MODAL_CALLBACK_H + +#include "hilog_tag_wrapper.h" +#include + +#ifdef SUPPORT_SCREEN +#include "ui_content.h" +#endif // SUPPORT_SCREEN + +namespace OHOS { +namespace AbilityRuntime { + +class UIExtensionContext; + +/** + * @brief Callback class for modal UIExtension lifecycle management in UIExtension context. + * Similar to DialogUIExtensionCallback but does not depend on IAbilityCallback. + * Uses weak_ptr to avoid dangling pointer issues (P1 fix). + */ +class UIExtensionModalCallback { +public: + UIExtensionModalCallback() = default; + ~UIExtensionModalCallback() = default; + + /** + * @brief Set the session ID for this modal UIExtension. + * @param sessionId The session ID. + */ + void SetSessionId(int32_t sessionId) { sessionId_ = sessionId; } + +#ifdef SUPPORT_SCREEN + /** + * @brief Set the UIContent for this modal UIExtension. + * @param uiContent Pointer to the UIContent. + */ + void SetUIContent(Ace::UIContent* uiContent) { uiContent_ = uiContent; } +#endif // SUPPORT_SCREEN + + /** + * @brief Set the UIExtensionContext for this modal UIExtension. + * @param context Weak pointer to the UIExtensionContext (P1 fix: use weak_ptr). + */ + void SetUIExtensionContext(const std::weak_ptr& context) { contextWeak_ = context; } + + /** + * @brief Called when the modal UIExtension is released. + * Will close the modal UIExtension and erase it from context's map. + */ + void OnRelease(); + + /** + * @brief Called when an error occurs in the modal UIExtension. + * Will close the modal UIExtension and erase it from context's map. + */ + void OnError(); + + /** + * @brief Called when the modal UIExtension is destroyed. + * Will only erase it from context's map (UIExtension already closed by system). + */ + void OnDestroy(); + + /** + * @brief Called when the modal UIExtension receives data. + * @param data The received WantParams data. + */ + void OnReceive(const AAFwk::WantParams& data); + +private: + int32_t sessionId_ = 0; +#ifdef SUPPORT_SCREEN + Ace::UIContent* uiContent_ = nullptr; +#endif // SUPPORT_SCREEN + std::weak_ptr contextWeak_; // P1 fix: use weak_ptr instead of raw pointer +}; + +} // namespace AbilityRuntime +} // namespace OHOS + +#endif // OHOS_ABILITY_RUNTIME_UI_EXTENSION_MODAL_CALLBACK_H diff --git a/interfaces/kits/native/appkit/ability_runtime/context/ability_native_thread.h b/interfaces/kits/native/appkit/ability_runtime/context/ability_native_thread.h index cfea6d8af6..0fd6563ebb 100644 --- a/interfaces/kits/native/appkit/ability_runtime/context/ability_native_thread.h +++ b/interfaces/kits/native/appkit/ability_runtime/context/ability_native_thread.h @@ -30,7 +30,7 @@ class NativeReference; #ifdef __cplusplus extern "C" { #endif -struct NativeAbilityWrapper { +struct AbilityRuntime_NativeAbilityWrapper { std::string instanceId; std::string abilityName; napi_env env = nullptr; @@ -78,13 +78,13 @@ public: * @brief Post the ability wrapper to the native thread. * @param nativeAbilityWrapper The pointer to the native ability wrapper. */ - void PostAbility(const NativeAbilityWrapper* nativeAbilityWrapper); + void PostAbility(const AbilityRuntime_NativeAbilityWrapper* nativeAbilityWrapper); /** * @brief Notify the native module that the ability is being destroyed. * @param nativeAbilityWrapper The pointer to the native ability wrapper. */ - void DestroyAbility(const NativeAbilityWrapper* nativeAbilityWrapper); + void DestroyAbility(const AbilityRuntime_NativeAbilityWrapper* nativeAbilityWrapper); /** * @brief Notify the native module that the process is exiting. @@ -95,8 +95,8 @@ public: private: void *moduleHandle_ = nullptr; std::function ohMainFun_; - std::function postAbilityFunc_; - std::function destroyAbilityFunc_; + std::function postAbilityFunc_; + std::function destroyAbilityFunc_; std::function notifyProcessExitFunc_; std::thread nativeThread_; }; diff --git a/interfaces/kits/native/appkit/ability_runtime/context/application_context.h b/interfaces/kits/native/appkit/ability_runtime/context/application_context.h index d9fb3c276f..11e30d075f 100644 --- a/interfaces/kits/native/appkit/ability_runtime/context/application_context.h +++ b/interfaces/kits/native/appkit/ability_runtime/context/application_context.h @@ -105,10 +105,10 @@ public: bool CreateNativeThread(const AAFwk::NativeAbilityMetaData &metaData, const std::string &bundleName, const std::string &moduleName); std::shared_ptr GetNativeThread(); - void AddNativeAbility(const std::string &instanceId, std::shared_ptr wrapper); - std::shared_ptr GetNativeAbility(const std::string &instanceId); + void AddNativeAbility(const std::string &instanceId, std::shared_ptr wrapper); + std::shared_ptr GetNativeAbility(const std::string &instanceId); void RemoveNativeAbility(const std::string &instanceId); - void PostAbility(const std::string &instanceId, std::shared_ptr wrapper); + void PostAbility(const std::string &instanceId, std::shared_ptr wrapper); void DestroyAbility(const std::string &instanceId); void NotifyProcessExit(); @@ -167,6 +167,9 @@ public: void KillProcessBySelf(const bool clearPageStack = false); int32_t GetProcessRunningInformation(AppExecFwk::RunningProcessInfo &info); int32_t RestartApp(const AAFwk::Want& want); + int32_t EnableDelayedProcessExit(); + int32_t DisableDelayedProcessExit(); + int32_t StartSelfUIAbility(const AAFwk::Want &want); void AttachContextImpl(const std::shared_ptr &contextImpl); @@ -233,6 +236,7 @@ protected: } private: + bool IsDelayedProcessExitPending(); std::vector> GetInteropCallbacks(); private: @@ -261,8 +265,10 @@ private: // Native Module related members std::shared_ptr abilityNativeThread_; - std::unordered_map> nativeAbilities_; + std::unordered_map> nativeAbilities_; std::mutex nativeMutex_; + std::mutex delayedProcessExitStateLock_; + bool delayedProcessExitEnabled_ = false; }; } // namespace AbilityRuntime } // namespace OHOS diff --git a/interfaces/kits/native/appkit/ability_runtime/context/js_application_context_utils.h b/interfaces/kits/native/appkit/ability_runtime/context/js_application_context_utils.h index bda5377be2..40ef840719 100644 --- a/interfaces/kits/native/appkit/ability_runtime/context/js_application_context_utils.h +++ b/interfaces/kits/native/appkit/ability_runtime/context/js_application_context_utils.h @@ -106,6 +106,10 @@ public: napi_value OnSetFontSizeScale(napi_env env, NapiCallbackInfo& info); napi_value OnGetAllWindowStages(napi_env env, NapiCallbackInfo& info); napi_value OnGetUIAbilityByInstanceId(napi_env env, NapiCallbackInfo& info); + napi_value OnEnableDelayedProcessExit(napi_env env, NapiCallbackInfo& info); + napi_value OnDisableDelayedProcessExit(napi_env env, NapiCallbackInfo& info); + napi_value OnStartSelfUIAbility(napi_env env, NapiCallbackInfo& info); + static napi_value GetCacheDir(napi_env env, napi_callback_info info); static napi_value GetTempDir(napi_env env, napi_callback_info info); static napi_value GetResourceDir(napi_env env, napi_callback_info info); @@ -137,6 +141,10 @@ public: static napi_value CreateDisplayContext(napi_env env, napi_callback_info info); static napi_value GetAllWindowStages(napi_env env, napi_callback_info info); static napi_value GetUIAbilityByInstanceId(napi_env env, napi_callback_info info); + static napi_value EnableDelayedProcessExit(napi_env env, napi_callback_info info); + static napi_value DisableDelayedProcessExit(napi_env env, napi_callback_info info); + static napi_value StartSelfUIAbility(napi_env env, napi_callback_info info); + protected: std::weak_ptr applicationContext_; diff --git a/interfaces/kits/native/appkit/app/dump_runtime_helper.h b/interfaces/kits/native/appkit/app/dump_runtime_helper.h index e4862af838..9b50d39516 100644 --- a/interfaces/kits/native/appkit/app/dump_runtime_helper.h +++ b/interfaces/kits/native/appkit/app/dump_runtime_helper.h @@ -78,6 +78,8 @@ private: void DumpJsHeapGc(const std::unique_ptr &runtime, const OHOS::AppExecFwk::JsHeapDumpInfo &info); void DumpKmpKotlinHeap(const OHOS::AppExecFwk::MemDumpInfo &info); + void DumpJsvmHeap(const OHOS::AppExecFwk::MemDumpInfo &info); + void DumpArkwebJsHeap(const OHOS::AppExecFwk::MemDumpInfo &info); }; } // namespace AppExecFwk } // namespace OHOS diff --git a/interfaces/kits/native/appkit/app/main_thread.h b/interfaces/kits/native/appkit/app/main_thread.h index fa97432f5b..e529a6cec5 100644 --- a/interfaces/kits/native/appkit/app/main_thread.h +++ b/interfaces/kits/native/appkit/app/main_thread.h @@ -446,8 +446,8 @@ private: void HandleSchedulePrepareTerminate(const std::string &moduleName); - void PreloadModule(const AppExecFwk::HapModuleInfo &entryHapModuleInfo, - const std::unique_ptr& runtime); + void PreloadModule(const BundleInfo &bundleInfo, const AppLaunchData &appLaunchData, + const AppExecFwk::HapModuleInfo &entryHapModuleInfo, const std::unique_ptr &runtime); void ProcessMainAbility(const AbilityInfo &info, const std::unique_ptr& runtime); diff --git a/interfaces/kits/native/appkit/app_startup/ext_native_startup_manager.h b/interfaces/kits/native/appkit/app_startup/ext_native_startup_manager.h index baf176dbb2..f1a3b3f411 100644 --- a/interfaces/kits/native/appkit/app_startup/ext_native_startup_manager.h +++ b/interfaces/kits/native/appkit/app_startup/ext_native_startup_manager.h @@ -30,6 +30,7 @@ namespace OHOS { namespace AbilityRuntime { enum class SchedulerPhase { + None, PostLaunchApplication, }; class ExtNativeStartupManager : public NoCopyable { @@ -54,6 +55,7 @@ private: ~ExtNativeStartupManager() override; std::mutex mutex_; + SchedulerPhase phaseFlag_ = SchedulerPhase::None; std::unordered_map>> extNativeStartupTasks_; }; } // namespace AbilityRuntime diff --git a/interfaces/kits/native/appkit/dfr/appfreeze_inner.h b/interfaces/kits/native/appkit/dfr/appfreeze_inner.h index cbd8aabdbf..788ba8ee8f 100644 --- a/interfaces/kits/native/appkit/dfr/appfreeze_inner.h +++ b/interfaces/kits/native/appkit/dfr/appfreeze_inner.h @@ -20,6 +20,7 @@ #include #include #include +#include #include "refbase.h" #include "singleton.h" @@ -84,6 +85,7 @@ private: bool GetProcessStartTime(pid_t tid, unsigned long long &startTime); bool ReadFdToString(int fd, std::string& content); int TransformHicollieFaultNumber(const std::string& faultName); + std::string GetMainStackDump(int32_t pid); static std::mutex singletonMutex_; static std::shared_ptr instance_; @@ -95,6 +97,11 @@ private: std::list handlinglist_; std::shared_ptr appfreezeInnerTaskHandler_; std::shared_ptr application_ = nullptr; + + std::mutex mainStackMutex_; + std::condition_variable mainStackCv_; + std::string lastMainStack_ = ""; + std::atomic lastMainStackTime_ = 0; }; class MainHandlerDumper : public Dumper { diff --git a/js_environment/test/unittest/source_map_test/BUILD.gn b/js_environment/test/unittest/source_map_test/BUILD.gn index a0664acfc5..90ff9f49ab 100644 --- a/js_environment/test/unittest/source_map_test/BUILD.gn +++ b/js_environment/test/unittest/source_map_test/BUILD.gn @@ -20,9 +20,10 @@ ohos_unittest("source_map_test") { module_out_path = module_output_path sources = [ "source_map_test.cpp" ] + deps = [ "${ability_runtime_path}/js_environment/frameworks/js_environment:js_environment" ] + external_deps = [ "ability_base:want", - "ability_runtime:js_environment", "bundle_framework:appexecfwk_base", "c_utils:utils", "eventhandler:libeventhandler", diff --git a/services/abilitymgr/BUILD.gn b/services/abilitymgr/BUILD.gn index bb4bc14776..c52ed62322 100644 --- a/services/abilitymgr/BUILD.gn +++ b/services/abilitymgr/BUILD.gn @@ -41,6 +41,7 @@ config("abilityms_config") { "include/extension_record", "include/foreground_app_connection_manager", "include/insight_intent", + "include/skill", "include/mission/", "include/modular_object/", "include/rdb/", diff --git a/services/abilitymgr/abilitymgr.gni b/services/abilitymgr/abilitymgr.gni index c6144b0474..eee990049b 100644 --- a/services/abilitymgr/abilitymgr.gni +++ b/services/abilitymgr/abilitymgr.gni @@ -149,6 +149,13 @@ abilityms_files = [ "src/insight_intent/insight_intent_event_mgr.cpp", "src/insight_intent/insight_intent_sys_event_receiver.cpp", + #skill execution + "src/skill/skill_execute_callback_proxy.cpp", + "src/skill/skill_execute_callback_stub.cpp", + "src/skill/skill_execute_param.cpp", + "src/skill/skill_execute_result.cpp", + "src/skill/skill_execute_manager.cpp", + #sa interceptor "src/sa_interceptor_manager.cpp", diff --git a/services/abilitymgr/include/ability_connect_manager.h b/services/abilitymgr/include/ability_connect_manager.h index d128c2443b..43283802eb 100644 --- a/services/abilitymgr/include/ability_connect_manager.h +++ b/services/abilitymgr/include/ability_connect_manager.h @@ -33,6 +33,8 @@ #include "event_report.h" #include "extension_config.h" #include "extension_running_info.h" +#include "modular_object_extension_info.h" +#include "modular_object_manager.h" #include "connection_record.h" #include "element_name.h" #include "res_sched_util.h" @@ -722,6 +724,8 @@ private: */ void GetOrCreateServiceRecord(const AbilityRequest &abilityRequest, const bool isCreatedByConnect, std::shared_ptr &targetAbilityRecord, bool &isLoadedAbility); + int32_t HandleExtensionSetup(const AbilityRequest &abilityRequest, + std::shared_ptr &targetService, const std::string &serviceKey); void SetServiceAfterNewCreate(const AbilityRequest &abilityRequest, BaseExtensionRecord &targetService); /** @@ -820,6 +824,7 @@ private: void ProcessEliminateAbilityRecord(std::shared_ptr eliminateRecord); static std::string GetServiceKey(const std::shared_ptr &service); static std::string GetServiceKey(const AbilityRequest &abilityRequest); + int32_t CheckModularObjectLimits(const AbilityRequest &abilityRequest); void SetExtensionLoadParam(AbilityRuntime::LoadParam &loadParam, std::shared_ptr abilityRecord); diff --git a/services/abilitymgr/include/ability_event_handler.h b/services/abilitymgr/include/ability_event_handler.h index 5e52d5cbf0..a3a3f198a8 100644 --- a/services/abilitymgr/include/ability_event_handler.h +++ b/services/abilitymgr/include/ability_event_handler.h @@ -47,6 +47,7 @@ private: void ProcessForegroundTimeOut(const EventWrap &event, bool isHalf = false); void ProcessShareDataTimeOut(int64_t uniqueId); void ProcessConnectTimeOut(const EventWrap &event, bool isHalf = false); + void ProcessSkillExecuteTimeOut(int64_t requestCodeSeq); private: std::weak_ptr server_; }; diff --git a/services/abilitymgr/include/ability_manager_proxy.h b/services/abilitymgr/include/ability_manager_proxy.h index 7f632be092..5f555c75af 100644 --- a/services/abilitymgr/include/ability_manager_proxy.h +++ b/services/abilitymgr/include/ability_manager_proxy.h @@ -927,8 +927,8 @@ public: * @return Returns ERR_OK on success, others on failure. */ virtual int StartAbilityByCall(const Want &want, const sptr &connect, - const sptr &callerToken, int32_t accountId = DEFAULT_INVAL_VALUE, - bool isSilent = false, bool promotePriority = false, bool isVisible = false) override; + const sptr &callerToken, int32_t accountId = DEFAULT_INVAL_VALUE, bool isSilent = false, + bool promotePriority = false, bool isVisible = false, uint64_t specifiedFullTokenId = 0) override; /** * Start Ability for prelauch. @@ -953,7 +953,8 @@ public: */ virtual int StartAbilityByCallWithErrMsg(const Want &want, const sptr &connect, const sptr &callerToken, int32_t accountId, std::string &errMsg, - bool isSilent = false, bool promotePriority = false, bool isVisible = false) override; + bool isSilent = false, bool promotePriority = false, + bool isVisible = false, uint64_t specifiedFullTokenId = 0) override; /** * CallRequestDone, after invoke callRequest, ability will call this interface to return callee. @@ -1785,6 +1786,21 @@ public: virtual int32_t UpdateAssociateConfigList(const std::map>& configs, const std::list& exportConfigs, int32_t flag) override; + virtual int32_t ExecuteInAppSkill(const std::string &bundleName, const std::string &moduleName, + const std::string &skillName, const std::string &arkTSPath = "", + const std::string &funcName = "", + const std::shared_ptr &skillArgs = nullptr, + const sptr &callback = nullptr) override; + + virtual int32_t ExecuteInAppSkillWithTokenId(const AppExecFwk::SkillExecuteRequest &request, + const sptr &callback) override; + + virtual int32_t QuerySkillType(const std::string &bundleName, const std::string &moduleName, + const std::string &skillName, int32_t &skillType) override; + + virtual int32_t ExecuteSkillDone(const sptr &token, const std::string &requestCode, + int32_t resultCode, const AppExecFwk::SkillExecuteResult &result) override; + /** * Set keep-alive flag for application under a specific user. * @param bundleName Bundle name. @@ -2079,6 +2095,15 @@ public: */ virtual int32_t SetGamePreLaunchCompleteTime(int32_t userId, int64_t completeTime) override; + /** + * Start Self UIAbility In Child Process. + * @param want Ability want. + * @param specifiedFlag specified flag. + * @return Returns ERR_OK on success, others on failure. + */ + virtual ErrCode StartSelfUIAbilityInChildProcess(const Want &want, const std::string &specifiedFlag, + sptr callerToken) override; + /** * Check if the app is restart-limited. * @return Returns true on being limited. @@ -2140,6 +2165,14 @@ public: std::unordered_set &userLockedBundleList) override; virtual int32_t SetAppRecoveryFlag(const sptr& token, int flag)override; + + /** + * StartSelfUIAbility from ApplicationContext and force launch in current process. + * + * @param want, the want of the ability to start. + * @return Returns ERR_OK on success, others on failure. + */ + virtual int StartSelfUIAbilityByAppContext(const Want &want) override; private: template int GetParcelableInfos(MessageParcel &reply, std::vector &parcelableInfos); diff --git a/services/abilitymgr/include/ability_manager_service.h b/services/abilitymgr/include/ability_manager_service.h index 8de07bb3ba..6f182d179c 100644 --- a/services/abilitymgr/include/ability_manager_service.h +++ b/services/abilitymgr/include/ability_manager_service.h @@ -83,6 +83,7 @@ namespace AbilityRuntime { class IStatusBarDelegate; struct ExtractInsightIntentGenericInfo; struct LinkIntentParamMapping; +struct ExecuteIntentCommonOptions; } namespace Rosen { class FocusChangeInfo; @@ -216,6 +217,15 @@ public: virtual ErrCode StartSelfUIAbilityInCurrentProcess(const Want &want, const std::string &specifiedFlag, const AAFwk::StartOptions &startOptions, bool hasOptions, sptr callerToken) override; + /** + * Start Self UIAbility In Child Process. + * @param want Ability want. + * @param specifiedFlag specified flag. + * @return Returns ERR_OK on success, others on failure. + */ + virtual ErrCode StartSelfUIAbilityInChildProcess( + const Want &want, const std::string &specifiedFlag, sptr callerToken) override; + /** * StartAbilityWithSpecifyTokenIdInner with want and specialId, send want to ability manager service. * @@ -1350,8 +1360,8 @@ public: * @return Returns ERR_OK on success, others on failure. */ virtual int StartAbilityByCall(const Want &want, const sptr &connect, - const sptr &callerToken, int32_t accountId = DEFAULT_INVAL_VALUE, - bool isSilent = false, bool promotePriority = false, bool isVisible = false) override; + const sptr &callerToken, int32_t accountId = DEFAULT_INVAL_VALUE, bool isSilent = false, + bool promotePriority = false, bool isVisible = false, uint64_t specifiedFullTokenId = 0) override; /** * Start Ability, connect session with common ability. @@ -1368,7 +1378,8 @@ public: */ virtual int StartAbilityByCallWithErrMsg(const Want &want, const sptr &connect, const sptr &callerToken, int32_t accountId, std::string &errMsg, - bool isSilent = false, bool promotePriority = false, bool isVisible = false) override; + bool isSilent = false, bool promotePriority = false, + bool isVisible = false, uint64_t specifiedFullTokenId = 0) override; /** * Start Ability for prelauch. @@ -1413,6 +1424,7 @@ public: void HandleForegroundTimeOut(int64_t abilityRecordId, bool isHalf = false, bool isExtension = false); void HandleConnectTimeOut(int64_t abilityRecordId, bool isHalf = false); void HandleShareDataTimeOut(int64_t uniqueId); + void HandleSkillExecuteTimeOut(int64_t requestCodeSeq); int32_t GetShareDataPairAndReturnData(std::shared_ptr abilityRecord, const int32_t &resultCode, const int32_t &uniqueId, WantParams &wantParam); @@ -1441,7 +1453,7 @@ public: int StartAbilityForOptionWrap(const Want &want, const StartOptions &startOptions, const sptr &callerToken, bool isPendingWantCaller, int32_t userId = DEFAULT_INVAL_VALUE, int requestCode = DEFAULT_INVAL_VALUE, bool isStartAsCaller = false, uint32_t callerTokenId = 0, - bool isImplicit = false, bool isCallByShortcut = false); + bool isImplicit = false, bool isCallByShortcut = false, bool isCallByDelayed = false); int StartAbilityForOptionInner( const Want &want, @@ -1453,7 +1465,8 @@ public: bool isStartAsCaller = false, uint32_t specifyTokenId = 0, bool isImplicit = false, - bool isCallByShortcut = false); + bool isCallByShortcut = false, + bool isCallByDelayed = false); int ImplicitStartAbility( const Want &want, @@ -1491,14 +1504,16 @@ public: int requestCode = DEFAULT_INVAL_VALUE, bool isImplicit = false, bool isAppCloneSelector = false, - uint32_t callerAccessTokenId = 0); + uint32_t callerAccessTokenId = 0, + sptr callback = nullptr); int ImplicitStartAbilityAsCaller( const Want &want, const sptr &callerToken, sptr asCallerSourceToken, int32_t userId = DEFAULT_INVAL_VALUE, - int requestCode = DEFAULT_INVAL_VALUE); + int requestCode = DEFAULT_INVAL_VALUE, + sptr callback = nullptr); /** * @brief called when the module's onAcceptWant done to notify ability mgr to continue @@ -2177,10 +2192,11 @@ public: int32_t OnExecuteIntent(AbilityRequest &abilityRequest, std::shared_ptr &targetRecord); int32_t StartAbilityWithInsightIntent(const Want &want, int32_t userId = DEFAULT_INVAL_VALUE, - int requestCode = DEFAULT_INVAL_VALUE); + int requestCode = DEFAULT_INVAL_VALUE, uint64_t specifiedFullTokenId = 0); - int32_t StartAbilityByCallWithInsightIntent(const Want &want, const sptr &callerToken, - const InsightIntentExecuteParam ¶m, int32_t userId = DEFAULT_INVAL_VALUE); + int32_t StartAbilityByCallWithInsightIntent(const Want &want, + const sptr &callerToken, const InsightIntentExecuteParam ¶m, + int32_t userId = DEFAULT_INVAL_VALUE, uint64_t specifiedFullTokenId = 0); int32_t ExecuteIntentForDistributed(const Want &want, const std::string &srcDeviceId, uint64_t requestCode, uint64_t specifiedFullTokenId = 0) override; @@ -2202,6 +2218,26 @@ public: int32_t ExecuteInsightIntentDone(const sptr &token, uint64_t intentId, const InsightIntentExecuteResult &result) override; + int32_t ExecuteInAppSkill(const std::string &bundleName, const std::string &moduleName, + const std::string &skillName, const std::string &arkTSPath = "", + const std::string &funcName = "", + const std::shared_ptr &skillArgs = nullptr, + const sptr &callback = nullptr) override; + + int32_t ExecuteInAppSkillWithTokenId(const AppExecFwk::SkillExecuteRequest &request, + const sptr &callback) override; + + int32_t StartAbilityByCallWithSkill(const Want &want, + const sptr &callerToken, int32_t userId = DEFAULT_INVAL_VALUE); + + int32_t StartExtensionAbilityWithSkill(const Want &want, int32_t userId); + + int32_t ExecuteSkillDone(const sptr &token, const std::string &requestCode, + int32_t resultCode, const AppExecFwk::SkillExecuteResult &result) override; + + int32_t QuerySkillType(const std::string &bundleName, const std::string &moduleName, + const std::string &skillName, int32_t &skillType) override; + /** * @brief Open file by uri. * @param uri The file uri. @@ -2472,7 +2508,7 @@ public: int StartUIAbilityForOptionWrap(const Want &want, const StartOptions &options, sptr callerToken, bool isPendingWantCaller, int32_t userId, int requestCode, uint32_t callerTokenId = 0, bool isImplicit = false, - bool isCallByShortcut = false); + bool isCallByShortcut = false, bool isCallByDelayed = false); /** * KillProcessForPermissionUpdate, call KillProcessForPermissionUpdate() through proxy object, @@ -2657,6 +2693,8 @@ public: static constexpr uint32_t FOREGROUND_HALF_TIMEOUT_MSG = 9; static constexpr uint32_t CONNECT_TIMEOUT_MSG = 10; static constexpr uint32_t CONNECT_HALF_TIMEOUT_MSG = 11; + static constexpr uint32_t SKILL_EXECUTE_TIMEOUT_MSG = 12; + static constexpr uint32_t START_SELF_TIMEOUT_MSG = 13; static constexpr uint32_t MIN_DUMP_ARGUMENT_NUM = 2; static constexpr uint32_t MAX_WAIT_SYSTEM_UI_NUM = 600; @@ -2815,6 +2853,13 @@ protected: virtual int32_t GetUserLockedBundleList(int32_t userId, std::unordered_set &userLockedBundleList) override; + /** + * StartSelfUIAbility from ApplicationContext and force launch in current process. + * + * @param want, the want of the ability to start. + * @return Returns ERR_OK on success, others on failure. + */ + virtual int StartSelfUIAbilityByAppContext(const Want &want) override; private: int GetTopAbilityInner(sptr &token, uint64_t displayId = 0); @@ -2860,6 +2905,14 @@ private: * @return Returns ERR_OK on success, error code on failure. */ int32_t CheckAndSubmitAutoStartupStatusBarTask(std::shared_ptr abilityRecord); + + /** + * @brief Check whether caller triggers distributed intent flood attack. + * @param callerUid Calling uid. + * @return Returns true if flood attack detected. + */ + bool IsFloodAttackByCallerUid(int32_t callerUid); + /** * initialization of ability manager service. * @@ -3226,7 +3279,13 @@ private: ErrCode IsUIAbilityAlreadyExist(const Want &want, const std::string &specifiedFlag, int32_t appIndex, const std::string &instanceKey, AppExecFwk::LaunchMode launchMode); - + + ErrCode IsSpecifiedUIAbilityAlreadyExist(const Want &want, const std::string &specifiedFlag, + int32_t appIndex, const std::string &instanceKey); + + ErrCode CheckStartSelfUIAbilityInChildProcess(const Want &want, const std::string &specifiedFlag, + const std::shared_ptr &callerRecord, AppExecFwk::AbilityInfo &abilityInfo); + bool IsAppCloneOrMultiInstance(const Want &want, const std::shared_ptr callerRecord, int32_t &appIndex, const std::string &callerInstanceKey); @@ -3346,7 +3405,7 @@ private: }; int32_t SignRestartAppFlag(const SignRestartAppFlagParam ¶m); - int32_t CheckRestartAppWant(const AAFwk::Want &want, int32_t appIndex, int32_t userId); + int32_t CheckRestartAppWant(const AppExecFwk::ElementName &elementName, int32_t appIndex, int32_t userId); int32_t CheckDebugAssertPermission(); bool VerifySameAppOrAppIdentifierAllowListPermission(const AbilityRequest &abilityRequest); @@ -3434,6 +3493,13 @@ private: bool HandleExecuteSAInterceptor(const Want &want, sptr callerToken, AbilityRequest &abilityRequest, int32_t &result); + int StartAbilityDelayed(StartAbilityWrapParam ¶m); + + int CheckDelayedStartBelongToCaller(const Want &want, const AppExecFwk::RunningProcessInfo &processInfo); + + int StartAbilityDelayedInner(const Want &want, const AppExecFwk::RunningProcessInfo &processInfo, + int32_t callingPid); + bool controllerIsAStabilityTest_ = false; bool isParamStartAbilityEnable_ = false; // Component StartUp rule switch @@ -3545,11 +3611,18 @@ private: void SetAppDeathRecipient(const sptr& abilityToken); void HandleAppDiedForRecovery(const sptr& remote, const AbilityInfo& abilityInfo, int32_t pid, int32_t uid, int32_t userId); + int32_t AtomicServicePreprocess(const Want &want); void startRecoveryMgr(); int getAppRecoveryFlag(const sptr &token); void HandleRecoveryRecipient(const std::shared_ptr& abilityRecord, const sptr& token); int32_t SetAppRecoveryFlag(const sptr& token, int flag) override; + int32_t ExecuteIntentCommon(const sptr &callerToken, + const std::shared_ptr ¶m, const std::string &callerBundleName, + const AbilityRuntime::ExecuteIntentCommonOptions &infos); + void GetCallerUidAndToken(const std::string &bundleName, int32_t userId, + int32_t &callerUid, uint32_t &accessToken); + #ifdef BGTASKMGR_CONTINUOUS_TASK_ENABLE std::shared_ptr bgtaskObserver_; #endif @@ -3589,6 +3662,10 @@ private: ffrt::mutex shouldBlockAllAppStartMutex_; mutable ffrt::mutex timeoutMapLock_; std::mutex whiteListMutex_; + ffrt::mutex delayedStartPidsLock_; + std::unordered_set delayedStartPids_; + std::mutex floodAttackMutex_; + std::unordered_map> floodAttackStatistics_; std::mutex prepareTermiationCallbackMutex_; std::map> prepareTermiationCallbacks_; diff --git a/services/abilitymgr/include/ability_manager_stub.h b/services/abilitymgr/include/ability_manager_stub.h index e7169813e6..223903d47c 100644 --- a/services/abilitymgr/include/ability_manager_stub.h +++ b/services/abilitymgr/include/ability_manager_stub.h @@ -442,9 +442,15 @@ private: int32_t ExitKioskModeInner(MessageParcel &data, MessageParcel &reply); int32_t GetKioskStatusInner(MessageParcel &data, MessageParcel &reply); int32_t StartSelfUIAbilityInCurrentProcessInner(MessageParcel &data, MessageParcel &reply); + int32_t StartSelfUIAbilityInChildProcessInner(MessageParcel &data, MessageParcel &reply); int32_t IsRestartAppLimitInner(MessageParcel &data, MessageParcel &reply); int32_t QuerySelfModularObjectExtensionInfosInner(MessageParcel &data, MessageParcel &reply); int32_t GetUserLockedBundleListInner(MessageParcel &data, MessageParcel &reply); + int32_t ExecuteInAppSkillInner(MessageParcel &data, MessageParcel &reply); + int32_t ExecuteInAppSkillWithTokenIdInner(MessageParcel &data, MessageParcel &reply); + int32_t ExecuteSkillDoneWithTokenInner(MessageParcel &data, MessageParcel &reply); + int32_t QuerySkillTypeInner(MessageParcel &data, MessageParcel &reply); + int32_t StartSelfUIAbilityByAppContextInner(MessageParcel &data, MessageParcel &reply); }; } // namespace AAFwk } // namespace OHOS diff --git a/services/abilitymgr/include/ability_record.h b/services/abilitymgr/include/ability_record.h index c3aa4d7d60..21a8b9e23f 100644 --- a/services/abilitymgr/include/ability_record.h +++ b/services/abilitymgr/include/ability_record.h @@ -104,7 +104,7 @@ public: * foreground the ability. * */ - void ForegroundAbility(uint32_t sceneFlag = 0, bool hasLastWant = false); + void ForegroundAbility(uint32_t sceneFlag = 0); void ForegroundUIExtensionAbility(uint32_t sceneFlag = 0); /** @@ -360,6 +360,28 @@ public: * */ Want GetWant() const; + std::string GetAbilityName() const; + std::string GetBundleName() const; + std::string GetModuleName() const; + std::string GetStringParam(const std::string &key) const; + int GetIntParam(const std::string &key, int defaultValue) const; + bool GetBoolParam(const std::string &key, bool defaultValue) const; + bool HasParameter(const std::string &key) const; + + inline const std::string &GetInfoAbilityName() const + { + return abilityInfo_.name; + } + + inline const std::string &GetInfoBundleName() const + { + return abilityInfo_.bundleName; + } + + inline const std::string &GetInfoModuleName() const + { + return abilityInfo_.moduleName; + } /** * remove signature info of want. @@ -747,41 +769,6 @@ public: void NotifyAbilityRequestSuccess(const std::string &requestId, const AppExecFwk::ElementName &element); void NotifyAbilitiesRequestDone(const std::string &requestKey, int32_t resultCode); - inline void SetLaunchWant(std::shared_ptr launchWant) - { - launchWant_ = launchWant; - } - - inline std::shared_ptr GetLaunchWant() const - { - return launchWant_; - } - - inline void SetLastWant(std::shared_ptr lastWant) - { - lastWant_ = lastWant; - } - - inline bool HasLastWant() const - { - return lastWant_ != nullptr; - } - - inline void SetBackgroundDrivenFlag(bool isLastWantBackgroundDriven) - { - isLastWantBackgroundDriven_.store(isLastWantBackgroundDriven); - } - - inline void SetOnNewWantSkipScenarios(int32_t scenarios) - { - scenarios_.store(scenarios); - } - - inline int32_t GetOnNewWantSkipScenarios() const - { - return scenarios_.load(); - } - inline void SetPreloadStart(bool isPreloadStart) { isPreloadStart_.store(isPreloadStart); @@ -1005,21 +992,17 @@ protected: std::shared_ptr callContainer_ = nullptr; // new version std::shared_ptr connectWant_ = nullptr; std::shared_ptr saCallerInfo_ = nullptr; - std::shared_ptr launchWant_ = nullptr; - std::shared_ptr lastWant_ = nullptr; std::shared_ptr uiAbilityProperty_ = nullptr; LaunchDebugInfo launchDebugInfo_; WindowConfig windowConfig_; - int64_t startTime_ = 0; // records first time of ability start + std::atomic_int64_t startTime_ = 0; // records first time of ability start int64_t restartTime_ = 0; // the time of last trying restart std::atomic pendingState_ = AbilityState::INITIAL; // pending life state std::atomic abilityVisibilityState_ = AbilityVisibilityState::INITIAL; std::atomic_bool isPrepareTerminateAbilityCalled_ = false; std::atomic_bool isPrepareTerminateAbilityDone_ = false; - std::atomic_bool isLastWantBackgroundDriven_ = false; - std::atomic scenarios_ = 0; std::atomic isPreloaded_ = false; std::atomic isFrozenByPreload_ = false; std::atomic isAbilityConnectionReported_ = false; diff --git a/services/abilitymgr/include/ability_record/ability_request.h b/services/abilitymgr/include/ability_record/ability_request.h index 0a8a58cab2..eab949eb8b 100644 --- a/services/abilitymgr/include/ability_record/ability_request.h +++ b/services/abilitymgr/include/ability_record/ability_request.h @@ -92,6 +92,7 @@ struct AbilityRequest { uint32_t specifyTokenId = 0; int callerUid = -1; // call ability int requestCode = -1; + int32_t atomicServiceShortLink = 0; AbilityCallType callType = AbilityCallType::INVALID_TYPE; // call ability AppExecFwk::ExtensionAbilityType extensionType = AppExecFwk::ExtensionAbilityType::UNSPECIFIED; AppExecFwk::ExtensionProcessMode extensionProcessMode = AppExecFwk::ExtensionProcessMode::UNDEFINED; diff --git a/services/abilitymgr/include/ability_scheduler_proxy.h b/services/abilitymgr/include/ability_scheduler_proxy.h index 7091edf95d..f106af83b3 100644 --- a/services/abilitymgr/include/ability_scheduler_proxy.h +++ b/services/abilitymgr/include/ability_scheduler_proxy.h @@ -325,6 +325,8 @@ public: void OnExecuteIntent(const Want &want) override; + void ExecuteSkill(const Want &want) override; + /** * @brief Update sessionToken. * @param sessionToken The token of session. diff --git a/services/abilitymgr/include/ability_scheduler_stub.h b/services/abilitymgr/include/ability_scheduler_stub.h index 4c32117470..567848cd0b 100644 --- a/services/abilitymgr/include/ability_scheduler_stub.h +++ b/services/abilitymgr/include/ability_scheduler_stub.h @@ -67,6 +67,7 @@ private: int DumpAbilityInfoInner(MessageParcel& data, MessageParcel& reply); int CallRequestInner(MessageParcel &data, MessageParcel &reply); int OnExecuteIntentInner(MessageParcel &data, MessageParcel &reply); + int ExecuteSkillInner(MessageParcel &data, MessageParcel &reply); int ContinueAbilityInner(MessageParcel &data, MessageParcel &reply); int ShareDataInner(MessageParcel &data, MessageParcel &reply); int CreateModalUIExtensionInner(MessageParcel &data, MessageParcel &reply); diff --git a/services/abilitymgr/include/dialog_session/dialog_session_manager.h b/services/abilitymgr/include/dialog_session/dialog_session_manager.h index 6f7c60155e..7ec8f10104 100644 --- a/services/abilitymgr/include/dialog_session/dialog_session_manager.h +++ b/services/abilitymgr/include/dialog_session/dialog_session_manager.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023-2025 Huawei Device Co., Ltd. + * Copyright (c) 2023-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 @@ -47,6 +47,7 @@ struct DialogCallerInfo { // for app gallery selector bool needGrantUriPermission = false; sptr sessionToken = nullptr; + sptr requestCallback = nullptr; }; struct StartupSessionInfo { diff --git a/services/abilitymgr/include/disposed_observer.h b/services/abilitymgr/include/disposed_observer.h index e58e00a0e6..c05f23e384 100644 --- a/services/abilitymgr/include/disposed_observer.h +++ b/services/abilitymgr/include/disposed_observer.h @@ -34,6 +34,8 @@ public: private: void OnAbilityStateChanged(const AppExecFwk::AbilityStateData &abilityStateData) override; void OnPageShow(const AppExecFwk::PageStateData &pageStateData) override; + + ErrCode ExecuteUIExtension(const AppExecFwk::PageStateData &pageStateData); private: std::shared_ptr interceptor_ = nullptr; AppExecFwk::DisposedRule disposedRule_; diff --git a/services/abilitymgr/include/extension_record/base_extension_record.h b/services/abilitymgr/include/extension_record/base_extension_record.h index c495d2fdd2..61859850e9 100644 --- a/services/abilitymgr/include/extension_record/base_extension_record.h +++ b/services/abilitymgr/include/extension_record/base_extension_record.h @@ -141,6 +141,8 @@ public: void SetClientPid(pid_t clientPid); pid_t GetClientPid() const; + void SetRequestId(const std::string &requestId); + std::string GetRequestId() const; private: void DumpUIExtensionRootHostInfo(std::vector &info) const; @@ -154,6 +156,7 @@ private: // service(ability) onConnect() return proxy of service ability sptr connRemoteObject_ = {}; bool isConnected = false; + std::string requestId_; }; } // namespace AAFwk } // namespace OHOS diff --git a/services/abilitymgr/include/extension_record/extension_record_factory.h b/services/abilitymgr/include/extension_record/extension_record_factory.h index ce4df82d11..d5edfc77ee 100644 --- a/services/abilitymgr/include/extension_record/extension_record_factory.h +++ b/services/abilitymgr/include/extension_record/extension_record_factory.h @@ -28,6 +28,8 @@ constexpr uint32_t PROCESS_MODE_TYPE = 1 << static_cast(AppExecFwk::Ex constexpr uint32_t PROCESS_MODE_BUNDLE = 1 << static_cast(AppExecFwk::ExtensionProcessMode::BUNDLE); constexpr uint32_t PROCESS_MODE_RUN_WITH_MAIN_PROCESS = 1 << static_cast(AppExecFwk::ExtensionProcessMode::RUN_WITH_MAIN_PROCESS); +constexpr uint32_t PROCESS_MODE_CALLER_INSTANCE = + 1 << static_cast(AppExecFwk::ExtensionProcessMode::CALLER_INSTANCE); constexpr uint32_t PROCESS_INNER_MODE_OFFSET = 16; constexpr uint32_t PROCESS_MODE_HOST_SPECIFIED = 1 << (PROCESS_INNER_MODE_OFFSET + 0); constexpr uint32_t PROCESS_MODE_HOST_INSTANCE = 1 << (PROCESS_INNER_MODE_OFFSET + 1); diff --git a/services/abilitymgr/include/implicit_start_processor.h b/services/abilitymgr/include/implicit_start_processor.h index d2164a205b..04b0b3a3ae 100644 --- a/services/abilitymgr/include/implicit_start_processor.h +++ b/services/abilitymgr/include/implicit_start_processor.h @@ -87,8 +87,8 @@ private: int GenerateAbilityRequestByAppIndexes(int32_t userId, AbilityRequest &request, std::vector &dialogAppInfos); - int FindExtensionInfo(const Want &want, int32_t flags, int32_t userId, int32_t appIndex, - AppExecFwk::AbilityInfo &abilityInfo); + int FindExtensionInfo(const AppExecFwk::ElementName &elementName, int32_t flags, int32_t userId, + int32_t appIndex, AppExecFwk::AbilityInfo &abilityInfo); std::string MatchTypeAndUri(const AAFwk::Want &want); std::shared_ptr GetBundleManagerHelper(); diff --git a/services/abilitymgr/include/insight_intent/extract_insight_intent_profile.h b/services/abilitymgr/include/insight_intent/extract_insight_intent_profile.h index 2f27551cb9..173497af8b 100644 --- a/services/abilitymgr/include/insight_intent/extract_insight_intent_profile.h +++ b/services/abilitymgr/include/insight_intent/extract_insight_intent_profile.h @@ -223,6 +223,17 @@ struct ExtractInsightIntentProfileInfoVec { std::vector insightIntents {}; }; +struct ExecuteIntentCommonOptions { + bool ignoreAbilityName; + ExtractInsightIntentGenericInfo infos; + uint64_t key; + std::string srcDeviceId; + uint64_t requestCode; + uint64_t specifiedFullTokenId; + ExecuteIntentCommonOptions(bool ignoreAbilityName, const ExtractInsightIntentGenericInfo &infos, + uint64_t key): ignoreAbilityName(ignoreAbilityName), infos(infos), key(key){}; +}; + class ExtractInsightIntentProfile { public: static bool TransformTo(const std::string &profileStr, ExtractInsightIntentProfileInfoVec &infos); diff --git a/services/abilitymgr/include/insight_intent/insight_intent_execute_manager.h b/services/abilitymgr/include/insight_intent/insight_intent_execute_manager.h index 88fedb8bbd..44e907ad5e 100644 --- a/services/abilitymgr/include/insight_intent/insight_intent_execute_manager.h +++ b/services/abilitymgr/include/insight_intent/insight_intent_execute_manager.h @@ -43,6 +43,9 @@ struct InsightIntentExecuteRecord { std::string bundleName; std::string callerBundleName; InsightIntentExecuteState state = InsightIntentExecuteState::UNKNOWN; + bool isDistributed = false; + std::string deviceId; + uint64_t requestCode = 0; }; class InsightIntentExecuteConnection : public AbilityConnectionStub { @@ -77,7 +80,8 @@ DECLARE_DELAYED_SINGLETON(InsightIntentExecuteManager) public: int32_t CheckAndUpdateParam(uint64_t key, const sptr &callerToken, const std::shared_ptr ¶m, std::string callerBundleName = "", - const bool ignoreAbilityName = false); + const bool ignoreAbilityName = false, bool isDistributed = false, + const std::string &srcDeviceId = "", uint64_t requestCode = 0, uint64_t specifiedFullTokenId = 0); int32_t CheckAndUpdateWant(Want &want, AppExecFwk::ExecuteMode executeMode, int32_t userId, std::string callerBundleName = ""); @@ -85,7 +89,10 @@ public: int32_t RemoveExecuteIntent(uint64_t intentId); int32_t ExecuteIntentDone(uint64_t intentId, int32_t resultCode, - const AppExecFwk::InsightIntentExecuteResult &result); + const AppExecFwk::InsightIntentExecuteResult &result, int32_t callerUid = 0, uint32_t accessToken = 0); + + int32_t GetDistributedInfo(uint64_t intentId, std::string &srcDeviceId, + int32_t &requestCode, uint64_t &callbackKey) const; int32_t RemoteDied(uint64_t intentId); @@ -103,9 +110,14 @@ public: bool CheckIntentIsExemption(int32_t uid); - static int32_t CheckCallerPermission(); + static int32_t CheckCallerPermission(uint64_t specifiedFullTokenId = 0, + const std::string &callerBundleName = "", + const std::string &targetBundleName = ""); - static int32_t CheckGetInsightIntenInfoPermission(); + static int32_t CheckGetInsightIntenInfoPermission( + const std::string &targetBundleName = ""); + + static std::string GetCallerBundleNameByUid(); void OnInsightAppDied(const std::string &bundleName); @@ -122,7 +134,8 @@ private: std::map intentExemptionDeadlineTime_; int32_t AddRecord(uint64_t key, const sptr &callerToken, const std::string &bundleName, - uint64_t &intentId, const std::string &callerBundleName); + uint64_t &intentId, const std::string &callerBundleName, bool isDistributed = false, + const std::string &deviceId = "", uint64_t requestCode = 0); static int32_t IsValidCall(const Want &want); @@ -139,7 +152,8 @@ private: static int32_t UpdateEntryDecoratorParams(const std::shared_ptr ¶m, AbilityRuntime::ExtractInsightIntentInfo &info, Want &want); static int32_t UpdateEntryDecoratorParams(Want &want, AppExecFwk::ExecuteMode executeMode, int32_t userId); - static std::string GetMainElementName(const std::string &bundleName, const std::string &moduleName); + static std::string GetMainElementName( + const std::string &bundleName, const std::string &moduleName, int32_t userId = -1); static std::shared_ptr CheckEntityQueryable( const AbilityRuntime::ExtractInsightIntentInfo& intentInfo, const std::string& className, const AppExecFwk::InsightIntentQueryEntityParam& queryParams); diff --git a/services/abilitymgr/include/modular_object_utils.h b/services/abilitymgr/include/modular_object_utils.h index 3fff5bf121..393b86b7d9 100644 --- a/services/abilitymgr/include/modular_object_utils.h +++ b/services/abilitymgr/include/modular_object_utils.h @@ -20,6 +20,7 @@ #include #include "ability_record/ability_request.h" +#include "base_extension_record.h" #include "iremote_object.h" #include "modular_object_extension_info.h" @@ -34,8 +35,15 @@ public: static int32_t CheckRateLimit(); static bool GetPidToCheckByCallerToken(sptr callerToken, pid_t &outPid); + // Connect management helpers + static std::shared_ptr QueryConfig(const AbilityRequest &abilityRequest); + static int32_t SetupNewRecord(const AbilityRequest &abilityRequest, + std::shared_ptr &targetService, const std::string &serviceKey); + static int32_t CheckLimits(int32_t instanceCount, int32_t connectionCount); + private: static int32_t CheckExtensionEnabled(const ModularObjectExtensionInfo &info, const AbilityRequest &abilityRequest); + static int32_t CheckInProcessLaunchMode(MoeLaunchMode launchMode, int32_t targetUid); static int32_t CheckCallerForeground(); static int32_t CheckAppDistributionType(const std::string &callerAppDistributionType, const std::string &targetAppDistributionType); diff --git a/services/abilitymgr/include/remote_intent_result_callback.h b/services/abilitymgr/include/remote_intent_result_callback.h index b4eae4a35f..c432e73b3d 100644 --- a/services/abilitymgr/include/remote_intent_result_callback.h +++ b/services/abilitymgr/include/remote_intent_result_callback.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 Huawei Device Co., Ltd. + * 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 diff --git a/services/abilitymgr/include/scene_board/ui_ability_lifecycle_manager.h b/services/abilitymgr/include/scene_board/ui_ability_lifecycle_manager.h index 16ecd36dce..234c459683 100644 --- a/services/abilitymgr/include/scene_board/ui_ability_lifecycle_manager.h +++ b/services/abilitymgr/include/scene_board/ui_ability_lifecycle_manager.h @@ -585,6 +585,9 @@ public: ErrCode IsUIAbilityAlreadyExist(const Want &want, const std::string &specifiedFlag, int32_t appIndex, const std::string &instanceKey, AppExecFwk::LaunchMode launchMode); + ErrCode IsSpecifiedUIAbilityAlreadyExist(const Want &want, const std::string &specifiedFlag, + int32_t appIndex, const std::string &instanceKey); + void HandleUIAbilityDiedByPid(pid_t pid); /** @@ -742,6 +745,8 @@ private: * @param ability The ability that timed out */ void HandleForegroundTimeout(const UIAbilityRecordPtr &ability); + void HandleStartSelfTimeout(const UIAbilityRecordPtr &abilityRecord, bool isHalf); + void PostStartSelfTimeoutEvent(const UIAbilityRecordPtr &abilityRecord); /** * @brief Notify SCB to handle ability exception diff --git a/services/abilitymgr/include/scene_board/ui_ability_record.h b/services/abilitymgr/include/scene_board/ui_ability_record.h index 0ff007d331..ab6d50e0ae 100644 --- a/services/abilitymgr/include/scene_board/ui_ability_record.h +++ b/services/abilitymgr/include/scene_board/ui_ability_record.h @@ -79,9 +79,38 @@ public: hookOff_ = hookOff; } - inline bool IsLastWantBackgroundDriven() const + inline void SetShouldUpdateWant(bool shouldUpdateWant) { - return isLastWantBackgroundDriven_.load(); + shouldUpdateWant_.store(shouldUpdateWant); + } + + inline bool ShouldUpdateWant() const + { + return shouldUpdateWant_.load(); + } + + inline void SetLastWant(std::shared_ptr lastWant) + { + std::lock_guard lock(wantLock_); + lastWant_ = lastWant; + } + + inline bool HasLastWant() const + { + std::lock_guard lock(wantLock_); + return lastWant_ != nullptr; + } + + bool UpdateWantByLastWant(); + + inline void SetOnNewWantSkipScenarios(int32_t scenarios) + { + scenarios_.store(scenarios); + } + + inline int32_t GetOnNewWantSkipScenarios() const + { + return scenarios_.load(); } inline void SetNativeState(AbilityNativeState newState) @@ -108,12 +137,26 @@ public: startSelfRequestId_ = startSelfRequestId; } + inline void SetLaunchWant(std::shared_ptr launchWant) + { + launchWant_ = launchWant; + } + + inline std::shared_ptr GetLaunchWant() const + { + return launchWant_; + } + private: bool exitReasonLoaded_ = false; bool hookOff_ = false; int32_t startSelfRequestId_ = 0; + std::atomic_bool shouldUpdateWant_ = false; std::atomic_bool isKillPrecedeStart_ = false; std::atomic abilityNativeState_ = AbilityNativeState::NONE; + std::atomic_int32_t scenarios_ = 0; + std::shared_ptr launchWant_; + std::shared_ptr lastWant_; }; } // namespace AAFwk } // namespace OHOS diff --git a/services/abilitymgr/include/skill/skill_execute_manager.h b/services/abilitymgr/include/skill/skill_execute_manager.h new file mode 100644 index 0000000000..9d5cc00f71 --- /dev/null +++ b/services/abilitymgr/include/skill/skill_execute_manager.h @@ -0,0 +1,93 @@ +/* + * 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_SKILL_EXECUTE_MANAGER_H +#define OHOS_ABILITY_RUNTIME_SKILL_EXECUTE_MANAGER_H + +#include +#include "bundle_skill/skill_info.h" +#include "global_constant.h" +#include "bundle_skill/skill_manager_interface.h" +#include "cpp/mutex.h" +#include "extension_ability_info.h" +#include "iremote_object.h" +#include "singleton.h" +#include "skill/skill_execute_param.h" +#include "skill/skill_execute_record.h" +#include "skill/skill_execute_result.h" + +namespace OHOS { +namespace AAFwk { + +class SkillExecuteManager { +DECLARE_DELAYED_SINGLETON(SkillExecuteManager) +public: + int32_t GenerateSkillWant(const AppExecFwk::SkillInfo &skillInfo, Want &want, + int32_t userId, const std::string &requestCode, AppExecFwk::ExtensionAbilityType &targetType, + const std::string &scriptPath = "", const std::string &functionName = "", + const std::shared_ptr &skillArgs = nullptr); + + int32_t QuerySkillInfo(const std::string &bundleName, const std::string &moduleName, + const std::string &skillName, int32_t userId, AppExecFwk::SkillInfo &skillInfo); + + int32_t CheckSkillPermission(const AppExecFwk::SkillInfo &skillInfo); + + std::string CreateExecuteRecord(const sptr &callerToken, + const std::string &targetBundleName, const std::string &callerBundleName, + uint32_t callerTokenId, + const sptr &callback = nullptr, + const std::string &externalRequestCode = ""); + + int32_t ExecuteSkillDone(const std::string &requestCode, int32_t resultCode, + const AppExecFwk::SkillExecuteResult &result, + const std::string &callerBundleName); + + void OnTimeout(int64_t requestCodeSeq); + +private: + class CallerDeathRecipient : public IRemoteObject::DeathRecipient { + public: + CallerDeathRecipient(std::function callback, std::string requestCode) + : callback_(std::move(callback)), requestCode_(std::move(requestCode)) {} + void OnRemoteDied(const wptr &object) override + { + if (callback_ != nullptr) { + callback_(requestCode_); + } + } + private: + std::function callback_; + std::string requestCode_; + }; + + sptr GetSkillManagerProxy(); + std::string ResolveDefaultAbilityName(const std::string &bundleName, + const std::string &moduleName, int32_t userId); + AppExecFwk::ExtensionAbilityType ResolveTargetType(const std::string &bundleName, + const std::string &moduleName, const std::string &abilityName, int32_t userId); + void RemoveRecord(const std::string &requestCode); + void OnCallerDied(const std::string &requestCode); + void PostSkillExecuteTimeout(const std::string &requestCode, uint64_t requestCodeSeq); + void RemoveSkillExecuteTimeoutLocked(uint64_t requestCodeSeq); + + ffrt::mutex mutex_; + uint64_t requestCodeSeq_ = 0; + std::map> records_; + std::map seqToRequestCodeMap_; +}; + +} // namespace AAFwk +} // namespace OHOS +#endif // OHOS_ABILITY_RUNTIME_SKILL_EXECUTE_MANAGER_H diff --git a/services/abilitymgr/include/skill/skill_execute_record.h b/services/abilitymgr/include/skill/skill_execute_record.h new file mode 100644 index 0000000000..ebd8291b60 --- /dev/null +++ b/services/abilitymgr/include/skill/skill_execute_record.h @@ -0,0 +1,49 @@ +/* + * 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_SKILL_EXECUTE_RECORD_H +#define OHOS_ABILITY_RUNTIME_SKILL_EXECUTE_RECORD_H + +#include + +#include "iremote_object.h" +#include "skill/skill_execute_callback_interface.h" + +namespace OHOS { +namespace AAFwk { + +enum class SkillExecuteState { + UNKNOWN = 0, + EXECUTING, + EXECUTE_DONE, + REMOTE_DIED, + TIMED_OUT, +}; + +struct SkillExecuteRecord { + std::string requestCode; + sptr callerToken = nullptr; + sptr deathRecipient = nullptr; + std::string targetBundleName; + std::string callerBundleName; + uint32_t callerTokenId = 0; + uint64_t requestCodeSeq = 0; + SkillExecuteState state = SkillExecuteState::UNKNOWN; + sptr callback = nullptr; +}; + +} // namespace AAFwk +} // namespace OHOS +#endif // OHOS_ABILITY_RUNTIME_SKILL_EXECUTE_RECORD_H diff --git a/services/abilitymgr/include/utils/ability_permission_util.h b/services/abilitymgr/include/utils/ability_permission_util.h index 7f762d301c..f0cf21e426 100644 --- a/services/abilitymgr/include/utils/ability_permission_util.h +++ b/services/abilitymgr/include/utils/ability_permission_util.h @@ -116,6 +116,15 @@ public: bool NeedCheckStatusBar(std::shared_ptr abilityRecord, const AbilityRequest &abilityRequest); + /** + * CheckStartUIAbilityByUserLockStatus, check if UIAbility start is allowed based on user lock status. + * When status is locked, only system apps are allowed to start. + * + * @param bundleName The bundle name of the target application. + * @return Whether the ability start is allowed (true) or should be blocked (false). + */ + bool CheckStartUIAbilityByUserLockStatus(const std::string &bundleName); + private: /** * AbilityPermissionUtil, the private constructor. diff --git a/services/abilitymgr/include/utils/ability_util.h b/services/abilitymgr/include/utils/ability_util.h index 895a0d4462..42a4982545 100644 --- a/services/abilitymgr/include/utils/ability_util.h +++ b/services/abilitymgr/include/utils/ability_util.h @@ -210,7 +210,7 @@ static constexpr int64_t MICROSECONDS = 1000000; // MICROSECONDS mean 10^6 mi return false; } callerPkg = targetWant.GetStringParam(JUMP_INTERCEPTOR_DIALOG_CALLER_PKG); - targetPkg = targetWant.GetElement().GetBundleName(); + targetPkg = targetWant.GetBundle(); return !callerPkg.empty() && !targetPkg.empty(); } diff --git a/services/abilitymgr/include/utils/dlp_utils.h b/services/abilitymgr/include/utils/dlp_utils.h index a0c33520fa..cee36c1769 100644 --- a/services/abilitymgr/include/utils/dlp_utils.h +++ b/services/abilitymgr/include/utils/dlp_utils.h @@ -55,7 +55,7 @@ using Dlp = Security::DlpPermission::DlpPermissionKit; if (abilityRecord->GetAppIndex() <= AbilityRuntime::GlobalConstant::MAX_APP_CLONE_INDEX) { return true; } - if (abilityRecord->GetApplicationInfo().bundleName == want.GetElement().GetBundleName()) { + if (abilityRecord->GetApplicationInfo().bundleName == want.GetBundle()) { return true; } int32_t uid = abilityRecord->GetApplicationInfo().uid; diff --git a/services/abilitymgr/libabilityms.map b/services/abilitymgr/libabilityms.map index 7e0ae12754..83f5ae409f 100644 --- a/services/abilitymgr/libabilityms.map +++ b/services/abilitymgr/libabilityms.map @@ -39,7 +39,8 @@ *ApplicationAnrListener*; *AppMgrUtil*; *AppScheduler*; - *AtomicServiceStatusCallback*; + *AtomicServiceStatusCallback*; + *RemoteIntentResultCallback*; *BackgroundTaskObserver*; *MainElementUtils*; *CallContainer*; diff --git a/services/abilitymgr/src/ability_cache_manager.cpp b/services/abilitymgr/src/ability_cache_manager.cpp index dc483deab0..17019bd83a 100644 --- a/services/abilitymgr/src/ability_cache_manager.cpp +++ b/services/abilitymgr/src/ability_cache_manager.cpp @@ -159,7 +159,7 @@ bool AbilityCacheManager::IsRecInfoSame(const AbilityRequest& abilityRequest, { return abilityRecord != nullptr && abilityRequest.abilityInfo.moduleName == abilityRecord->GetAbilityInfo().moduleName && - abilityRequest.want.GetElement().GetAbilityName() == abilityRecord->GetWant().GetElement().GetAbilityName(); + abilityRequest.want.GetElement().GetAbilityName() == abilityRecord->GetAbilityName(); } std::shared_ptr AbilityCacheManager::GetAbilityRecInProcList(const AbilityRequest &abilityRequest) @@ -237,7 +237,7 @@ std::shared_ptr AbilityCacheManager::FindRecordBySessionId( it++; continue; } - auto assertSessionStr = (*it)->GetWant().GetStringParam(Want::PARAM_ASSERT_FAULT_SESSION_ID); + auto assertSessionStr = (*it)->GetStringParam(Want::PARAM_ASSERT_FAULT_SESSION_ID); if (assertSessionStr == assertSessionId) { std::shared_ptr &abilityRecord = *it; TAG_LOGD(AAFwkTag::SERVICE_EXT, @@ -262,7 +262,7 @@ std::shared_ptr AbilityCacheManager::FindRecordByServiceKey } std::string curServiceKey = (*it)->GetURI(); if (FRS_BUNDLE_NAME == (*it)->GetAbilityInfo().bundleName) { - curServiceKey = curServiceKey + std::to_string((*it)->GetWant().GetIntParam(FRS_APP_INDEX, 0)); + curServiceKey = curServiceKey + std::to_string((*it)->GetIntParam(FRS_APP_INDEX, 0)); } if (curServiceKey.compare(serviceKey) == 0) { std::shared_ptr &abilityRecord = *it; diff --git a/services/abilitymgr/src/ability_connect_manager.cpp b/services/abilitymgr/src/ability_connect_manager.cpp index 98f4cbb483..e4ae7b239e 100644 --- a/services/abilitymgr/src/ability_connect_manager.cpp +++ b/services/abilitymgr/src/ability_connect_manager.cpp @@ -36,6 +36,8 @@ #include "init_reboot.h" #include "int_wrapper.h" #include "multi_instance_utils.h" +#include "modular_object_manager.h" +#include "modular_object_utils.h" #include "param.h" #include "request_id_util.h" #include "res_sched_util.h" @@ -135,12 +137,12 @@ int AbilityConnectManager::StopServiceAbility(const AbilityRequest &abilityReque int AbilityConnectManager::StartAbilityLocked(const AbilityRequest &abilityRequest) { if (AppUtils::GetInstance().IsForbidStart()) { - TAG_LOGW(AAFwkTag::EXT, "forbid start: %{public}s", abilityRequest.want.GetElement().GetBundleName().c_str()); + TAG_LOGW(AAFwkTag::EXT, "forbid start: %{public}s", abilityRequest.want.GetBundle().c_str()); return INNER_ERR; } HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__); TAG_LOGD(AAFwkTag::EXT, "bundle/ability:%{public}s/%{public}s", - abilityRequest.want.GetElement().GetBundleName().c_str(), + abilityRequest.want.GetBundle().c_str(), abilityRequest.want.GetElement().GetAbilityName().c_str()); int32_t ret = AbilityPermissionUtil::GetInstance().CheckMultiInstanceKeyForExtension(abilityRequest); @@ -155,8 +157,8 @@ int AbilityConnectManager::StartAbilityLocked(const AbilityRequest &abilityReque GetOrCreateServiceRecord(abilityRequest, false, targetService, isLoadedAbility); CHECK_POINTER_AND_RETURN(targetService, ERR_INVALID_VALUE); TAG_LOGI(AAFwkTag::EXT, "%{public}s/%{public}s", - targetService->GetElementName().GetBundleName().c_str(), - targetService->GetElementName().GetAbilityName().c_str()); + targetService->GetInfoBundleName().c_str(), + targetService->GetInfoAbilityName().c_str()); targetService->AddCallerRecord(abilityRequest.callerToken, abilityRequest.requestCode, abilityRequest.want); @@ -196,7 +198,7 @@ void AbilityConnectManager::EnqueueStartServiceReq(const AbilityRequest &ability abilityUri = serviceUri; } TAG_LOGI(AAFwkTag::EXT, "abilityUri: %{public}s/%{public}s", - abilityRequest.want.GetElement().GetBundleName().c_str(), + abilityRequest.want.GetBundle().c_str(), abilityRequest.want.GetElement().GetAbilityName().c_str()); auto reqListIt = startServiceReqList_.find(abilityUri); if (reqListIt != startServiceReqList_.end()) { @@ -354,8 +356,14 @@ int AbilityConnectManager::ConnectAbilityLocked(const AbilityRequest &abilityReq #endif // SUPPORT_UPMS std::lock_guard guard(serialMutex_); + // ModularObject instance and connection limit checks + int32_t ret = CheckModularObjectLimits(abilityRequest); + if (ret != ERR_OK) { + return ret; + } + // 1. get target service ability record, and check whether it has been loaded. - int32_t ret = AbilityPermissionUtil::GetInstance().CheckMultiInstanceKeyForExtension(abilityRequest); + ret = AbilityPermissionUtil::GetInstance().CheckMultiInstanceKeyForExtension(abilityRequest); if (ret != ERR_OK) { // Do not distinguishing specific error codes return ERR_INVALID_VALUE; @@ -784,8 +792,8 @@ int AbilityConnectManager::AbilityTransitionDone(const sptr &toke CHECK_POINTER_AND_RETURN(abilityRecord, ERR_INVALID_VALUE); TAG_LOGI(AAFwkTag::EXT, "%{public}s/%{public}s, %{public}s", - abilityRecord->GetElementName().GetBundleName().c_str(), - abilityRecord->GetElementName().GetAbilityName().c_str(), abilityState.c_str()); + abilityRecord->GetInfoBundleName().c_str(), + abilityRecord->GetInfoAbilityName().c_str(), abilityState.c_str()); switch (targetState) { case AbilityState::INACTIVE: { @@ -840,8 +848,8 @@ int AbilityConnectManager::ScheduleConnectAbilityDoneLocked( auto abilityRecord = BaseExtensionRecord::TransferToExtensionRecordBase(Token::GetAbilityRecordByToken(token)); CHECK_POINTER_AND_RETURN(abilityRecord, ERR_INVALID_VALUE); - TAG_LOGI(AAFwkTag::EXT, "%{public}s/%{public}s", abilityRecord->GetElementName().GetBundleName().c_str(), - abilityRecord->GetElementName().GetAbilityName().c_str()); + TAG_LOGI(AAFwkTag::EXT, "%{public}s/%{public}s", abilityRecord->GetInfoBundleName().c_str(), + abilityRecord->GetInfoAbilityName().c_str()); if ((!abilityRecord->IsAbilityState(AbilityState::INACTIVE)) && (!abilityRecord->IsAbilityState(AbilityState::ACTIVE))) { @@ -966,8 +974,8 @@ int AbilityConnectManager::UpdateStateAndCompleteDisconnect(const std::shared_pt } TAG_LOGI(AAFwkTag::EXT, "schedule disconnect %{public}s/%{public}s", - abilityRecord->GetElementName().GetBundleName().c_str(), - abilityRecord->GetElementName().GetAbilityName().c_str()); + abilityRecord->GetInfoBundleName().c_str(), + abilityRecord->GetInfoAbilityName().c_str()); // complete disconnect and remove record from conn map connect->ScheduleDisconnectAbilityDone(); @@ -1031,8 +1039,8 @@ int AbilityConnectManager::ScheduleCommandAbilityWindowDone( std::string element = abilityRecord->GetURI(); TAG_LOGI(AAFwkTag::ABILITYMGR, "%{public}s/%{public}s, persistentId:%{private}d, winCmd:%{public}d, abilityCmd:%{public}d", - abilityRecord->GetElementName().GetBundleName().c_str(), - abilityRecord->GetElementName().GetAbilityName().c_str(), + abilityRecord->GetInfoBundleName().c_str(), + abilityRecord->GetInfoAbilityName().c_str(), sessionInfo->persistentId, winCmd, abilityCmd); // Only foreground mode need cancel, cause only foreground CommandAbilityWindow post timeout task. @@ -1362,8 +1370,7 @@ void AbilityConnectManager::SetExtensionLoadParam(AbilityRuntime::LoadParam &loa bool AbilityConnectManager::IsStrictMode(std::shared_ptr abilityRecord) { CHECK_POINTER_AND_RETURN(abilityRecord, false); - const auto &want = abilityRecord->GetWant(); - bool strictMode = want.GetBoolParam(OHOS::AAFwk::STRICT_MODE, false); + bool strictMode = abilityRecord->GetBoolParam(OHOS::AAFwk::STRICT_MODE, false); if (abilityRecord->GetAbilityInfo().extensionAbilityType == AppExecFwk::ExtensionAbilityType::INPUTMETHOD) { return strictMode; } @@ -1374,7 +1381,7 @@ bool AbilityConnectManager::IsStrictMode(std::shared_ptr ab TAG_LOGD(AAFwkTag::EXT, "SetExtensionLoadParam, not SACall, force enable strictMode"); return true; } - if (!want.HasParameter(OHOS::AAFwk::STRICT_MODE)) { + if (!abilityRecord->HasParameter(OHOS::AAFwk::STRICT_MODE)) { TAG_LOGD(AAFwkTag::EXT, "SetExtensionLoadParam, no striteMode param, force enable strictMode"); return true; } @@ -1428,10 +1435,12 @@ void AbilityConnectManager::HandleRestartResidentTask(const AbilityRequest &abil { TAG_LOGI(AAFwkTag::EXT, "HandleRestartResidentTask start"); std::lock_guard guard(serialMutex_); - auto findRestartResidentTask = [abilityRequest](const AbilityRequest &requestInfo) { - return (requestInfo.want.GetElement().GetBundleName() == abilityRequest.want.GetElement().GetBundleName() && - requestInfo.want.GetElement().GetModuleName() == abilityRequest.want.GetElement().GetModuleName() && - requestInfo.want.GetElement().GetAbilityName() == abilityRequest.want.GetElement().GetAbilityName()); + auto srcElement = abilityRequest.want.GetElement(); + auto findRestartResidentTask = [&srcElement](const AbilityRequest &requestInfo) { + auto dstElement = requestInfo.want.GetElement(); + return (dstElement.GetBundleName() == srcElement.GetBundleName() && + dstElement.GetModuleName() == srcElement.GetModuleName() && + dstElement.GetAbilityName() == srcElement.GetAbilityName()); }; auto findIter = find_if(restartResidentTaskList_.begin(), restartResidentTaskList_.end(), findRestartResidentTask); if (findIter != restartResidentTaskList_.end()) { @@ -1581,8 +1590,8 @@ void AbilityConnectManager::HandleStartTimeoutTaskInner(const std::shared_ptrGetElementName().GetBundleName().c_str(), - abilityRecord->GetElementName().GetAbilityName().c_str(), userId_); + abilityRecord->GetInfoBundleName().c_str(), + abilityRecord->GetInfoAbilityName().c_str(), userId_); MoveToTerminatingMap(abilityRecord); RemoveServiceAbility(abilityRecord); DelayedSingleton::GetInstance()->AttachTimeOut(abilityRecord->GetToken()); @@ -1899,8 +1908,8 @@ void AbilityConnectManager::TerminateAbilityWindowLocked(const std::shared_ptrConvertAbilityState(abilityRecord->GetAbilityState()); TAG_LOGI(AAFwkTag::ABILITYMGR, "ability:%{public}s/%{public}s, persistentId:%{public}d, abilityState:%{public}s", - abilityRecord->GetElementName().GetBundleName().c_str(), - abilityRecord->GetElementName().GetAbilityName().c_str(), + abilityRecord->GetInfoBundleName().c_str(), + abilityRecord->GetInfoAbilityName().c_str(), sessionInfo->persistentId, abilitystateStr.c_str()); EventInfo eventInfo; eventInfo.bundleName = abilityRecord->GetAbilityInfo().bundleName; @@ -2065,8 +2074,8 @@ void AbilityConnectManager::OnLoadAbilityFailed(std::shared_ptr abilityRecord) { CHECK_POINTER(abilityRecord); - TAG_LOGI(AAFwkTag::EXT, "%{public}s/%{public}s", abilityRecord->GetElementName().GetBundleName().c_str(), - abilityRecord->GetElementName().GetAbilityName().c_str()); + TAG_LOGI(AAFwkTag::EXT, "%{public}s/%{public}s", abilityRecord->GetInfoBundleName().c_str(), + abilityRecord->GetInfoAbilityName().c_str()); if (abilityRecord->GetAbilityInfo().type != AbilityType::SERVICE && abilityRecord->GetAbilityInfo().type != AbilityType::EXTENSION) { TAG_LOGW(AAFwkTag::ABILITYMGR, "type not service"); @@ -2260,8 +2269,8 @@ void AbilityConnectManager::DisconnectBeforeCleanup() auto abilityRecord = it->second; CHECK_POINTER(abilityRecord); TAG_LOGI(AAFwkTag::EXT, "ability will died: %{public}s/%{public}s", - abilityRecord->GetElementName().GetBundleName().c_str(), - abilityRecord->GetElementName().GetAbilityName().c_str()); + abilityRecord->GetInfoBundleName().c_str(), + abilityRecord->GetInfoAbilityName().c_str()); if (abilityRecord->GetAbilityInfo().type != AbilityType::SERVICE && abilityRecord->GetAbilityInfo().type != AbilityType::EXTENSION) { TAG_LOGW(AAFwkTag::EXT, "type not service"); @@ -2378,8 +2387,7 @@ void AbilityConnectManager::HandleNotifyAssertFaultDialogDied( return; } - auto want = abilityRecord->GetWant(); - auto assertSessionStr = want.GetStringParam(Want::PARAM_ASSERT_FAULT_SESSION_ID); + auto assertSessionStr = abilityRecord->GetStringParam(Want::PARAM_ASSERT_FAULT_SESSION_ID); if (!CheckIsNumString(assertSessionStr)) { TAG_LOGE(AAFwkTag::EXT, "assertSessionStr not number"); return; @@ -2404,7 +2412,7 @@ void AbilityConnectManager::CloseAssertDialog(const std::string &assertSessionId continue; } - auto assertSessionStr = item.second->GetWant().GetStringParam(Want::PARAM_ASSERT_FAULT_SESSION_ID); + auto assertSessionStr = item.second->GetStringParam(Want::PARAM_ASSERT_FAULT_SESSION_ID); if (assertSessionStr == assertSessionId) { abilityRecord = item.second; NotifyExtensionTerminated(abilityRecord); @@ -2434,8 +2442,8 @@ void AbilityConnectManager::RestartAbility(const std::shared_ptrGetElementName().GetBundleName().c_str(), - abilityRecord->GetElementName().GetAbilityName().c_str()); + abilityRecord->GetInfoBundleName().c_str(), + abilityRecord->GetInfoAbilityName().c_str()); AbilityRequest requestInfo; requestInfo.want = abilityRecord->GetWant(); requestInfo.abilityInfo = abilityRecord->GetAbilityInfo(); @@ -2477,10 +2485,12 @@ void AbilityConnectManager::RestartAbility(const std::shared_ptrGetURI(); if (FRS_BUNDLE_NAME == service->GetAbilityInfo().bundleName) { - serviceKey = serviceKey + std::to_string(service->GetWant().GetIntParam(FRS_APP_INDEX, 0)); + serviceKey = serviceKey + std::to_string(service->GetIntParam(FRS_APP_INDEX, 0)); } else if (service->GetAbilityInfo().extensionAbilityType == AppExecFwk::ExtensionAbilityType::AGENT) { - serviceKey = serviceKey + service->GetWant().GetStringParam(AgentRuntime::AGENTID_KEY); + serviceKey = serviceKey + service->GetStringParam(AgentRuntime::AGENTID_KEY); + } else if (service->GetAbilityInfo().extensionAbilityType == + AppExecFwk::ExtensionAbilityType::MODULAR_OBJECT) { + std::string requestId = service->GetRequestId(); + if (!requestId.empty()) { + serviceKey = serviceKey + "_" + requestId; + } } return serviceKey; } @@ -2513,6 +2529,10 @@ std::string AbilityConnectManager::GetServiceKey(const AbilityRequest &abilityRe serviceKey = serviceKey + std::to_string(abilityRequest.want.GetIntParam(FRS_APP_INDEX, 0)); } else if (abilityRequest.abilityInfo.extensionAbilityType == AppExecFwk::ExtensionAbilityType::AGENT) { serviceKey = serviceKey + abilityRequest.want.GetStringParam(AgentRuntime::AGENTID_KEY); + } else if (abilityRequest.abilityInfo.extensionAbilityType == + AppExecFwk::ExtensionAbilityType::MODULAR_OBJECT) { + auto requestId = std::to_string(RequestIdUtil::GetRequestId()); + serviceKey = serviceKey + "_" + requestId; } return serviceKey; } @@ -2802,8 +2822,8 @@ void AbilityConnectManager::PrintTimeOutLog(const std::shared_ptr::GetInstance()->GetRunningProcessInfoByToken(ability->GetToken(), processInfo); if (processInfo.pid_ == 0) { TAG_LOGE(AAFwkTag::EXT, "ability %{public}s/%{public}s pid invalid", - ability->GetElementName().GetBundleName().c_str(), - ability->GetElementName().GetAbilityName().c_str()); + ability->GetInfoBundleName().c_str(), + ability->GetInfoAbilityName().c_str()); return; } int typeId = AppExecFwk::AppfreezeManager::TypeAttribute::NORMAL_TIMEOUT; @@ -2811,7 +2831,7 @@ void AbilityConnectManager::PrintTimeOutLog(const std::shared_ptrGetWant().GetBoolParam(IS_PRELOAD_UIEXTENSION_ABILITY, false)) { + if (ability->GetBoolParam(IS_PRELOAD_UIEXTENSION_ABILITY, false)) { msgContent += "\nabilityLoadType: PreloadUIExtension\n"; } @@ -2896,15 +2916,15 @@ void AbilityConnectManager::MoveToTerminatingMap(const std::shared_ptrGetElementName().GetBundleName().c_str(), - abilityRecord->GetElementName().GetAbilityName().c_str()); + abilityRecord->GetInfoBundleName().c_str(), + abilityRecord->GetInfoAbilityName().c_str()); } TAG_LOGD(AAFwkTag::EXT, "ServiceMap remove, size:%{public}zu", serviceMap_.size()); AbilityCacheManager::GetInstance().Remove(abilityRecord); if (IsSpecialAbility(abilityRecord->GetAbilityInfo())) { TAG_LOGI(AAFwkTag::EXT, "moving ability: %{public}s/%{public}s", - abilityRecord->GetElementName().GetBundleName().c_str(), - abilityRecord->GetElementName().GetAbilityName().c_str()); + abilityRecord->GetInfoBundleName().c_str(), + abilityRecord->GetInfoAbilityName().c_str()); } } @@ -3087,7 +3107,7 @@ EventInfo AbilityConnectManager::BuildEventInfo(const std::shared_ptr( std::chrono::system_clock::now().time_since_epoch()).count(); - auto callerPid = abilityRecord->GetWant().GetIntParam(Want::PARAM_RESV_CALLER_PID, -1); + auto callerPid = abilityRecord->GetIntParam(Want::PARAM_RESV_CALLER_PID, -1); eventInfo.callerPid = callerPid == -1 ? IPCSkeleton::GetCallingPid() : callerPid; DelayedSingleton::GetInstance()->GetRunningProcessInfoByPid(eventInfo.callerPid, processInfo); eventInfo.callerPid = processInfo.pid_; @@ -3272,9 +3292,16 @@ void AbilityConnectManager::GetOrCreateServiceRecord(const AbilityRequest &abili targetService->SetCreateByConnectMode(); } SetServiceAfterNewCreate(abilityRequest, *targetService); - AddToServiceMap(serviceKey, targetService); isLoadedAbility = false; + auto setupRet = HandleExtensionSetup(abilityRequest, targetService, serviceKey); + if (setupRet != ERR_OK) { + targetService = nullptr; + return; + } + + AddToServiceMap(serviceKey, targetService); + // Notify running timeout monitor about service extension start auto &newAbilityInfo = abilityRequest.abilityInfo; auto recordId = targetService->GetRecordId(); @@ -3289,6 +3316,16 @@ void AbilityConnectManager::GetOrCreateServiceRecord(const AbilityRequest &abili TAG_LOGD(AAFwkTag::EXT, "service map add, serviceKey: %{public}s", serviceKey.c_str()); } +int32_t AbilityConnectManager::HandleExtensionSetup(const AbilityRequest &abilityRequest, + std::shared_ptr &targetService, const std::string &serviceKey) +{ + if (abilityRequest.abilityInfo.extensionAbilityType != + AppExecFwk::ExtensionAbilityType::MODULAR_OBJECT) { + return ERR_OK; + } + return ModularObjectUtils::SetupNewRecord(abilityRequest, targetService, serviceKey); +} + void AbilityConnectManager::SetServiceAfterNewCreate(const AbilityRequest &abilityRequest, BaseExtensionRecord &targetService) { @@ -3311,5 +3348,48 @@ void AbilityConnectManager::SetServiceAfterNewCreate(const AbilityRequest &abili sceneBoardTokenId_ = abilityRequest.appInfo.accessTokenId; } } + +int32_t AbilityConnectManager::CheckModularObjectLimits(const AbilityRequest &abilityRequest) +{ + if (abilityRequest.abilityInfo.extensionAbilityType != + AppExecFwk::ExtensionAbilityType::MODULAR_OBJECT) { + return ERR_OK; + } + AppExecFwk::ElementName element(abilityRequest.abilityInfo.deviceId, + GenerateBundleName(abilityRequest), + abilityRequest.abilityInfo.name, abilityRequest.abilityInfo.moduleName); + std::string prefix = element.GetURI() + "_"; + int32_t callerPid = IPCSkeleton::GetCallingPid(); + + // Snapshot matching entries under one lock to minimize lock hold time + std::vector> connections; + int32_t instanceCount = 0; + { + std::lock_guard lock(serviceMapMutex_); + for (const auto &[key, record] : serviceMap_) { + if (key.compare(0, prefix.size(), prefix) != 0) { + continue; + } + instanceCount++; + if (record == nullptr) { + continue; + } + for (const auto &conn : record->GetConnectRecordList()) { + if (conn != nullptr) { + connections.push_back(conn); + } + } + } + } + + int32_t connectionCount = 0; + for (const auto &conn : connections) { + if (conn->GetCallerPid() == callerPid) { + connectionCount++; + } + } + return ModularObjectUtils::CheckLimits(instanceCount, connectionCount); +} + } // namespace AAFwk } // namespace OHOS diff --git a/services/abilitymgr/src/ability_event_handler.cpp b/services/abilitymgr/src/ability_event_handler.cpp index dbdfb7f6cd..d1b7e5f5e9 100644 --- a/services/abilitymgr/src/ability_event_handler.cpp +++ b/services/abilitymgr/src/ability_event_handler.cpp @@ -69,6 +69,9 @@ void AbilityEventHandler::ProcessEvent(const EventWrap &event) case AbilityManagerService::CONNECT_HALF_TIMEOUT_MSG: ProcessConnectTimeOut(event, true); break; + case AbilityManagerService::SKILL_EXECUTE_TIMEOUT_MSG: + ProcessSkillExecuteTimeOut(event.GetParam()); + break; default: TAG_LOGW(AAFwkTag::ABILITYMGR, "unsupported timeout message"); break; @@ -123,5 +126,13 @@ void AbilityEventHandler::ProcessConnectTimeOut(const EventWrap &event, bool isH server->HandleConnectTimeOut(event.GetParam(), isHalf); } +void AbilityEventHandler::ProcessSkillExecuteTimeOut(int64_t requestCodeSeq) +{ + TAG_LOGI(AAFwkTag::ABILITYMGR, "skill execute timeout"); + auto server = server_.lock(); + CHECK_POINTER(server); + server->HandleSkillExecuteTimeOut(requestCodeSeq); +} + } // namespace AAFwk } // namespace OHOS diff --git a/services/abilitymgr/src/ability_manager_client.cpp b/services/abilitymgr/src/ability_manager_client.cpp index c03b05ba3c..496cb24c1f 100644 --- a/services/abilitymgr/src/ability_manager_client.cpp +++ b/services/abilitymgr/src/ability_manager_client.cpp @@ -15,12 +15,15 @@ #include "ability_manager_client.h" +#include #ifdef WITH_DLP #include "dlp_file_kits.h" #endif // WITH_DLP #include "freeze_util.h" #include "hilog_tag_wrapper.h" #include "hitrace_meter.h" +#include "insight_intent_callback_interface.h" +#include "insight_intent_host_client.h" #include "iservice_registry.h" #ifdef SUPPORT_SCREEN #include "scene_board_judgement.h" @@ -148,7 +151,7 @@ ErrCode AbilityManagerClient::StartAbility(const Want &want, int requestCode, in CHECK_POINTER_RETURN_NOT_CONNECTED(abms); TAG_LOGI(AAFwkTag::ABILITYMGR, "StartAbility %{public}s/%{public}s, userId:%{public}d, " "appCloneIndex:%{public}d, requestCode:%{public}d, specifiedFullTokenId:%{public}" PRIu64 "", - want.GetElement().GetBundleName().c_str(), want.GetElement().GetAbilityName().c_str(), + want.GetBundle().c_str(), want.GetElement().GetAbilityName().c_str(), userId, want.GetIntParam(Want::PARAM_APP_CLONE_INDEX_KEY, -1), requestCode, specifiedFullTokenId); @@ -160,7 +163,7 @@ ErrCode AbilityManagerClient::StartAbilityWithWait(Want &want, sptr(want)); @@ -200,7 +203,7 @@ ErrCode AbilityManagerClient::StartAbilityByInsightIntent( auto abms = GetAbilityManager(); CHECK_POINTER_RETURN_NOT_CONNECTED(abms); TAG_LOGD(AAFwkTag::ABILITYMGR, "ability:%{public}s, bundle:%{public}s, intentId:%{public}" PRIu64, - want.GetElement().GetAbilityName().c_str(), want.GetElement().GetBundleName().c_str(), intentId); + want.GetElement().GetAbilityName().c_str(), want.GetBundle().c_str(), intentId); HandleDlpApp(const_cast(want)); return abms->StartAbilityByInsightIntent(want, callerToken, intentId, userId); } @@ -212,7 +215,7 @@ ErrCode AbilityManagerClient::StartAbilityByOEExt( auto abms = GetAbilityManager(); CHECK_POINTER_RETURN_NOT_CONNECTED(abms); TAG_LOGI(AAFwkTag::ABILITYMGR, "StartAbilityByOEExt ability:%{public}s/%{public}s, hostPid=%{public}d, " - "specifiedFlag=%{public}s", want.GetElement().GetBundleName().c_str(), + "specifiedFlag=%{public}s", want.GetBundle().c_str(), want.GetElement().GetAbilityName().c_str(), hostPid, specifiedFlag.c_str()); HandleDlpApp(const_cast(want)); return abms->StartAbilityByOEExt(want, callerToken, hostPid, specifiedFlag); @@ -225,7 +228,7 @@ ErrCode AbilityManagerClient::StartAbility(const Want &want, const AbilityStartS auto abms = GetAbilityManager(); CHECK_POINTER_RETURN_NOT_CONNECTED(abms); TAG_LOGI(AAFwkTag::ABILITYMGR, "StartAbility ability:%{public}s/%{public}s, userId:%{public}d, " - "appCloneIndex:%{public}d, requestCode:%{public}d", want.GetElement().GetBundleName().c_str(), + "appCloneIndex:%{public}d, requestCode:%{public}d", want.GetBundle().c_str(), want.GetElement().GetAbilityName().c_str(), userId, want.GetIntParam(Want::PARAM_APP_CLONE_INDEX_KEY, -1), requestCode); HandleDlpApp(const_cast(want)); @@ -240,7 +243,7 @@ ErrCode AbilityManagerClient::StartAbility(const Want &want, const StartOptions CHECK_POINTER_RETURN_NOT_CONNECTED(abms); TAG_LOGI(AAFwkTag::ABILITYMGR, "StartAbility ability:%{public}s/%{public}s, userId:%{public}d, " "appCloneIndex:%{public}d, requestCode:%{public}d, splitRatioPreference:%{public}d", - want.GetElement().GetBundleName().c_str(), + want.GetBundle().c_str(), want.GetElement().GetAbilityName().c_str(), userId, want.GetIntParam(Want::PARAM_APP_CLONE_INDEX_KEY, -1), requestCode, startOptions.GetSplitRatioPreference()); HandleDlpApp(const_cast(want)); @@ -255,7 +258,7 @@ ErrCode AbilityManagerClient::StartAbilityAsCaller( auto abms = GetAbilityManager(); CHECK_POINTER_RETURN_NOT_CONNECTED(abms); TAG_LOGI(AAFwkTag::ABILITYMGR, "StartAbilityAsCaller ability:%{public}s/%{public}s, userId:%{public}d, " - "appIndex:%{public}d", want.GetElement().GetBundleName().c_str(), want.GetElement().GetAbilityName().c_str(), + "appIndex:%{public}d", want.GetBundle().c_str(), want.GetElement().GetAbilityName().c_str(), userId, want.GetIntParam(Want::PARAM_APP_CLONE_INDEX_KEY, -1)); HandleDlpApp(const_cast(want)); return abms->StartAbilityAsCaller(want, callerToken, asCallerSourceToken, userId, requestCode); @@ -269,7 +272,7 @@ ErrCode AbilityManagerClient::StartAbilityAsCaller(const Want &want, const Start auto abms = GetAbilityManager(); CHECK_POINTER_RETURN_NOT_CONNECTED(abms); TAG_LOGI(AAFwkTag::ABILITYMGR, "StartAbilityAsCaller ability:%{public}s/%{public}s, userId:%{public}d, " - "appIndex:%{public}d", want.GetElement().GetBundleName().c_str(), want.GetElement().GetAbilityName().c_str(), + "appIndex:%{public}d", want.GetBundle().c_str(), want.GetElement().GetAbilityName().c_str(), userId, want.GetIntParam(Want::PARAM_APP_CLONE_INDEX_KEY, -1)); HandleDlpApp(const_cast(want)); return abms->StartAbilityAsCaller(want, startOptions, callerToken, asCallerSourceToken, userId, requestCode); @@ -282,7 +285,7 @@ ErrCode AbilityManagerClient::StartAbilityForResultAsCaller( auto abms = GetAbilityManager(); CHECK_POINTER_RETURN_NOT_CONNECTED(abms); TAG_LOGI(AAFwkTag::ABILITYMGR, "StartAbilityForResultAsCaller ability:%{public}s/%{public}s, userId:%{public}d", - want.GetElement().GetBundleName().c_str(), want.GetElement().GetAbilityName().c_str(), userId); + want.GetBundle().c_str(), want.GetElement().GetAbilityName().c_str(), userId); HandleDlpApp(const_cast(want)); return abms->StartAbilityForResultAsCaller(want, callerToken, requestCode, userId); } @@ -294,7 +297,7 @@ ErrCode AbilityManagerClient::StartAbilityForResultAsCaller(const Want &want, co auto abms = GetAbilityManager(); CHECK_POINTER_RETURN_NOT_CONNECTED(abms); TAG_LOGI(AAFwkTag::ABILITYMGR, "StartAbilityForResultAsCaller ability:%{public}s/%{public}s, userId:%{public}d", - want.GetElement().GetBundleName().c_str(), want.GetElement().GetAbilityName().c_str(), userId); + want.GetBundle().c_str(), want.GetElement().GetAbilityName().c_str(), userId); HandleDlpApp(const_cast(want)); return abms->StartAbilityForResultAsCaller(want, startOptions, callerToken, requestCode, userId); } @@ -374,7 +377,7 @@ ErrCode AbilityManagerClient::StartUIAbilityWithCallback(const Want &want, sptr< auto abms = GetAbilityManager(); CHECK_POINTER_RETURN_NOT_CONNECTED(abms); TAG_LOGI(AAFwkTag::ABILITYMGR, "StartUIAbilityWithCallback ability:%{public}s/%{public}s", - want.GetElement().GetBundleName().c_str(), want.GetElement().GetAbilityName().c_str()); + want.GetBundle().c_str(), want.GetElement().GetAbilityName().c_str()); return abms->StartUIAbilityWithCallback(want, callerToken, callback); } @@ -393,7 +396,7 @@ ErrCode AbilityManagerClient::StartExtensionAbility(const Want &want, sptrStartExtensionAbility(want, callerToken, userId, extensionType); } @@ -417,7 +420,7 @@ ErrCode AbilityManagerClient::PreloadUIExtensionAbility( auto abms = GetAbilityManager(); CHECK_POINTER_RETURN_NOT_CONNECTED(abms); TAG_LOGI(AAFwkTag::ABILITYMGR, "elementName:%{public}s/%{public}s, hostBundleName:%{public}s", - want.GetElement().GetBundleName().c_str(), want.GetElement().GetAbilityName().c_str(), hostBundleName.c_str()); + want.GetBundle().c_str(), want.GetElement().GetAbilityName().c_str(), hostBundleName.c_str()); return abms->PreloadUIExtensionAbility(want, hostBundleName, userId, hostPid, requestCode); } @@ -451,7 +454,7 @@ ErrCode AbilityManagerClient::StartUIExtensionAbility(sptr extensio CHECK_POINTER_RETURN_INVALID_VALUE(extensionSessionInfo); TAG_LOGD(AAFwkTag::ABILITYMGR, "name:%{public}s %{public}s, persistentId:%{public}d, userId:%{public}d", extensionSessionInfo->want.GetElement().GetAbilityName().c_str(), - extensionSessionInfo->want.GetElement().GetBundleName().c_str(), extensionSessionInfo->persistentId, userId); + extensionSessionInfo->want.GetBundle().c_str(), extensionSessionInfo->persistentId, userId); return abms->StartUIExtensionAbility(extensionSessionInfo, userId); } @@ -467,7 +470,7 @@ ErrCode AbilityManagerClient::StartUIAbilityBySCB(sptr sessionInfo, CHECK_POINTER_RETURN_NOT_CONNECTED(abms); TAG_LOGI(AAFwkTag::ABILITYMGR, "scb call, StartUIAbilityBySCB target: %{public}s/%{public}s, flags: %{public}u, " "persistentId: %{public}d, appIndex: %{public}d, pageConfigSize:%{public}zu, requestCode:%{public}d", - sessionInfo->want.GetElement().GetBundleName().c_str(), + sessionInfo->want.GetBundle().c_str(), sessionInfo->want.GetElement().GetAbilityName().c_str(), sessionInfo->want.GetFlags(), sessionInfo->persistentId, sessionInfo->want.GetIntParam(Want::PARAM_APP_CLONE_INDEX_KEY, -1), params.pageConfig.size(), @@ -481,7 +484,7 @@ ErrCode AbilityManagerClient::StopExtensionAbility(const Want &want, sptrStopExtensionAbility(want, callerToken, userId, extensionType); } @@ -527,7 +530,7 @@ ErrCode AbilityManagerClient::TerminateUIExtensionAbility(sptr exte CHECK_POINTER_RETURN_INVALID_VALUE(extensionSessionInfo); TAG_LOGI(AAFwkTag::ABILITYMGR, "name: %{public}s %{public}s, persistentId: %{public}d", extensionSessionInfo->want.GetElement().GetAbilityName().c_str(), - extensionSessionInfo->want.GetElement().GetBundleName().c_str(), extensionSessionInfo->persistentId); + extensionSessionInfo->want.GetBundle().c_str(), extensionSessionInfo->persistentId); return abms->TerminateUIExtensionAbility(extensionSessionInfo, resultCode, resultWant); } @@ -615,7 +618,7 @@ ErrCode AbilityManagerClient::MinimizeUIExtensionAbility(sptr exten CHECK_POINTER_RETURN_INVALID_VALUE(extensionSessionInfo); TAG_LOGI(AAFwkTag::ABILITYMGR, "name: %{public}s %{public}s, persistentId: %{public}d, fromUser: %{public}d", extensionSessionInfo->want.GetElement().GetAbilityName().c_str(), - extensionSessionInfo->want.GetElement().GetBundleName().c_str(), extensionSessionInfo->persistentId, fromUser); + extensionSessionInfo->want.GetBundle().c_str(), extensionSessionInfo->persistentId, fromUser); return abms->MinimizeUIExtensionAbility(extensionSessionInfo, fromUser); } @@ -631,7 +634,7 @@ ErrCode AbilityManagerClient::MinimizeUIAbilityBySCB(sptr sessionIn CHECK_POINTER_RETURN_NOT_CONNECTED(abms); TAG_LOGI(AAFwkTag::ABILITYMGR, "scb call, MinimizeUIAbilityBySCB target: %{public}s/%{public}s, " "persistentId: %{public}d, backgroundReason: %{public}d", - sessionInfo->want.GetElement().GetBundleName().c_str(), + sessionInfo->want.GetBundle().c_str(), sessionInfo->want.GetElement().GetAbilityName().c_str(), sessionInfo->persistentId, backgroundReason); return abms->MinimizeUIAbilityBySCB(sessionInfo, fromUser, sceneFlag, backgroundReason); } @@ -648,7 +651,7 @@ ErrCode AbilityManagerClient::ConnectAbility(const Want &want, sptrConnectAbilityCommon(want, connect, nullptr, AppExecFwk::ExtensionAbilityType::SERVICE, userId, false, 0, loadTimeout); } @@ -661,7 +664,7 @@ ErrCode AbilityManagerClient::ConnectAbility( auto abms = GetAbilityManager(); CHECK_POINTER_RETURN_NOT_CONNECTED(abms); TAG_LOGI(AAFwkTag::SERVICE_EXT, "name:%{public}s %{public}s, userId:%{public}d, " - "specifiedFullTokenId:%{public}" PRIu64 "", want.GetElement().GetBundleName().c_str(), + "specifiedFullTokenId:%{public}" PRIu64 "", want.GetBundle().c_str(), want.GetElement().GetAbilityName().c_str(), userId, specifiedFullTokenId); return abms->ConnectAbilityCommon(want, connect, callerToken, AppExecFwk::ExtensionAbilityType::SERVICE, userId, false, specifiedFullTokenId); @@ -675,7 +678,7 @@ ErrCode AbilityManagerClient::ConnectAbilityWithIndirectCallerInfo(const Want &w auto abms = GetAbilityManager(); CHECK_POINTER_RETURN_NOT_CONNECTED(abms); TAG_LOGI(AAFwkTag::SERVICE_EXT, "name:%{public}s %{public}s, userId:%{public}d", - want.GetElement().GetBundleName().c_str(), want.GetElement().GetAbilityName().c_str(), userId); + want.GetBundle().c_str(), want.GetElement().GetAbilityName().c_str(), userId); return abms->ConnectAbilityCommon(want, connect, callerToken, extensionType, userId, false, 0, 0, indirectCallerInfo); } @@ -688,7 +691,7 @@ ErrCode AbilityManagerClient::ConnectAbilityWithExtensionType( auto abms = GetAbilityManager(); CHECK_POINTER_RETURN_NOT_CONNECTED(abms); TAG_LOGI(AAFwkTag::ABILITYMGR, "%{public}s/%{public}s, userId:%{public}d", - want.GetElement().GetBundleName().c_str(), want.GetElement().GetAbilityName().c_str(), userId); + want.GetBundle().c_str(), want.GetElement().GetAbilityName().c_str(), userId); return abms->ConnectAbilityCommon(want, connect, callerToken, extensionType, userId); } @@ -699,7 +702,7 @@ ErrCode AbilityManagerClient::ConnectUIServiceExtesnionAbility( auto abms = GetAbilityManager(); CHECK_POINTER_RETURN_NOT_CONNECTED(abms); TAG_LOGI(AAFwkTag::ABILITYMGR, "name:%{public}s %{public}s, userId:%{public}d", - want.GetElement().GetBundleName().c_str(), want.GetElement().GetAbilityName().c_str(), userId); + want.GetBundle().c_str(), want.GetElement().GetAbilityName().c_str(), userId); return abms->ConnectAbilityCommon(want, connect, callerToken, AppExecFwk::ExtensionAbilityType::UI_SERVICE, userId); } @@ -711,13 +714,13 @@ ErrCode AbilityManagerClient::ConnectDataShareExtensionAbility(const Want &want, auto abms = GetAbilityManager(); if (abms == nullptr) { TAG_LOGE(AAFwkTag::SERVICE_EXT, "failed,bundleName:%{public}s,abilityName:%{public}s,uri:%{public}s", - want.GetElement().GetBundleName().c_str(), want.GetElement().GetAbilityName().c_str(), + want.GetBundle().c_str(), want.GetElement().GetAbilityName().c_str(), want.GetUriString().c_str()); return ABILITY_SERVICE_NOT_CONNECTED; } TAG_LOGI(AAFwkTag::SERVICE_EXT, "name:%{public}s %{public}s, uri:%{public}s.", - want.GetElement().GetBundleName().c_str(), want.GetElement().GetAbilityName().c_str(), + want.GetBundle().c_str(), want.GetElement().GetAbilityName().c_str(), want.GetUriString().c_str()); return abms->ConnectAbilityCommon(want, connect, nullptr, AppExecFwk::ExtensionAbilityType::DATASHARE, userId); } @@ -729,12 +732,12 @@ ErrCode AbilityManagerClient::ConnectExtensionAbility(const Want &want, sptrConnectAbilityCommon(want, connect, nullptr, AppExecFwk::ExtensionAbilityType::UNSPECIFIED, userId, false, 0, loadTimeout); } @@ -746,13 +749,13 @@ ErrCode AbilityManagerClient::ConnectUIExtensionAbility(const Want &want, sptrConnectUIExtensionAbility(want, connect, sessionInfo, userId, connectInfo); } @@ -1257,7 +1260,7 @@ ErrCode AbilityManagerClient::StartAbilityByCall(const Want &want, sptrStartAbilityByCall(want, connect, nullptr, DEFAULT_INVAL_VALUE, isSilent, isVisible); } @@ -1268,7 +1271,7 @@ ErrCode AbilityManagerClient::StartAbilityByCall(const Want &want, sptrStartAbilityByCall(want, connect, callToken, accountId, isSilent, promotePriority, isVisible); } @@ -1281,7 +1284,7 @@ ErrCode AbilityManagerClient::StartAbilityByCallWithErrMsg(const Want &want, spt auto abms = GetAbilityManager(); CHECK_POINTER_RETURN_NOT_CONNECTED(abms); TAG_LOGI(AAFwkTag::ABILITYMGR, "ByCall, ability:%{public}s/%{public}s, userId:%{public}d, isSilent:%{public}d, " - "isVisible:%{public}d", want.GetElement().GetBundleName().c_str(), want.GetElement().GetAbilityName().c_str(), + "isVisible:%{public}d", want.GetBundle().c_str(), want.GetElement().GetAbilityName().c_str(), accountId, isSilent, isVisible); return abms->StartAbilityByCallWithErrMsg(want, connect, callToken, accountId, errMsg, isSilent, promotePriority, isVisible); @@ -1292,7 +1295,7 @@ ErrCode AbilityManagerClient::StartAbilityForPrelaunch(const Want &want, const i auto abms = GetAbilityManager(); CHECK_POINTER_RETURN_NOT_CONNECTED(abms); TAG_LOGI(AAFwkTag::ABILITYMGR, "prelaunch, ability:%{public}s/%{public}s", - want.GetElement().GetBundleName().c_str(), + want.GetBundle().c_str(), want.GetElement().GetAbilityName().c_str()); return abms->StartAbilityForPrelaunch(want, frameNum); } @@ -1356,7 +1359,7 @@ ErrCode AbilityManagerClient::RequestDialogService( HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__); auto abms = GetAbilityManager(); CHECK_POINTER_RETURN_NOT_CONNECTED(abms); - TAG_LOGI(AAFwkTag::ABILITYMGR, "request:%{public}s/%{public}s", want.GetElement().GetBundleName().c_str(), + TAG_LOGI(AAFwkTag::ABILITYMGR, "request:%{public}s/%{public}s", want.GetBundle().c_str(), want.GetElement().GetAbilityName().c_str()); HandleDlpApp(const_cast(want)); return abms->RequestDialogService(want, callerToken); @@ -1935,7 +1938,7 @@ void AbilityManagerClient::CallUIAbilityBySCB(sptr sessionInfo, Abi if (sessionInfo) { TAG_LOGI(AAFwkTag::ABILITYMGR, "scb call,CallUIAbilityBySCB:%{public}s/%{public}s/%{public}d,flag:%{public}u, " "pageConfigSize:%{public}zu", - sessionInfo->want.GetElement().GetBundleName().c_str(), + sessionInfo->want.GetBundle().c_str(), sessionInfo->want.GetElement().GetAbilityName().c_str(), sessionInfo->want.GetIntParam(Want::PARAM_APP_CLONE_INDEX_KEY, -1), params.sceneFlag, params.pageConfig.size()); @@ -1950,7 +1953,7 @@ int32_t AbilityManagerClient::StartSpecifiedAbilityBySCB(const Want &want, const auto abms = GetAbilityManager(); CHECK_POINTER_RETURN_NOT_CONNECTED(abms); TAG_LOGI(AAFwkTag::ABILITYMGR, "scb call, StartSpecifiedAbilityBySCB, target: %{public}s/%{public}s, " - "persistentId:%{public}d, reason:%{public}d", want.GetElement().GetBundleName().c_str(), + "persistentId:%{public}d, reason:%{public}d", want.GetBundle().c_str(), want.GetElement().GetAbilityName().c_str(), params.persistentId, static_cast(params.specifiedReason)); return abms->StartSpecifiedAbilityBySCB(want, params); } @@ -2164,6 +2167,59 @@ ErrCode AbilityManagerClient::QueryEntityInfo(uint64_t key, sptr return abms->QueryEntityInfo(key, callerToken, param); } +namespace { +constexpr int32_t INSIGHT_INTENT_EXECUTE_TIMEOUT = AbilityRuntime::INSIGHT_INTENT_EXECUTE_REPLY_FAILED; + +class SyncInsightIntentCallback : public AbilityRuntime::InsightIntentExecuteCallbackInterface { +public: + explicit SyncInsightIntentCallback(std::shared_ptr> promise) + : promise_(promise) {} + + void ProcessInsightIntentExecute(int32_t resultCode, + AppExecFwk::InsightIntentExecuteResult executeResult) override + { + TAG_LOGD(AAFwkTag::ABILITYMGR, "ProcessInsightIntentExecute called, resultCode: %{public}d", resultCode); + executeResult.innerErr = resultCode; + if (promise_ != nullptr) { + promise_->set_value(executeResult); + } + } + +private: + std::shared_ptr> promise_; +}; +} // namespace + +ErrCode AbilityManagerClient::ExecuteIntentWithResult( + const InsightIntentExecuteParam ¶m, InsightIntentExecuteResult &result, int32_t timeoutMs) +{ + TAG_LOGD(AAFwkTag::ABILITYMGR, "called, timeout: %{public}d ms", timeoutMs); + + auto promise1 = std::make_shared>(); + auto future = promise1->get_future(); + + auto syncCallback = std::make_shared(promise1); + uint64_t key = AbilityRuntime::InsightIntentHostClient::GetInstance()->AddInsightIntentExecute(syncCallback); + + ErrCode err = ExecuteIntent(key, AbilityRuntime::InsightIntentHostClient::GetInstance(), param); + if (err != ERR_OK) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "ExecuteIntent failed, err: %{public}d", err); + AbilityRuntime::InsightIntentHostClient::GetInstance()->RemoveInsightIntentExecute(key); + return err; + } + + std::future_status status = future.wait_for(std::chrono::milliseconds(timeoutMs)); + if (status == std::future_status::timeout) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "ExecuteIntent timeout"); + AbilityRuntime::InsightIntentHostClient::GetInstance()->RemoveInsightIntentExecute(key); + return INSIGHT_INTENT_EXECUTE_TIMEOUT; + } + + result = future.get(); + TAG_LOGI(AAFwkTag::ABILITYMGR, "ExecuteIntentWithResult done, innerErr: %{public}d", result.innerErr); + return ERR_OK; +} + bool AbilityManagerClient::IsAbilityControllerStart(const Want &want) { TAG_LOGD(AAFwkTag::ABILITYMGR, "call"); @@ -2259,7 +2315,7 @@ int32_t AbilityManagerClient::RestartApp(const AAFwk::Want &want) int32_t AbilityManagerClient::OpenAtomicService(Want& want, const StartOptions &options, sptr callerToken, int32_t requestCode, int32_t userId) { - TAG_LOGI(AAFwkTag::ABILITYMGR, "openas:%{public}s", want.GetElement().GetBundleName().c_str()); + TAG_LOGI(AAFwkTag::ABILITYMGR, "openas:%{public}s", want.GetBundle().c_str()); auto abms = GetAbilityManager(); CHECK_POINTER_RETURN_INVALID_VALUE(abms); return abms->OpenAtomicService(want, options, callerToken, requestCode, userId); @@ -2684,6 +2740,15 @@ ErrCode AbilityManagerClient::SetGamePreLaunchCompleteTime(int32_t userId, int64 return abms->SetGamePreLaunchCompleteTime(userId, completeTime); } +ErrCode AbilityManagerClient::StartSelfUIAbilityInChildProcess(const Want &want, const std::string &specifiedFlag, + sptr callerToken) +{ + HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__); + auto abms = GetAbilityManager(); + CHECK_POINTER_RETURN_NOT_CONNECTED(abms); + return abms->StartSelfUIAbilityInChildProcess(want, specifiedFlag, callerToken); +} + bool AbilityManagerClient::IsRestartAppLimit() { HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__); @@ -2752,5 +2817,52 @@ int32_t AbilityManagerClient::SetAppRecoveryFlag(const sptr& toke } return proxy->SetAppRecoveryFlag(token, flag); } + +ErrCode AbilityManagerClient::ExecuteInAppSkill(const std::string &bundleName, const std::string &moduleName, + const std::string &skillName, const std::string &arkTSPath, + const std::string &funcName, const std::shared_ptr &skillArgs, + const sptr &callback) +{ + TAG_LOGD(AAFwkTag::ABILITYMGR, "called"); + auto abms = GetAbilityManager(); + CHECK_POINTER_RETURN_NOT_CONNECTED(abms); + return abms->ExecuteInAppSkill(bundleName, moduleName, skillName, arkTSPath, funcName, skillArgs, callback); +} + +ErrCode AbilityManagerClient::ExecuteInAppSkillWithTokenId( + const AppExecFwk::SkillExecuteRequest &request, + const sptr &callback) +{ + TAG_LOGD(AAFwkTag::ABILITYMGR, "called"); + auto abms = GetAbilityManager(); + CHECK_POINTER_RETURN_NOT_CONNECTED(abms); + return abms->ExecuteInAppSkillWithTokenId(request, callback); +} + +ErrCode AbilityManagerClient::ExecuteSkillDone(sptr token, const std::string &requestCode, + int32_t resultCode, const AppExecFwk::SkillExecuteResult &result) +{ + TAG_LOGD(AAFwkTag::ABILITYMGR, "called"); + auto abms = GetAbilityManager(); + CHECK_POINTER_RETURN_NOT_CONNECTED(abms); + return abms->ExecuteSkillDone(token, requestCode, resultCode, result); +} + +ErrCode AbilityManagerClient::QuerySkillType(const std::string &bundleName, const std::string &moduleName, + const std::string &skillName, int32_t &skillType) +{ + TAG_LOGD(AAFwkTag::ABILITYMGR, "called"); + auto abms = GetAbilityManager(); + CHECK_POINTER_RETURN_NOT_CONNECTED(abms); + return abms->QuerySkillType(bundleName, moduleName, skillName, skillType); +} + +ErrCode AbilityManagerClient::StartSelfUIAbilityByAppContext(const Want &want) +{ + auto abms = GetAbilityManager(); + CHECK_POINTER_RETURN_NOT_CONNECTED(abms); + HandleDlpApp(const_cast(want)); + return abms->StartSelfUIAbilityByAppContext(want); +} } // namespace AAFwk } // namespace OHOS diff --git a/services/abilitymgr/src/ability_manager_event_subscriber.cpp b/services/abilitymgr/src/ability_manager_event_subscriber.cpp index ba0ddea545..f11b48b3e8 100644 --- a/services/abilitymgr/src/ability_manager_event_subscriber.cpp +++ b/services/abilitymgr/src/ability_manager_event_subscriber.cpp @@ -18,6 +18,7 @@ #include "ability_util.h" #include "common_event_support.h" #include "hilog_tag_wrapper.h" +#include "user_controller/user_controller.h" namespace OHOS { namespace AbilityRuntime { @@ -129,6 +130,7 @@ void AbilityUserUnlockEventSubscriber::OnReceiveEvent(const EventFwk::CommonEven } int32_t userId = data.GetCode(); AbilityEventMapManager::GetInstance().AddEvent(userId, action); + UserController::GetInstance().SetUserLockStatus(userId, UserController::UserLockStatus::USER_UNLOCKED); if (AbilityEventMapManager::GetInstance().CheckAllUnlocked(userId)) { screenUnlockCallback_(userId); AbilityEventMapManager::GetInstance().RemoveUser(userId); diff --git a/services/abilitymgr/src/ability_manager_proxy.cpp b/services/abilitymgr/src/ability_manager_proxy.cpp index a0e3f93b23..17e5a7dcbf 100644 --- a/services/abilitymgr/src/ability_manager_proxy.cpp +++ b/services/abilitymgr/src/ability_manager_proxy.cpp @@ -4037,16 +4037,17 @@ int AbilityManagerProxy::UnRegisterMissionListener(const std::string &deviceId, } int AbilityManagerProxy::StartAbilityByCall(const Want &want, const sptr &connect, - const sptr &callerToken, int32_t accountId, bool isSilent, bool promotePriority, bool isVisible) + const sptr &callerToken, int32_t accountId, + bool isSilent, bool promotePriority, bool isVisible, uint64_t specifiedFullTokenId) { std::string errMsg; return StartAbilityByCallWithErrMsg(want, connect, callerToken, accountId, errMsg, isSilent, promotePriority, - isVisible); + isVisible, specifiedFullTokenId); } int AbilityManagerProxy::StartAbilityByCallWithErrMsg(const Want &want, const sptr &connect, const sptr &callerToken, int32_t accountId, std::string &errMsg, bool isSilent, - bool promotePriority, bool isVisible) + bool promotePriority, bool isVisible, uint64_t specifiedFullTokenId) { if (AppUtils::GetInstance().IsForbidStart()) { TAG_LOGW(AAFwkTag::ABILITYMGR, "forbid start: %{public}s", want.GetElement().GetBundleName().c_str()); @@ -6982,6 +6983,35 @@ int32_t AbilityManagerProxy::StartSelfUIAbilityWithStartOptions(const Want &want return reply.ReadInt32(); } +int32_t AbilityManagerProxy::StartSelfUIAbilityByAppContext(const Want &want) +{ + if (AppUtils::GetInstance().IsForbidStart()) { + TAG_LOGW(AAFwkTag::ABILITYMGR, "forbid start: %{public}s", want.GetElement().GetBundleName().c_str()); + return INNER_ERR; + } + MessageParcel data; + MessageParcel reply; + MessageOption option; + + if (!WriteInterfaceToken(data)) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "write token fail"); + return ERR_WRITE_INTERFACE_CODE; + } + + if (!data.WriteParcelable(&want)) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "write want fail"); + return ERR_WRITE_WANT; + } + + auto error = SendRequest(AbilityManagerInterfaceCode::START_SELF_UI_ABILITY_BY_APP_CONTEXT, + data, reply, option); + if (error != NO_ERROR) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "request error:%{public}d", error); + return error; + } + return reply.ReadInt32(); +} + int32_t AbilityManagerProxy::StartSelfUIAbilityWithPidResult(const Want &want, StartOptions &options, uint64_t callbackId) { @@ -8009,6 +8039,42 @@ int32_t AbilityManagerProxy::SetGamePreLaunchCompleteTime(int32_t userId, int64_ return reply.ReadInt32(); } +int32_t AbilityManagerProxy::StartSelfUIAbilityInChildProcess( + const Want &want, const std::string &specifiedFlag, sptr callerToken) +{ + MessageParcel data; + MessageParcel reply; + MessageOption option; + if (!WriteInterfaceToken(data)) { + return INNER_ERR; + } + if (!data.WriteParcelable(&want)) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "want write fail"); + return INNER_ERR; + } + if (!data.WriteString(specifiedFlag)) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "specifiedFlag write fail"); + return ERR_INVALID_VALUE; + } + if (callerToken) { + if (!data.WriteBool(true) || !data.WriteRemoteObject(callerToken)) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "callerToken and flag write fail"); + return INNER_ERR; + } + } else { + if (!data.WriteBool(false)) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "flag write fail"); + return INNER_ERR; + } + } + auto ret = SendRequest(AbilityManagerInterfaceCode::START_SELF_UI_ABILITY_IN_CHILD_PROCESS, data, reply, option); + if (ret != NO_ERROR) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "send request error: %{public}d", ret); + return ret; + } + return reply.ReadInt32(); +} + bool AbilityManagerProxy::IsRestartAppLimit() { MessageParcel data; @@ -8196,5 +8262,159 @@ int32_t AbilityManagerProxy::SetAppRecoveryFlag(const sptr& token } return reply.ReadInt32(); } + +int32_t AbilityManagerProxy::ExecuteInAppSkill(const std::string &bundleName, const std::string &moduleName, + const std::string &skillName, const std::string &arkTSPath, + const std::string &funcName, const std::shared_ptr &skillArgs, + const sptr &callback) +{ + TAG_LOGD(AAFwkTag::ABILITYMGR, "execute in-app skill proxy, bundleName:%{public}s", bundleName.c_str()); + MessageParcel data; + if (!WriteInterfaceToken(data)) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "write token fail"); + return INNER_ERR; + } + if (!data.WriteString16(Str8ToStr16(bundleName)) || + !data.WriteString16(Str8ToStr16(moduleName)) || + !data.WriteString16(Str8ToStr16(skillName)) || + !data.WriteString16(Str8ToStr16(arkTSPath)) || + !data.WriteString16(Str8ToStr16(funcName))) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "write string params fail"); + return INNER_ERR; + } + auto paramsToWrite = (skillArgs != nullptr) ? skillArgs : std::make_shared(); + if (!data.WriteParcelable(paramsToWrite.get())) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "write skillArgs fail"); + return INNER_ERR; + } + bool hasCallback = callback != nullptr; + if (!data.WriteBool(hasCallback) || + (hasCallback && !data.WriteRemoteObject(callback->AsObject()))) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "write callback fail"); + return INNER_ERR; + } + MessageParcel reply; + MessageOption option; + auto ret = SendRequest(AbilityManagerInterfaceCode::EXECUTE_IN_APP_SKILL, data, reply, option); + if (ret != NO_ERROR) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "request fail:%{public}d", ret); + return ret; + } + return reply.ReadInt32(); +} + +int32_t AbilityManagerProxy::ExecuteInAppSkillWithTokenId(const AppExecFwk::SkillExecuteRequest &request, + const sptr &callback) +{ + TAG_LOGD(AAFwkTag::ABILITYMGR, "execute in-app skill with tokenId proxy, bundleName:%{public}s", + request.bundleName.c_str()); + MessageParcel data; + if (!WriteInterfaceToken(data)) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "write token fail"); + return INNER_ERR; + } + if (!data.WriteUint32(request.callerTokenId)) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "write callerTokenId fail"); + return INNER_ERR; + } + if (!data.WriteString16(Str8ToStr16(request.bundleName)) || + !data.WriteString16(Str8ToStr16(request.moduleName)) || + !data.WriteString16(Str8ToStr16(request.skillName)) || + !data.WriteString16(Str8ToStr16(request.scriptPath)) || + !data.WriteString16(Str8ToStr16(request.functionName))) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "write string params fail"); + return INNER_ERR; + } + auto paramsToWrite = (request.skillArgs != nullptr) + ? request.skillArgs : std::make_shared(); + if (!data.WriteParcelable(paramsToWrite.get())) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "write skillArgs fail"); + return INNER_ERR; + } + bool hasCallback = callback != nullptr; + if (!data.WriteBool(hasCallback) || + (hasCallback && !data.WriteRemoteObject(callback->AsObject()))) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "write callback fail"); + return INNER_ERR; + } + MessageParcel reply; + MessageOption option; + auto ret = SendRequest(AbilityManagerInterfaceCode::EXECUTE_IN_APP_SKILL_WITH_TOKEN_ID, + data, reply, option); + if (ret != NO_ERROR) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "request fail:%{public}d", ret); + return ret; + } + return reply.ReadInt32(); +} + +int32_t AbilityManagerProxy::ExecuteSkillDone(const sptr &token, + const std::string &requestCode, int32_t resultCode, + const AppExecFwk::SkillExecuteResult &result) +{ + TAG_LOGD(AAFwkTag::ABILITYMGR, + "execute skill done with token proxy, requestCode:%{public}s code:%{public}d", + requestCode.c_str(), resultCode); + MessageParcel data; + if (!WriteInterfaceToken(data)) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "write interface token fail"); + return INNER_ERR; + } + if (!data.WriteRemoteObject(token)) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "write token fail"); + return INNER_ERR; + } + if (!data.WriteString(requestCode)) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "write requestCode fail"); + return INNER_ERR; + } + if (!data.WriteInt32(resultCode)) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "write resultCode fail"); + return INNER_ERR; + } + if (!data.WriteParcelable(&result)) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "write result fail"); + return INNER_ERR; + } + MessageParcel reply; + MessageOption option(MessageOption::TF_ASYNC); + auto ret = SendRequest(AbilityManagerInterfaceCode::EXECUTE_SKILL_DONE_WITH_TOKEN, data, reply, option); + if (ret != NO_ERROR) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "request fail:%{public}d", ret); + return ret; + } + return reply.ReadInt32(); +} + +int32_t AbilityManagerProxy::QuerySkillType(const std::string &bundleName, const std::string &moduleName, + const std::string &skillName, int32_t &skillType) +{ + TAG_LOGD(AAFwkTag::ABILITYMGR, "query skill type proxy, bundleName:%{public}s", bundleName.c_str()); + MessageParcel data; + if (!WriteInterfaceToken(data)) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "write token fail"); + return INNER_ERR; + } + + if (!data.WriteString16(Str8ToStr16(bundleName)) || + !data.WriteString16(Str8ToStr16(moduleName)) || + !data.WriteString16(Str8ToStr16(skillName))) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "write string params fail"); + return INNER_ERR; + } + + MessageParcel reply; + MessageOption option; + auto ret = SendRequest(AbilityManagerInterfaceCode::QUERY_SKILL_TYPE, data, reply, option); + if (ret != NO_ERROR) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "request fail:%{public}d", ret); + return ret; + } + int32_t result = reply.ReadInt32(); + if (result == ERR_OK) { + skillType = reply.ReadInt32(); + } + return result; +} } // namespace AAFwk } // namespace OHOS diff --git a/services/abilitymgr/src/ability_manager_service.cpp b/services/abilitymgr/src/ability_manager_service.cpp index 7823502c72..77a5fb7566 100644 --- a/services/abilitymgr/src/ability_manager_service.cpp +++ b/services/abilitymgr/src/ability_manager_service.cpp @@ -57,6 +57,7 @@ #include "extension_running_timeout_monitor.h" #include "insight_intent_execute_manager.h" #include "insight_intent_db_cache.h" +#include "skill/skill_execute_manager.h" #include "insight_intent_utils.h" #include "interceptor/ability_jump_interceptor.h" #include "interceptor/block_all_app_start_interceptor.h" @@ -92,6 +93,7 @@ #include "rate_limiter.h" #include "recovery_info_timer.h" #include "recovery_param.h" +#include "remote_intent_result_callback.h" #include "report_data_partition_usage_manager.h" #include "res_sched_util.h" #include "restart_app_manager.h" @@ -149,6 +151,10 @@ #endif #include "xcollie/process_kill_reason.h" #include "xcollie/watchdog.h" +#ifdef APP_DOMAIN_VERIFY_ENABLED +#include "ag_convert_callback_impl.h" +#include "app_domain_verify_mgr_client.h" +#endif using OHOS::AppExecFwk::ElementName; using OHOS::Security::AccessToken::AccessTokenKit; @@ -274,6 +280,9 @@ constexpr int32_t START_AUTO_START_APP_DELAY_TIME = 200; constexpr int32_t START_AUTO_START_APP_RETRY_MAX_TIMES = 5; constexpr int32_t RETRY_COUNT = 20; constexpr int32_t BROKER_UID = 5557; +constexpr int64_t FLOOD_ATTACK_INTERVAL_MAX = 1000; +constexpr size_t FLOOD_ATTACK_NUMBER_MAX = 10; +constexpr int32_t DEFAULT_USER_ID = 100; const std::unordered_set COMMON_PICKER_TYPE = { "share", "action", "navigation", "mail", "finance", "flight", "express", "photoEditor" @@ -288,7 +297,10 @@ constexpr const char* UI_EXTENSION_TARGET_USER_ID = "ohos.ability.params.uiExten constexpr int32_t INSTALL_TYPE_UPGRADE = 2; constexpr int64_t CLEAR_USER_LOCKED_BUNDLE_LIST_KEY_DELAY_TIME = 60 * 1000; // 60s constexpr const char* VPN_PERMISSION_IF = "libnet_vpn_permission_if.z.so"; -using RequestVpnPermission = int32_t(*)(int32_t, const std::string&, const std::string&,bool &); +constexpr const char* INTENT_USER_ID = "ohos.insightIntent.userId"; +constexpr const char* START_SELF_UI_ABILITY_IN_CHILD_PROCESS_FLAG = "startSelfUIAbilityInChildProcessFlag"; + +using RequestVpnPermission = int32_t (*)(int32_t, const std::string &, const std::string &, bool &); void SendAbilityEvent(const EventName &eventName, HiSysEventEventType type, const EventInfo &eventInfo) { @@ -365,6 +377,8 @@ constexpr int64_t USER_SWITCH_TIMEOUT = 3 * 1000; // 3s constexpr const char* SUPPORT_LINKAGE_SCENE = "const.window.supportLinkageScene"; constexpr const char* KEY_SPECIFIED_FLAG = "com.ohos.param.specifiedFlag"; constexpr const char* KEY_SKIP_ABILITY_STAGE_LIFECYCLE = "ohos.ability.param.skipAbilityStageLifecycle"; +constexpr int32_t ATOMIC_URL = 1; +constexpr int32_t NOT_ATOMIC_URL = 0; const bool REGISTER_RESULT = SystemAbility::MakeAndRegisterAbility(DelayedSingleton::GetInstance().get()); @@ -665,7 +679,7 @@ int AbilityManagerService::StartAbility(const Want &want, int32_t userId, int re uint64_t specifiedFullTokenId) { if (AppUtils::GetInstance().IsForbidStart()) { - TAG_LOGW(AAFwkTag::ABILITYMGR, "forbid start: %{public}s", want.GetElement().GetBundleName().c_str()); + TAG_LOGW(AAFwkTag::ABILITYMGR, "forbid start: %{public}s", want.GetBundle().c_str()); return INNER_ERR; } if (specifiedFullTokenId != 0 && IPCSkeleton::GetCallingUid() != DMS_UID) { @@ -673,7 +687,7 @@ int AbilityManagerService::StartAbility(const Want &want, int32_t userId, int re specifiedFullTokenId = 0; } HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__); - XCOLLIE_TIMER_LESS_IGNORE(__PRETTY_FUNCTION__, !want.GetElement().GetDeviceID().empty()); + XCOLLIE_TIMER_LESS_IGNORE(__PRETTY_FUNCTION__, !want.GetDeviceId().empty()); bool isDebugApp = want.GetBoolParam(AbilityConfig::DEBUG_APP, false); bool isNativeDebugApp = want.GetBoolParam(AbilityConfig::NATIVE_DEBUG, false); bool hasWindowOptions = (want.HasParameter(Want::PARAM_RESV_WINDOW_LEFT) || @@ -753,19 +767,19 @@ int AbilityManagerService::StartAbility(const Want &want, const sptr(param.want)); //intent openlink do not RemoveInsightIntent @@ -811,10 +825,10 @@ int AbilityManagerService::StartAbilityWithSpecifyTokenId(const Want &want, cons uint32_t specifyTokenId, int32_t userId, int requestCode) { if (AppUtils::GetInstance().IsForbidStart()) { - TAG_LOGW(AAFwkTag::ABILITYMGR, "forbid start: %{public}s", want.GetElement().GetBundleName().c_str()); + TAG_LOGW(AAFwkTag::ABILITYMGR, "forbid start: %{public}s", want.GetBundle().c_str()); return INNER_ERR; } - XCOLLIE_TIMER_LESS_IGNORE(__PRETTY_FUNCTION__, !want.GetElement().GetDeviceID().empty()); + XCOLLIE_TIMER_LESS_IGNORE(__PRETTY_FUNCTION__, !want.GetDeviceId().empty()); if (IPCSkeleton::GetCallingUid() != FOUNDATION_UID) { TAG_LOGE(AAFwkTag::ABILITYMGR, "startAbility with specialId, current process not foundation process"); return CHECK_PERMISSION_FAILED; @@ -872,12 +886,12 @@ int32_t AbilityManagerService::StartAbilityByInsightIntent(const Want &want, con uint64_t intentId, int32_t userId) { if (AppUtils::GetInstance().IsForbidStart()) { - TAG_LOGW(AAFwkTag::ABILITYMGR, "forbid start: %{public}s", want.GetElement().GetBundleName().c_str()); + TAG_LOGW(AAFwkTag::ABILITYMGR, "forbid start: %{public}s", want.GetBundle().c_str()); return INNER_ERR; } HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__); - XCOLLIE_TIMER_LESS_IGNORE(__PRETTY_FUNCTION__, !want.GetElement().GetDeviceID().empty()); - std::string bundleNameFromWant = want.GetElement().GetBundleName(); + XCOLLIE_TIMER_LESS_IGNORE(__PRETTY_FUNCTION__, !want.GetDeviceId().empty()); + std::string bundleNameFromWant = want.GetBundle(); std::string bundleNameFromIntentMgr = ""; if (DelayedSingleton::GetInstance()-> GetBundleName(intentId, bundleNameFromIntentMgr) != ERR_OK) { @@ -904,7 +918,7 @@ int32_t AbilityManagerService::StartAbilityByOEExt(const Want &want, sptr callerToken, int32_t hostPid, const std::string &specifiedFlag) { HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__); - XCOLLIE_TIMER_LESS_IGNORE(__PRETTY_FUNCTION__, !want.GetElement().GetDeviceID().empty()); + XCOLLIE_TIMER_LESS_IGNORE(__PRETTY_FUNCTION__, !want.GetDeviceId().empty()); std::string hostBundleName; int32_t userId = -1; @@ -919,7 +933,7 @@ int32_t AbilityManagerService::StartAbilityByOEExt(const Want &want, SendAbilityEvent(EventName::START_ABILITY, HISYSEVENT_BEHAVIOR, eventInfo); TAG_LOGI(AAFwkTag::ABILITYMGR, "StartAbilityByOEExt: hostPid=%{public}d, specifiedFlag=%{public}s) %{public}s", - hostPid, specifiedFlag.c_str(), want.GetElement().GetBundleName().c_str()); + hostPid, specifiedFlag.c_str(), want.GetBundle().c_str()); StartAbilityWrapParam startAbilityWrapParam = { .want = want, .callerToken = callerToken, @@ -936,10 +950,10 @@ int AbilityManagerService::StartAbilityByUIContentSession(const Want &want, cons const sptr &sessionInfo, int32_t userId, int requestCode) { if (AppUtils::GetInstance().IsForbidStart()) { - TAG_LOGW(AAFwkTag::ABILITYMGR, "forbid start: %{public}s", want.GetElement().GetBundleName().c_str()); + TAG_LOGW(AAFwkTag::ABILITYMGR, "forbid start: %{public}s", want.GetBundle().c_str()); return INNER_ERR; } - XCOLLIE_TIMER_LESS_IGNORE(__PRETTY_FUNCTION__, !want.GetElement().GetDeviceID().empty()); + XCOLLIE_TIMER_LESS_IGNORE(__PRETTY_FUNCTION__, !want.GetDeviceId().empty()); if (!callerToken || !sessionInfo) { TAG_LOGE(AAFwkTag::ABILITYMGR, "callerToken or sessionInfo null"); return ERR_INVALID_VALUE; @@ -975,10 +989,10 @@ int AbilityManagerService::StartAbilityByUIContentSession(const Want &want, cons const sptr &callerToken, const sptr &sessionInfo, int32_t userId, int requestCode) { if (AppUtils::GetInstance().IsForbidStart()) { - TAG_LOGW(AAFwkTag::ABILITYMGR, "forbid start: %{public}s", want.GetElement().GetBundleName().c_str()); + TAG_LOGW(AAFwkTag::ABILITYMGR, "forbid start: %{public}s", want.GetBundle().c_str()); return INNER_ERR; } - XCOLLIE_TIMER_LESS_IGNORE(__PRETTY_FUNCTION__, !want.GetElement().GetDeviceID().empty()); + XCOLLIE_TIMER_LESS_IGNORE(__PRETTY_FUNCTION__, !want.GetDeviceId().empty()); if (!callerToken || !sessionInfo) { TAG_LOGE(AAFwkTag::ABILITYMGR, "callerToken or sessionInfo null"); return ERR_INVALID_VALUE; @@ -1014,10 +1028,10 @@ int AbilityManagerService::StartAbilityOnlyUIAbility(const Want &want, const spt uint32_t specifyTokenId) { if (AppUtils::GetInstance().IsForbidStart()) { - TAG_LOGW(AAFwkTag::ABILITYMGR, "forbid start: %{public}s", want.GetElement().GetBundleName().c_str()); + TAG_LOGW(AAFwkTag::ABILITYMGR, "forbid start: %{public}s", want.GetBundle().c_str()); return INNER_ERR; } - XCOLLIE_TIMER_LESS_IGNORE(__PRETTY_FUNCTION__, !want.GetElement().GetDeviceID().empty()); + XCOLLIE_TIMER_LESS_IGNORE(__PRETTY_FUNCTION__, !want.GetDeviceId().empty()); if (IPCSkeleton::GetCallingUid() != FOUNDATION_UID) { TAG_LOGE(AAFwkTag::ABILITYMGR, "StartAbility with specialId, process not foundation process"); return CHECK_PERMISSION_FAILED; @@ -1056,27 +1070,28 @@ int AbilityManagerService::StartAbilityAsCaller(const Want &want, const sptr asCallerSourceToken, int32_t userId, int requestCode) { if (AppUtils::GetInstance().IsForbidStart()) { - TAG_LOGW(AAFwkTag::ABILITYMGR, "forbid start: %{public}s", want.GetElement().GetBundleName().c_str()); + TAG_LOGW(AAFwkTag::ABILITYMGR, "forbid start: %{public}s", want.GetBundle().c_str()); return INNER_ERR; } - XCOLLIE_TIMER_LESS_IGNORE(__PRETTY_FUNCTION__, !want.GetElement().GetDeviceID().empty()); + XCOLLIE_TIMER_LESS_IGNORE(__PRETTY_FUNCTION__, !want.GetDeviceId().empty()); return StartAbilityAsCallerDetails(want, callerToken, asCallerSourceToken, userId, requestCode); } int AbilityManagerService::ImplicitStartAbilityAsCaller(const Want &want, const sptr &callerToken, - sptr asCallerSourceToken, int32_t userId, int requestCode) + sptr asCallerSourceToken, int32_t userId, int requestCode, + sptr callback) { if (AppUtils::GetInstance().IsForbidStart()) { - TAG_LOGW(AAFwkTag::ABILITYMGR, "forbid start: %{public}s", want.GetElement().GetBundleName().c_str()); + TAG_LOGW(AAFwkTag::ABILITYMGR, "forbid start: %{public}s", want.GetBundle().c_str()); return INNER_ERR; } return StartAbilityAsCallerDetails(want, callerToken, asCallerSourceToken, userId, - requestCode, true); + requestCode, true, false, 0, callback); } int AbilityManagerService::StartAbilityAsCallerDetails(const Want &want, const sptr &callerToken, sptr asCallerSourceToken, int32_t userId, int requestCode, bool isImplicit, bool isAppCloneSelector, - uint32_t callerAccessTokenId) + uint32_t callerAccessTokenId, sptr callback) { HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__); CHECK_CALLER_IS_SYSTEM_APP; @@ -1114,10 +1129,15 @@ int AbilityManagerService::StartAbilityAsCallerDetails(const Want &want, const s .specifyTokenId = callerAccessTokenId, .isImplicit = isImplicit, .isAppCloneSelector = isAppCloneSelector, + .requestCallback = callback, }; int32_t ret = StartAbilityWrap(startAbilityWrapParam); if (ret != ERR_OK) { TAG_LOGE(AAFwkTag::ABILITYMGR, "start ability as caller failed:%{public}d", ret); + if (callback != nullptr) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "ret: %{public}d, callback request ability", ret); + callback->OnRequestStartAbilityResult(false); + } } return ret; } @@ -1345,7 +1365,7 @@ void AbilityManagerService::CheckExtensionRateLimit(const Want &want) return; } EventInfo eventInfo; - eventInfo.abilityName = want.GetElement().GetBundleName() + "/" + want.GetElement().GetAbilityName(); + eventInfo.abilityName = want.GetBundle() + "/" + want.GetElement().GetAbilityName(); eventInfo.callerBundleName = callerBundleName; eventInfo.moduleName = "ReachLimit"; eventInfo.extensionType = limitResult.triggeredLimit; @@ -1480,9 +1500,12 @@ int AbilityManagerService::StartAbilityInner(StartAbilityWrapParam ¶m) abilityRequest.isFromOpenLink = param.isFromOpenLink; abilityRequest.isStartByOEExt = param.isStartByOEExt; abilityRequest.specifiedFlag = param.specifiedFlag; + abilityRequest.requestCallback = param.requestCallback; #ifdef SUPPORT_SCREEN if (ImplicitStartProcessor::IsImplicitStartAction(param.want)) { TAG_LOGD(AAFwkTag::ABILITYMGR, "is implicit start action"); + int32_t isAtomicUrl = AtomicServicePreprocess(param.want); + abilityRequest.atomicServiceShortLink = isAtomicUrl; auto checkResult = AbilityUtil::CheckInstanceKey(param.want); if (checkResult != ERR_OK) { AbilityEventUtil::SendStartAbilityErrorEvent(eventInfo, checkResult, "CheckInstanceKey failed"); @@ -1517,8 +1540,8 @@ int AbilityManagerService::StartAbilityInner(StartAbilityWrapParam ¶m) auto collaborator = GetCollaborator(CollaboratorType::RESERVE_TYPE); CollaboratorUtil::UpdateTargetIfNeed(collaborator, param.want, callerBundleName); result = GenerateAbilityRequest(param.want, param.requestCode, abilityRequest, param.callerToken, validUserId); - bool selfFreeInstallEnable = (result == RESOLVE_ABILITY_ERR && param.want.GetElement().GetModuleName() != "" && - param.want.GetElement().GetBundleName() == callerBundleName); + bool selfFreeInstallEnable = (result == RESOLVE_ABILITY_ERR && param.want.GetModuleName() != "" && + param.want.GetBundle() == callerBundleName); bool isStartFreeInstallByWant = AbilityUtil::IsStartFreeInstall(param.want); if (isStartFreeInstallByWant || selfFreeInstallEnable) { TAG_LOGD(AAFwkTag::ABILITYMGR, "selfFreeInstallEnable: %{public}d, isStartFreeInstallByWant: %{public}d", @@ -1777,11 +1800,11 @@ int AbilityManagerService::StartAbility(const Want &want, const AbilityStartSett const sptr &callerToken, int32_t userId, int requestCode) { if (AppUtils::GetInstance().IsForbidStart()) { - TAG_LOGW(AAFwkTag::ABILITYMGR, "forbid start: %{public}s", want.GetElement().GetBundleName().c_str()); + TAG_LOGW(AAFwkTag::ABILITYMGR, "forbid start: %{public}s", want.GetBundle().c_str()); return INNER_ERR; } HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__); - XCOLLIE_TIMER_LESS_IGNORE(__PRETTY_FUNCTION__, !want.GetElement().GetDeviceID().empty()); + XCOLLIE_TIMER_LESS_IGNORE(__PRETTY_FUNCTION__, !want.GetDeviceId().empty()); #ifdef SUPPORT_SCREEN DmsUtil::GetInstance().UpdateFlagForCollaboration(want); #endif @@ -2038,10 +2061,10 @@ int AbilityManagerService::StartAbility(const Want &want, const StartOptions &st const sptr &callerToken, int32_t userId, int requestCode) { if (AppUtils::GetInstance().IsForbidStart()) { - TAG_LOGW(AAFwkTag::ABILITYMGR, "forbid start: %{public}s", want.GetElement().GetBundleName().c_str()); + TAG_LOGW(AAFwkTag::ABILITYMGR, "forbid start: %{public}s", want.GetBundle().c_str()); return INNER_ERR; } - XCOLLIE_TIMER_LESS_IGNORE(__PRETTY_FUNCTION__, !want.GetElement().GetDeviceID().empty()); + XCOLLIE_TIMER_LESS_IGNORE(__PRETTY_FUNCTION__, !want.GetDeviceId().empty()); HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__); TAG_LOGD(AAFwkTag::ABILITYMGR, "Start ability with startOptions."); AbilityUtil::RemoveShowModeKey(const_cast(want)); @@ -2062,7 +2085,7 @@ int AbilityManagerService::ImplicitStartAbility(const Want &want, const StartOpt int AbilityManagerService::StartUIAbilityForOptionWrap(const Want &want, const StartOptions &options, sptr callerToken, bool isPendingWantCaller, int32_t userId, int requestCode, uint32_t callerTokenId, bool isImplicit, - bool isCallByShortcut) + bool isCallByShortcut, bool isCallByDelayed) { HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__); int32_t ret = ERR_OK; @@ -2071,7 +2094,7 @@ int AbilityManagerService::StartUIAbilityForOptionWrap(const Want &want, const S return ret; } return StartAbilityForOptionWrap(want, options, callerToken, isPendingWantCaller, userId, requestCode, false, - callerTokenId, isImplicit, isCallByShortcut); + callerTokenId, isImplicit, isCallByShortcut, isCallByDelayed); } int AbilityManagerService::StartAbilityAsCaller(const Want &want, const StartOptions &startOptions, @@ -2079,10 +2102,10 @@ int AbilityManagerService::StartAbilityAsCaller(const Want &want, const StartOpt int32_t userId, int requestCode) { if (AppUtils::GetInstance().IsForbidStart()) { - TAG_LOGW(AAFwkTag::ABILITYMGR, "forbid start: %{public}s", want.GetElement().GetBundleName().c_str()); + TAG_LOGW(AAFwkTag::ABILITYMGR, "forbid start: %{public}s", want.GetBundle().c_str()); return INNER_ERR; } - XCOLLIE_TIMER_LESS_IGNORE(__PRETTY_FUNCTION__, !want.GetElement().GetDeviceID().empty()); + XCOLLIE_TIMER_LESS_IGNORE(__PRETTY_FUNCTION__, !want.GetDeviceId().empty()); TAG_LOGD(AAFwkTag::ABILITYMGR, "Start ability as caller with startOptions."); CHECK_CALLER_IS_SYSTEM_APP; @@ -2096,10 +2119,10 @@ int AbilityManagerService::StartAbilityForResultAsCaller( const Want &want, const sptr &callerToken, int requestCode, int32_t userId) { if (AppUtils::GetInstance().IsForbidStart()) { - TAG_LOGW(AAFwkTag::ABILITYMGR, "forbid start: %{public}s", want.GetElement().GetBundleName().c_str()); + TAG_LOGW(AAFwkTag::ABILITYMGR, "forbid start: %{public}s", want.GetBundle().c_str()); return INNER_ERR; } - XCOLLIE_TIMER_LESS_IGNORE(__PRETTY_FUNCTION__, !want.GetElement().GetDeviceID().empty()); + XCOLLIE_TIMER_LESS_IGNORE(__PRETTY_FUNCTION__, !want.GetDeviceId().empty()); TAG_LOGD(AAFwkTag::ABILITYMGR, "called"); CHECK_CALLER_IS_SYSTEM_APP; @@ -2128,10 +2151,10 @@ int AbilityManagerService::StartAbilityForResultAsCaller(const Want &want, const const sptr &callerToken, int requestCode, int32_t userId) { if (AppUtils::GetInstance().IsForbidStart()) { - TAG_LOGW(AAFwkTag::ABILITYMGR, "forbid start: %{public}s", want.GetElement().GetBundleName().c_str()); + TAG_LOGW(AAFwkTag::ABILITYMGR, "forbid start: %{public}s", want.GetBundle().c_str()); return INNER_ERR; } - XCOLLIE_TIMER_LESS_IGNORE(__PRETTY_FUNCTION__, !want.GetElement().GetDeviceID().empty()); + XCOLLIE_TIMER_LESS_IGNORE(__PRETTY_FUNCTION__, !want.GetDeviceId().empty()); TAG_LOGD(AAFwkTag::ABILITYMGR, "called"); CHECK_CALLER_IS_SYSTEM_APP; @@ -2150,7 +2173,7 @@ int AbilityManagerService::StartAbilityForResultAsCaller(const Want &want, const int AbilityManagerService::StartAbilityForOptionWrap(const Want &want, const StartOptions &startOptions, const sptr &callerToken, bool isPendingWantCaller, int32_t userId, int requestCode, - bool isStartAsCaller, uint32_t callerTokenId, bool isImplicit, bool isCallByShortcut) + bool isStartAsCaller, uint32_t callerTokenId, bool isImplicit, bool isCallByShortcut, bool isCallByDelayed) { HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__); StartAbilityParams startParams(const_cast(want)); @@ -2167,18 +2190,18 @@ int AbilityManagerService::StartAbilityForOptionWrap(const Want &want, const Sta } return StartAbilityForOptionInner(want, startOptions, callerToken, isPendingWantCaller, userId, requestCode, - isStartAsCaller, callerTokenId, isImplicit, isCallByShortcut); + isStartAsCaller, callerTokenId, isImplicit, isCallByShortcut, isCallByDelayed); } int AbilityManagerService::StartAbilityForOptionInner(const Want &want, const StartOptions &startOptions, const sptr &callerToken, bool isPendingWantCaller, int32_t userId, int requestCode, - bool isStartAsCaller, uint32_t specifyTokenId, bool isImplicit, bool isCallByShortcut) + bool isStartAsCaller, uint32_t specifyTokenId, bool isImplicit, bool isCallByShortcut, bool isCallByDelayed) { HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__); EventInfo eventInfo = BuildEventInfo(want, userId); // prevent the app from dominating the screen if (callerToken == nullptr && !IsCallerSceneBoard() && !isCallByShortcut && - AbilityPermissionUtil::GetInstance().IsDominateScreen(want, isPendingWantCaller)) { + AbilityPermissionUtil::GetInstance().IsDominateScreen(want, isPendingWantCaller) && !isCallByDelayed) { TAG_LOGE(AAFwkTag::ABILITYMGR, "caller invalid"); AbilityEventUtil::SendStartAbilityErrorEvent(eventInfo, ERR_INVALID_CALLER, "caller invalid"); return ERR_INVALID_CALLER; @@ -2291,6 +2314,8 @@ int AbilityManagerService::StartAbilityForOptionInner(const Want &want, const St #ifdef SUPPORT_SCREEN if (ImplicitStartProcessor::IsImplicitStartAction(want)) { TAG_LOGD(AAFwkTag::ABILITYMGR, "is implicit start action"); + int32_t isAtomicUrl = AtomicServicePreprocess(want); + abilityRequest.atomicServiceShortLink = isAtomicUrl; auto checkResult = AbilityUtil::CheckInstanceKey(want); if (checkResult != ERR_OK) { AbilityEventUtil::SendStartAbilityErrorEvent(eventInfo, checkResult, "CheckInstanceKey failed"); @@ -2381,6 +2406,7 @@ int AbilityManagerService::StartAbilityForOptionInner(const Want &want, const St if (!abilityRecords.empty() && abilityRecords[0] && !startOptions.processOptions->isRestartKeepAlive && !ProcessOptions::IsAttachToStatusBarItemMode(startOptions.processOptions->processMode) && + currentProcessName != START_SELF_UI_ABILITY_IN_CHILD_PROCESS_FLAG && !startOptions.processOptions->isStartFromNDK) { TAG_LOGE(AAFwkTag::ABILITYMGR, "processMode is not attach to status bar item."); AbilityEventUtil::SendStartAbilityErrorEvent(eventInfo, ERR_ABILITY_ALREADY_RUNNING, @@ -2399,7 +2425,7 @@ int AbilityManagerService::StartAbilityForOptionInner(const Want &want, const St result = StartAbilityUtils::IsCallFromAncoShellOrBroker(callerToken) ? CheckBrokerCallPermission(abilityRequest, abilityInfo) : CheckCallAbilityPermission(abilityRequest, false, 0, isCallByShortcut); - if (result != ERR_OK) { + if (result != ERR_OK && !isCallByDelayed) { TAG_LOGE(AAFwkTag::ABILITYMGR, "startoption permission error:%{public}d", result); AbilityEventUtil::SendStartAbilityErrorEvent(eventInfo, result, "CheckCallAbilityPermission error"); return AbilityErrorUtil::ConvertToOriginErrorCode(result); @@ -2508,7 +2534,7 @@ int AbilityManagerService::StartAbilityForOptionInner(const Want &want, const St abilityRequest.supportWindowModes = startOptions.supportWindowModes_; auto abilityRecord = Token::GetAbilityRecordByToken(callerToken); std::string callerBundleName = abilityRecord ? abilityRecord->GetAbilityInfo().bundleName : ""; - if (abilityRequest.want.GetElement().GetBundleName() == callerBundleName) { + if (abilityRequest.want.GetBundle() == callerBundleName) { abilityRequest.hideStartWindow = startOptions.GetHideStartWindow(); } auto uiAbilityManager = GetUIAbilityManagerByUserId(oriValidUserId); @@ -2903,7 +2929,7 @@ int32_t AbilityManagerService::RequestDialogService(const Want &want, const sptr } TAG_LOGI(AAFwkTag::ABILITYMGR, "request dialog service, target:%{public}s/%{public}s", - want.GetElement().GetBundleName().c_str(), want.GetElement().GetAbilityName().c_str()); + want.GetBundle().c_str(), want.GetElement().GetAbilityName().c_str()); return RequestDialogServiceInner(want, callerToken, -1, -1); } @@ -3042,7 +3068,7 @@ int AbilityManagerService::SetWantForSessionInfo(sptr sessionInfo) { CHECK_POINTER_AND_RETURN(sessionInfo, ERR_INVALID_VALUE); if (!(sessionInfo->want).GetElement().GetAbilityName().empty() && - !(sessionInfo->want).GetElement().GetModuleName().empty()) { + !(sessionInfo->want).GetModuleName().empty()) { return ERR_OK; } auto bundleMgrHelper = AbilityUtil::GetBundleManagerHelper(); @@ -3074,11 +3100,10 @@ int AbilityManagerService::StartUIAbilityBySCB(sptr sessionInfo, Ab sessionInfo->want.SetParam(AbilityRuntime::GlobalConstant::PAGE_CONFIG, params.pageConfig); EventInfo eventInfo = BuildEventInfo(sessionInfo->want, -1); #ifdef MEMMGR_OVERRIDE_ENABLE - WantParams wantParams = (sessionInfo->want).GetParams(); - std::string bundleName = (sessionInfo->want).GetElement().GetBundleName(); - int windowMode = wantParams.GetIntParam(EXPECT_WINDOW_MODE, 0); + auto bundleName = (sessionInfo->want).GetBundle(); + int windowMode = sessionInfo->want.GetIntParam(EXPECT_WINDOW_MODE, 0); if (AppUtils::GetInstance().IsRequireBigMemoryProcess(bundleName) && - wantParams.HasParam(EXPECT_WINDOW_MODE) && + sessionInfo->want.HasParameter(EXPECT_WINDOW_MODE) && (windowMode == AbilityWindowConfiguration::MULTI_WINDOW_DISPLAY_FLOATING || windowMode == AbilityWindowConfiguration::MULTI_WINDOW_DISPLAY_PRIMARY || windowMode == AbilityWindowConfiguration::MULTI_WINDOW_DISPLAY_SECONDARY || @@ -3098,6 +3123,11 @@ int AbilityManagerService::StartUIAbilityBySCB(sptr sessionInfo, Ab "not sceneboard", true); return ERR_WRONG_INTERFACE_CALL; } + std::string targetBundleName = sessionInfo->want.GetBundle(); + if (!AbilityPermissionUtil::GetInstance().CheckStartUIAbilityByUserLockStatus(targetBundleName)) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "scb call, user lock"); + return ERR_BLOCK_START_FIRST_BOOT_SCREEN_UNLOCK; + } if (params.isRestart && !AppUtils::GetInstance().IsSupportRestartAppWithWindow()) { TAG_LOGE(AAFwkTag::ABILITYMGR, "not supported"); @@ -3154,7 +3184,7 @@ int AbilityManagerService::StartUIAbilityBySCB(sptr sessionInfo, Ab TAG_LOGI(AAFwkTag::ABILITYMGR, "free install task in progress"); const Want& want = sessionInfo->want; - freeInstallManager_->SetSCBCallStatus(want.GetElement().GetBundleName(), want.GetElement().GetAbilityName(), + freeInstallManager_->SetSCBCallStatus(want.GetBundle(), want.GetElement().GetAbilityName(), want.GetStringParam(Want::PARAM_RESV_START_TIME), true); return ERR_OK; } @@ -3230,8 +3260,7 @@ int AbilityManagerService::StartUIAbilityBySCBDefault(sptr sessionI abilityRequest.specifyTokenId = specifyTokenId; auto abilityInfo = abilityRequest.abilityInfo; - if (!AAFwk::PermissionVerification::GetInstance()->IsSystemAppCall() && - abilityInfo.type != AppExecFwk::AbilityType::PAGE) { + if (abilityInfo.type != AppExecFwk::AbilityType::PAGE) { TAG_LOGE(AAFwkTag::ABILITYMGR, "only support page type ability"); AbilityEventUtil::SendStartAbilityErrorEvent(eventInfo, ERR_INVALID_VALUE, "only support page type ability", true); @@ -3987,7 +4016,7 @@ int AbilityManagerService::PreloadUIExtensionAbility(const Want &want, std::stri int32_t userId, int32_t hostPid, int32_t requestCode) { if (AppUtils::GetInstance().IsForbidStart()) { - TAG_LOGW(AAFwkTag::ABILITYMGR, "forbid start: %{public}s", want.GetElement().GetBundleName().c_str()); + TAG_LOGW(AAFwkTag::ABILITYMGR, "forbid start: %{public}s", want.GetBundle().c_str()); return INNER_ERR; } TAG_LOGI(AAFwkTag::UI_EXT, "callerBundle: %{public}s", bundleName.c_str()); @@ -4008,7 +4037,7 @@ int AbilityManagerService::PreloadUIExtensionAbilityInner( const Want &want, std::string &hostBundleName, int32_t userId, int32_t hostPid, int32_t requestCode) { TAG_LOGD(AAFwkTag::UI_EXT, "PreloadUIExtension called, elementName: %{public}s/%{public}s", - want.GetElement().GetBundleName().c_str(), want.GetElement().GetAbilityName().c_str()); + want.GetBundle().c_str(), want.GetElement().GetAbilityName().c_str()); int32_t validUserId = GetValidUserId(userId); auto bms = AbilityUtil::GetBundleManagerHelper(); CHECK_POINTER_AND_RETURN(bms, ERR_INVALID_VALUE); @@ -4277,7 +4306,7 @@ int32_t AbilityManagerService::StartExtensionAbilityInner(const Want &want, cons HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__); TAG_LOGI(AAFwkTag::SERVICE_EXT, "Start extension ability come, bundlename: %{public}s, ability is %{public}s, userId is %{public}d", - want.GetElement().GetBundleName().c_str(), want.GetElement().GetAbilityName().c_str(), userId); + want.GetBundle().c_str(), want.GetElement().GetAbilityName().c_str(), userId); CheckExtensionRateLimit(want); if (checkSystemCaller) { CHECK_CALLER_IS_SYSTEM_APP; @@ -4480,7 +4509,7 @@ void AbilityManagerService::SetPickerElementNameAndParams(const sptrwant.GetStringParam(UIEXTENSION_TARGET_TYPE_KEY); if (!Rosen::SceneBoardJudgement::IsSceneBoardEnabled() && - extensionSessionInfo->want.GetElement().GetBundleName().empty() && + extensionSessionInfo->want.GetBundle().empty() && extensionSessionInfo->want.GetElement().GetAbilityName().empty() && COMMON_PICKER_TYPE.find(targetType) != COMMON_PICKER_TYPE.end()) { std::string abilityName = "CommonSelectPickerAbility"; @@ -4495,7 +4524,7 @@ void AbilityManagerService::SetPickerElementNameAndParams(const sptrwant.SetParams(parameters); return; } - if (extensionSessionInfo->want.GetElement().GetBundleName().empty() && + if (extensionSessionInfo->want.GetBundle().empty() && extensionSessionInfo->want.GetElement().GetAbilityName().empty() && !targetType.empty()) { std::string abilityName; std::string bundleName; @@ -4728,7 +4757,7 @@ int AbilityManagerService::StartUIExtensionAbility(const sptr &exte static_cast(callerRecord->GetRecordId()) << OFFSET) | static_cast(abilityRequest.sessionInfo->persistentId); TAG_LOGI(AAFwkTag::UI_EXT, "StartUIExtension: persistentId: %{public}d, element: %{public}s/%{public}s", - abilityRequest.sessionInfo->persistentId, extensionSessionInfo->want.GetElement().GetBundleName().c_str(), + abilityRequest.sessionInfo->persistentId, extensionSessionInfo->want.GetBundle().c_str(), extensionSessionInfo->want.GetElement().GetAbilityName().c_str()); if (result != ERR_OK) { TAG_LOGE(AAFwkTag::UI_EXT, "generate ability request local error"); @@ -4821,7 +4850,7 @@ int AbilityManagerService::StopExtensionAbility(const Want &want, const sptr token) { XCOLLIE_TIMER_LESS(__PRETTY_FUNCTION__); TAG_LOGI(AAFwkTag::ABILITYMGR, "StartSelf called"); + CHECK_TRUE_RETURN_RET(!AppUtils::GetInstance().IsSupportNativeUIAbility(), + ERR_CAPABILITY_NOT_SUPPORT, "device type not allowd"); auto abilityRecord = Token::GetAbilityRecordByToken(token); if (!abilityRecord) { @@ -5235,7 +5266,7 @@ int AbilityManagerService::TerminateUIExtensionAbility(const sptr & } TAG_LOGD(AAFwkTag::ABILITYMGR, "UIExtension persistentId: %{public}d, element: %{public}s/%{public}s", - extensionSessionInfo->persistentId, extensionSessionInfo->want.GetElement().GetBundleName().c_str(), + extensionSessionInfo->persistentId, extensionSessionInfo->want.GetBundle().c_str(), extensionSessionInfo->want.GetElement().GetAbilityName().c_str()); connectManager->TerminateAbilityWindowLocked(targetRecord, extensionSessionInfo); return ERR_OK; @@ -5262,16 +5293,16 @@ int AbilityManagerService::CloseUIExtensionAbilityBySCB(const sptrGetAbilityInfo().extensionAbilityType)) { TAG_LOGE(AAFwkTag::ABILITYMGR, "target ability %{public}s/%{public}s not an uiextensionability", - abilityRecord->GetElementName().GetBundleName().c_str(), - abilityRecord->GetElementName().GetAbilityName().c_str()); + abilityRecord->GetInfoBundleName().c_str(), + abilityRecord->GetInfoAbilityName().c_str()); return ERR_INVALID_VALUE; } auto sessionInfo = abilityRecord->GetSessionInfo(); if (sessionInfo == nullptr) { TAG_LOGE(AAFwkTag::ABILITYMGR, "target session info is null, ability: %{public}s/%{public}s", - abilityRecord->GetElementName().GetBundleName().c_str(), - abilityRecord->GetElementName().GetAbilityName().c_str()); + abilityRecord->GetInfoBundleName().c_str(), + abilityRecord->GetInfoAbilityName().c_str()); return ERR_INVALID_VALUE; } @@ -5280,16 +5311,16 @@ int AbilityManagerService::CloseUIExtensionAbilityBySCB(const sptrGetElementName().GetBundleName().c_str(), - abilityRecord->GetElementName().GetAbilityName().c_str()); + abilityRecord->GetInfoBundleName().c_str(), + abilityRecord->GetInfoAbilityName().c_str()); return ERR_INVALID_VALUE; } sptr sessionProxy = iface_cast(sessionInfo->sessionToken); if (sessionProxy == nullptr) { TAG_LOGE(AAFwkTag::ABILITYMGR, "Get session proxy failed, ability: %{public}s/%{public}s", - abilityRecord->GetElementName().GetBundleName().c_str(), - abilityRecord->GetElementName().GetAbilityName().c_str()); + abilityRecord->GetInfoBundleName().c_str(), + abilityRecord->GetInfoAbilityName().c_str()); return ERR_INVALID_VALUE; } @@ -5450,8 +5481,8 @@ bool AbilityManagerService::CheckIsRemote(const std::string& deviceId) bool AbilityManagerService::CheckIfOperateRemote(const Want &want) { - std::string deviceId = want.GetElement().GetDeviceID(); - if (deviceId.empty() || want.GetElement().GetBundleName().empty() || + std::string deviceId = want.GetDeviceId(); + if (deviceId.empty() || want.GetBundle().empty() || want.GetElement().GetAbilityName().empty()) { TAG_LOGD(AAFwkTag::ABILITYMGR, "CheckIfOperateRemote: DeviceId or BundleName or GetAbilityName empty"); return false; @@ -5556,7 +5587,7 @@ int AbilityManagerService::MinimizeUIExtensionAbility(const sptr &e static_cast(abilityRecord->GetRecordId()) << OFFSET) | static_cast(extensionSessionInfo->persistentId); TAG_LOGD(AAFwkTag::ABILITYMGR, "UIExtension component id: %{public}" PRId64 ", element: %{public}s/%{public}s", - extensionSessionInfo->uiExtensionComponentId, extensionSessionInfo->want.GetElement().GetBundleName().c_str(), + extensionSessionInfo->uiExtensionComponentId, extensionSessionInfo->want.GetBundle().c_str(), extensionSessionInfo->want.GetElement().GetAbilityName().c_str()); connectManager->BackgroundAbilityWindowLocked(targetRecord, extensionSessionInfo); return ERR_OK; @@ -5653,7 +5684,7 @@ int32_t AbilityManagerService::ConnectAbilityCommon( } if (AppUtils::GetInstance().IsForbidStart()) { - TAG_LOGW(AAFwkTag::ABILITYMGR, "forbid start: %{public}s", want.GetElement().GetBundleName().c_str()); + TAG_LOGW(AAFwkTag::ABILITYMGR, "forbid start: %{public}s", want.GetBundle().c_str()); return INNER_ERR; } if (specifiedFullTokenId != 0 && IPCSkeleton::GetCallingUid() != DMS_UID) { @@ -5661,9 +5692,9 @@ int32_t AbilityManagerService::ConnectAbilityCommon( specifiedFullTokenId = 0; } HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__); - XCOLLIE_TIMER_LESS_IGNORE(__PRETTY_FUNCTION__, !want.GetElement().GetDeviceID().empty()); + XCOLLIE_TIMER_LESS_IGNORE(__PRETTY_FUNCTION__, !want.GetDeviceId().empty()); TAG_LOGI(AAFwkTag::SERVICE_EXT, "element: %{public}s/%{public}s", - want.GetElement().GetBundleName().c_str(), want.GetElement().GetAbilityName().c_str()); + want.GetBundle().c_str(), want.GetElement().GetAbilityName().c_str()); CheckExtensionRateLimit(want); if (extensionType == AppExecFwk::ExtensionAbilityType::MODULAR_OBJECT) { auto ret = ModularObjectUtils::CheckRateLimit(); @@ -5817,14 +5848,14 @@ int AbilityManagerService::ConnectUIExtensionAbility(const Want &want, const spt const sptr &sessionInfo, int32_t userId, sptr connectInfo) { if (AppUtils::GetInstance().IsForbidStart()) { - TAG_LOGW(AAFwkTag::ABILITYMGR, "forbid start: %{public}s", want.GetElement().GetBundleName().c_str()); + TAG_LOGW(AAFwkTag::ABILITYMGR, "forbid start: %{public}s", want.GetBundle().c_str()); return INNER_ERR; } HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__); XCOLLIE_TIMER_LESS(__PRETTY_FUNCTION__); TAG_LOGI(AAFwkTag::UI_EXT, "ConnectUIExtensionAbility bundlename: %{public}s, ability is %{public}s, userId is %{private}d", - want.GetElement().GetBundleName().c_str(), want.GetElement().GetAbilityName().c_str(), userId); + want.GetBundle().c_str(), want.GetElement().GetAbilityName().c_str(), userId); CHECK_POINTER_AND_RETURN(connect, ERR_INVALID_VALUE); CHECK_POINTER_AND_RETURN(connect->AsObject(), ERR_INVALID_VALUE); CHECK_POINTER_AND_RETURN(sessionInfo, ERR_INVALID_VALUE); @@ -5873,7 +5904,7 @@ int AbilityManagerService::ConnectUIExtensionAbility(const Want &want, const spt if (!uri.empty()) { // if the want include uri, it may only has uri information. TAG_LOGI(AAFwkTag::UI_EXT, "called. uri:%{public}s/%{public}s, userId %{public}d", - abilityWant.GetElement().GetBundleName().c_str(), + abilityWant.GetBundle().c_str(), abilityWant.GetElement().GetAbilityName().c_str(), validUserId); AppExecFwk::ExtensionAbilityInfo extensionInfo; auto bms = AbilityUtil::GetBundleManagerHelper(); @@ -5914,8 +5945,8 @@ EventInfo AbilityManagerService::BuildEventInfo(const Want &want, int32_t userId { EventInfo eventInfo; eventInfo.userId = userId; - eventInfo.bundleName = want.GetElement().GetBundleName(); - eventInfo.moduleName = want.GetElement().GetModuleName(); + eventInfo.bundleName = want.GetBundle(); + eventInfo.moduleName = want.GetModuleName(); eventInfo.abilityName = want.GetElement().GetAbilityName(); return eventInfo; } @@ -6034,7 +6065,7 @@ int32_t AbilityManagerService::ConnectLocalAbility(const Want &want, const int32 return TARGET_ABILITY_NOT_SERVICE; } // LCOV_EXCL_STOP - bool isVpn = abilityInfo.extensionAbilityType != AppExecFwk::ExtensionAbilityType::VPN; + bool isVpn = abilityInfo.extensionAbilityType == AppExecFwk::ExtensionAbilityType::VPN; if (callerToken && extensionType == AppExecFwk::ExtensionAbilityType::SERVICE && !isService && !isVpn) { TAG_LOGE(AAFwkTag::SERVICE_EXT, "ability, type not service"); return TARGET_ABILITY_NOT_SERVICE; @@ -6176,7 +6207,7 @@ int AbilityManagerService::ConnectRemoteAbility(Want &want, const sptr AbilityManagerService::GetWantSenderByUserId(const WantSenderI bool isSpecifyUserId = wantSenderInfo.userId >= 0; std::string bundleName = ""; if (!wantSenderInfo.allWants.empty()) { - bundleName = wantSenderInfo.allWants.back().want.GetElement().GetBundleName(); + bundleName = wantSenderInfo.allWants.back().want.GetBundle(); } bool isSACall = AAFwk::PermissionVerification::GetInstance()->IsSACall(); bool isSystemApp = AAFwk::PermissionVerification::GetInstance()->IsSystemAppCall(); @@ -6649,7 +6680,7 @@ sptr AbilityManagerService::GetWantSender( int32_t appIndex = 0; std::string bundleName = ""; if (!wantSenderInfo.allWants.empty()) { - bundleName = wantSenderInfo.allWants.back().want.GetElement().GetBundleName(); + bundleName = wantSenderInfo.allWants.back().want.GetBundle(); } if (!bundleName.empty()) { if (!isSpecifyUidBySa) { @@ -7430,8 +7461,8 @@ int AbilityManagerService::AttachAbilityThread( auto userId = abilityRecord->GetApplicationInfo().uid / BASE_USER_RANGE; auto abilityInfo = abilityRecord->GetAbilityInfo(); auto type = abilityInfo.type; - TAG_LOGI(AAFwkTag::ABILITYMGR, "%{public}s/%{public}s", abilityRecord->GetElementName().GetBundleName().c_str(), - abilityRecord->GetElementName().GetAbilityName().c_str()); + TAG_LOGI(AAFwkTag::ABILITYMGR, "%{public}s/%{public}s", abilityRecord->GetInfoBundleName().c_str(), + abilityRecord->GetInfoAbilityName().c_str()); // force timeout ability for test if (IsNeedTimeoutForTest(abilityInfo.name, AbilityRecord::ConvertAbilityState(AbilityState::INITIAL))) { TAG_LOGW(AAFwkTag::ABILITYMGR, @@ -8029,8 +8060,8 @@ int AbilityManagerService::AbilityTransitionDone(const sptr &toke auto callerPid = IPCSkeleton::GetCallingPid(); TAG_LOGI(AAFwkTag::ABILITYMGR, "AbilityTransitionDone, ability:%{public}s/%{public}s, state:%{public}d, callerPid:%{public}d", - abilityRecord->GetElementName().GetBundleName().c_str(), - abilityRecord->GetElementName().GetAbilityName().c_str(), state, callerPid); + abilityRecord->GetInfoBundleName().c_str(), + abilityRecord->GetInfoAbilityName().c_str(), state, callerPid); if (!JudgeSelfCalled(abilityRecord)) { return CHECK_PERMISSION_FAILED; } @@ -8103,8 +8134,8 @@ int AbilityManagerService::AbilityWindowConfigTransitionDone( } TAG_LOGI(AAFwkTag::ABILITYMGR, "ability:%{public}s/%{public}s", - abilityRecord->GetElementName().GetBundleName().c_str(), - abilityRecord->GetElementName().GetAbilityName().c_str()); + abilityRecord->GetInfoBundleName().c_str(), + abilityRecord->GetInfoAbilityName().c_str()); auto abilityInfo = abilityRecord->GetAbilityInfo(); auto type = abilityInfo.extensionAbilityType; if (type != AppExecFwk::ExtensionAbilityType::UI_SERVICE) { @@ -8415,13 +8446,13 @@ int AbilityManagerService::GenerateAbilityRequest(const Want &want, int requestC request.callerTokenRecordId = abilityRecord->GetRecordId(); } if (abilityRecord && abilityRecord->GetAppIndex() > AbilityRuntime::GlobalConstant::MAX_APP_CLONE_INDEX && - abilityRecord->GetApplicationInfo().bundleName == want.GetElement().GetBundleName()) { + abilityRecord->GetApplicationInfo().bundleName == want.GetBundle()) { (const_cast(want)).SetParam(AbilityRuntime::ServerConstant::DLP_INDEX, abilityRecord->GetAppIndex()); (const_cast(want)).SetParam(DLP_PARAMS_SECURITY_FLAG, abilityRecord->GetSecurityFlag()); } if (abilityRecord != nullptr && - abilityRecord->GetApplicationInfo().bundleName == want.GetElement().GetBundleName() && + abilityRecord->GetApplicationInfo().bundleName == want.GetBundle() && (!want.HasParameter(Want::PARAM_APP_CLONE_INDEX_KEY) || want.GetIntParam(Want::PARAM_APP_CLONE_INDEX_KEY, 0) == abilityRecord->GetAppIndex())) { (const_cast(want)).SetParam(AbilityConfig::DEBUG_APP, abilityRecord->IsDebugApp()); @@ -8437,7 +8468,7 @@ int AbilityManagerService::GenerateAbilityRequest(const Want &want, int requestC } auto abilityInfo = StartAbilityUtils::startAbilityInfo; - if (abilityInfo == nullptr || abilityInfo->GetAppBundleName() != want.GetElement().GetBundleName()) { + if (abilityInfo == nullptr || abilityInfo->GetAppBundleName() != want.GetBundle()) { int32_t appIndex = 0; if (!StartAbilityUtils::GetAppIndex(want, callerToken, appIndex)) { return ERR_APP_CLONE_INDEX_INVALID; @@ -8556,7 +8587,7 @@ int AbilityManagerService::GenerateExtensionAbilityRequest(const Want &want, Abi } } if (abilityRecord && abilityRecord->GetAppIndex() > AbilityRuntime::GlobalConstant::MAX_APP_CLONE_INDEX && - abilityRecord->GetApplicationInfo().bundleName == want.GetElement().GetBundleName()) { + abilityRecord->GetApplicationInfo().bundleName == want.GetBundle()) { (const_cast(want)).SetParam(AbilityRuntime::ServerConstant::DLP_INDEX, abilityRecord->GetAppIndex()); (const_cast(want)).SetParam(DLP_PARAMS_SECURITY_FLAG, abilityRecord->GetSecurityFlag()); } @@ -8568,7 +8599,7 @@ int AbilityManagerService::GenerateExtensionAbilityRequest(const Want &want, Abi } auto abilityInfo = StartAbilityUtils::startAbilityInfo; - if (abilityInfo == nullptr || abilityInfo->GetAppBundleName() != want.GetElement().GetBundleName()) { + if (abilityInfo == nullptr || abilityInfo->GetAppBundleName() != want.GetBundle()) { int32_t appIndex = 0; if (!StartAbilityUtils::GetAppIndex(want, callerToken, appIndex)) { return ERR_APP_CLONE_INDEX_INVALID; @@ -8624,7 +8655,7 @@ int32_t AbilityManagerService::InitialAbilityRequest(AbilityRequest &request, int AbilityManagerService::StopServiceAbility(const Want &want, int32_t userId, const sptr &token) { HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__); - XCOLLIE_TIMER_LESS_IGNORE(__PRETTY_FUNCTION__, !want.GetElement().GetDeviceID().empty()); + XCOLLIE_TIMER_LESS_IGNORE(__PRETTY_FUNCTION__, !want.GetDeviceId().empty()); TAG_LOGD(AAFwkTag::ABILITYMGR, "call."); auto isSaCall = AAFwk::PermissionVerification::GetInstance()->IsSACall(); @@ -8680,8 +8711,8 @@ void AbilityManagerService::OnAbilityDied(std::shared_ptr ability { CHECK_POINTER(abilityRecord); TAG_LOGI(AAFwkTag::ABILITYMGR, "On ability died: %{public}s/%{public}s, %{public}d, %{public}" PRId64, - abilityRecord->GetElementName().GetBundleName().c_str(), - abilityRecord->GetElementName().GetAbilityName().c_str(), + abilityRecord->GetInfoBundleName().c_str(), + abilityRecord->GetInfoAbilityName().c_str(), abilityRecord->GetRecordId(), abilityRecord->GetAbilityRecordId()); if (abilityRecord->GetToken()) { @@ -9104,6 +9135,15 @@ void AbilityManagerService::HandleShareDataTimeOut(int64_t uniqueId) } } +void AbilityManagerService::HandleSkillExecuteTimeOut(int64_t requestCodeSeq) +{ + TAG_LOGI(AAFwkTag::ABILITYMGR, "called, seq:%{public}" PRId64, requestCodeSeq); + auto skillExecuteManager = DelayedSingleton::GetInstance(); + if (skillExecuteManager != nullptr) { + skillExecuteManager->OnTimeout(requestCodeSeq); + } +} + int32_t AbilityManagerService::GetShareDataPairAndReturnData(std::shared_ptr abilityRecord, const int32_t &resultCode, const int32_t &uniqueId, WantParams &wantParam) { @@ -9582,8 +9622,6 @@ std::function AbilityManagerService::GetUserScreenUnlockCallback( TAG_LOGE(AAFwkTag::ABILITYMGR, "Invalid abilityMgr pointer."); return; } - AbilityRuntime::UserController::GetInstance().SetUserLockStatus(userId, - AbilityRuntime::UserController::UserLockStatus::USER_UNLOCKED); auto taskHandler = abilityMgr->GetTaskHandler(); if (taskHandler == nullptr) { TAG_LOGE(AAFwkTag::ABILITYMGR, "taskHandler nullptr"); @@ -9827,23 +9865,27 @@ int AbilityManagerService::ReleaseRemoteAbility(const sptr &conne } int AbilityManagerService::StartAbilityByCall(const Want &want, const sptr &connect, - const sptr &callerToken, int32_t accountId, bool isSilent, bool promotePriority, - bool isVisible) + const sptr &callerToken, int32_t accountId, + bool isSilent, bool promotePriority, bool isVisible, uint64_t specifiedFullTokenId) { std::string errMsg; return StartAbilityByCallWithErrMsg(want, connect, callerToken, accountId, errMsg, isSilent, promotePriority, - isVisible); + isVisible, specifiedFullTokenId); } int AbilityManagerService::StartAbilityByCallWithErrMsg(const Want &want, const sptr &connect, const sptr &callerToken, int32_t accountId, std::string &errMsg, bool isSilent, - bool promotePriority, bool isVisible) + bool promotePriority, bool isVisible, uint64_t specifiedFullTokenId) { + if (specifiedFullTokenId != 0 && IPCSkeleton::GetCallingUid() != DMS_UID) { + TAG_LOGW(AAFwkTag::ABILITYMGR, "specifiedFullTokenId only support for DMS"); + specifiedFullTokenId = 0; + } if (AppUtils::GetInstance().IsForbidStart()) { - TAG_LOGW(AAFwkTag::ABILITYMGR, "forbid start: %{public}s", want.GetElement().GetBundleName().c_str()); + TAG_LOGW(AAFwkTag::ABILITYMGR, "forbid start: %{public}s", want.GetBundle().c_str()); return INNER_ERR; } - XCOLLIE_TIMER_LESS_IGNORE(__PRETTY_FUNCTION__, !want.GetElement().GetDeviceID().empty()); + XCOLLIE_TIMER_LESS_IGNORE(__PRETTY_FUNCTION__, !want.GetDeviceId().empty()); TAG_LOGD(AAFwkTag::ABILITYMGR, "call"); int paramCheckResult = AbilityStartByCallHelper::CheckParam(connect, errMsg); if (paramCheckResult != ERR_OK) { @@ -9914,6 +9956,7 @@ int AbilityManagerService::StartAbilityByCallWithErrMsg(const Want &want, const abilityRequest.callerToken = callerToken; abilityRequest.want = want; abilityRequest.connect = connect; + abilityRequest.specifiedFullTokenId = specifiedFullTokenId; abilityRequest.promotePriority = PermissionVerification::GetInstance()->IsSACall() && promotePriority; result = GenerateAbilityRequest(want, -1, abilityRequest, callerToken, oriValidUserId); if (result != ERR_OK) { @@ -10752,9 +10795,9 @@ void AbilityManagerService::ScheduleRecoverAbility(const sptr& to if (want != nullptr) { TAG_LOGD(AAFwkTag::ABILITYMGR, "BundleName:%{public}s targetBundleName:%{public}s.", - appInfo.bundleName.c_str(), want->GetElement().GetBundleName().c_str()); - if (want->GetElement().GetBundleName().empty() || - (appInfo.bundleName.compare(want->GetElement().GetBundleName()) != 0)) { + appInfo.bundleName.c_str(), want->GetBundle().c_str()); + if (want->GetBundle().empty() || + (appInfo.bundleName.compare(want->GetBundle()) != 0)) { TAG_LOGE(AAFwkTag::ABILITYMGR, "appRecovery bundleName not match, not recovery ability"); ReportAppRecoverResult(record->GetUid(), appInfo, abilityInfo.name, "FAIL_BUNDLE_NAME_NOT_MATCH"); return; @@ -10767,7 +10810,7 @@ void AbilityManagerService::ScheduleRecoverAbility(const sptr& to auto bms = AbilityUtil::GetBundleManagerHelper(); CHECK_POINTER_IS_NULLPTR(bms); AppExecFwk::BundleInfo bundleInfo; - auto bundleName = want->GetElement().GetBundleName(); + auto bundleName = want->GetBundle(); int32_t userId = AbilityRuntime::UserController::GetInstance().GetCallerUserId(); bool ret = IN_PROCESS_CALL( bms->GetBundleInfo(bundleName, AppExecFwk::BundleFlag::GET_BUNDLE_WITH_ABILITIES, bundleInfo, @@ -10838,7 +10881,6 @@ void AbilityManagerService::StartSwitchUserDialog() #endif // SUPPORT_GRAPHICS } - void AbilityManagerService::StartSwitchUserDialogInner(const Want &want, int32_t lastUserId) { TAG_LOGD(AAFwkTag::ABILITYMGR, "Start switch user dialog inner come"); @@ -12263,7 +12305,7 @@ int AbilityManagerService::CheckCallServicePermission(const AbilityRequest &abil { if (abilityRequest.want.GetIntParam(Want::PARAM_RESV_CALLER_UID, IPCSkeleton::GetCallingUid()) == AppUtils::GetInstance().GetCollaboratorBrokerUID() && - abilityRequest.want.GetElement().GetBundleName() == AppUtils::GetInstance().GetBrokerDelegateBundleName()) { + abilityRequest.want.GetBundle() == AppUtils::GetInstance().GetBrokerDelegateBundleName()) { auto collaborator = GetCollaborator(CollaboratorType::RESERVE_TYPE); if (collaborator != nullptr) { TAG_LOGI(AAFwkTag::ABILITYMGR, "Collaborator CheckCallAbilityPermission"); @@ -12389,7 +12431,9 @@ int AbilityManagerService::CheckCallServiceExtensionPermission(const AbilityRequ verificationInfo.visible = abilityRequest.abilityInfo.visible; verificationInfo.withContinuousTask = IsBackgroundTaskUid(IPCSkeleton::GetCallingUid()); verificationInfo.isBackgroundCall = false; - verificationInfo.specifyTokenId = static_cast(abilityRequest.specifyTokenId); + verificationInfo.specifyTokenId = (abilityRequest.specifiedFullTokenId != 0) ? + static_cast(abilityRequest.specifiedFullTokenId) : + static_cast(abilityRequest.specifyTokenId); if (isParamStartAbilityEnable_) { bool stopContinuousTaskFlag = ShouldPreventStartAbility(abilityRequest); if (stopContinuousTaskFlag) { @@ -12620,7 +12664,7 @@ bool AbilityManagerService::CheckUIExtensionCallerIsForeground(const AbilityRequ } TAG_LOGE(AAFwkTag::ABILITYMGR, "caller app not foreground, can't start %{public}s/%{public}s", - abilityRequest.want.GetElement().GetBundleName().c_str(), + abilityRequest.want.GetBundle().c_str(), abilityRequest.want.GetElement().GetAbilityName().c_str()); return false; } @@ -12836,6 +12880,8 @@ int AbilityManagerService::CheckStartByCallPermission(const AbilityRequest &abil verificationInfo.accessTokenId = abilityRequest.appInfo.accessTokenId; verificationInfo.visible = abilityRequest.abilityInfo.visible; verificationInfo.withContinuousTask = IsBackgroundTaskUid(IPCSkeleton::GetCallingUid()); + verificationInfo.specifiedFullTokenId = static_cast(abilityRequest.specifiedFullTokenId); + if (IsCallFromBackground(abilityRequest, verificationInfo.isBackgroundCall, false) != ERR_OK) { return ERR_INVALID_VALUE; } @@ -12968,7 +13014,7 @@ bool AbilityManagerService::IsTargetPermission(const Want &want) const bundleName = info.grantBundleName; abilityName = info.grantServiceAbilityName; } - if (want.GetElement().GetBundleName() == bundleName && + if (want.GetBundle() == bundleName && want.GetElement().GetAbilityName() == abilityName) { return true; } @@ -13163,8 +13209,8 @@ int AbilityManagerService::CheckUIExtensionIsFocused(uint32_t uiExtensionTokenId auto topAbility = Token::GetAbilityRecordByToken(token); if (topAbility != nullptr) { TAG_LOGD(AAFwkTag::ABILITYMGR, "top ability: %{public}s/%{public}s, pid: %{public}d, tokenId: %{public}d", - topAbility->GetWant().GetElement().GetBundleName().c_str(), - topAbility->GetWant().GetElement().GetAbilityName().c_str(), topAbility->GetPid(), + topAbility->GetInfoBundleName().c_str(), + topAbility->GetInfoAbilityName().c_str(), topAbility->GetPid(), topAbility->GetApplicationInfo().accessTokenId); } @@ -13401,6 +13447,11 @@ int32_t AbilityManagerService::StartSpecifiedAbilityBySCB(const Want &want, cons TAG_LOGE(AAFwkTag::ABILITYMGR, "no sceneboard called, no allowed"); return ERR_PERMISSION_DENIED; } + std::string targetBundleName = want.GetBundle(); + if (!AbilityPermissionUtil::GetInstance().CheckStartUIAbilityByUserLockStatus(targetBundleName)) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "scb call, user lock"); + return ERR_BLOCK_START_FIRST_BOOT_SCREEN_UNLOCK; + } StartAbilityUtils::startSpecifiedBySCB = true; StartAbilityWrapParam param = { .want = want, @@ -13994,6 +14045,9 @@ int32_t AbilityManagerService::ExecuteIntentForDistributed(const Want &want, con TAG_LOGE(AAFwkTag::INTENT, "GenerateFromWant failed, not a valid insight intent"); return ERR_INVALID_VALUE; } + int32_t userId = want.GetIntParam(AbilityRuntime::INTENT_USER_ID, -1); + param.userId_ = userId; + auto paramCopy = std::make_shared(param); uint64_t key = requestCode; @@ -14009,7 +14063,92 @@ int32_t AbilityManagerService::ExecuteIntentForDistributed(const Want &want, con (infos.decoratorType == AbilityRuntime::INSIGHT_INTENTS_DECORATOR_TYPE_PAGE) || (infos.decoratorType == AbilityRuntime::INSIGHT_INTENTS_DECORATOR_TYPE_FUNCTION); - return ERR_OK; + AbilityRuntime::ExecuteIntentCommonOptions options(ignoreAbilityName, infos, key); + options.srcDeviceId = srcDeviceId; + options.requestCode = requestCode; + options.specifiedFullTokenId = specifiedFullTokenId; + return ExecuteIntentCommon(nullptr, paramCopy, callerBundlename, options); +} + +int32_t AbilityManagerService::ExecuteIntentCommon(const sptr &callerToken, + const std::shared_ptr ¶m, const std::string &callerBundleName, + const AbilityRuntime::ExecuteIntentCommonOptions &options) +{ + bool openLinkExecuteFlag = options.infos.decoratorType == AbilityRuntime::INSIGHT_INTENTS_DECORATOR_TYPE_LINK; + bool isDistributed = !options.srcDeviceId.empty(); + + int32_t ret = DelayedSingleton::GetInstance()->CheckAndUpdateParam( + options.key, callerToken, param, callerBundleName, options.ignoreAbilityName, isDistributed, + options.srcDeviceId, options.requestCode, options.specifiedFullTokenId); + if (ret != ERR_OK) { + TAG_LOGE(AAFwkTag::INTENT, "CheckAndUpdateParam failed: %{public}d", ret); + return ret; + } + + TAG_LOGI(AAFwkTag::INTENT, "execute insight intent, bundleName: %{public}s, moduleName: %{public}s, " + "intentName: %{public}s, intentId:%{public}" PRIu64 ", openLinkExecuteFlag: %{public}d, " + "executeMode: %{public}d, userId: %{public}d, isDistributed: %{public}d", + param->bundleName_.c_str(), param->moduleName_.c_str(), param->insightIntentName_.c_str(), + param->insightIntentId_, openLinkExecuteFlag, param->executeMode_, param->userId_, isDistributed); + + if (openLinkExecuteFlag) { + auto info = options.infos; + return IntentOpenLinkInner(param, info, param->userId_); + } + Want want; + ret = InsightIntentExecuteManager::GenerateWant(param, options.infos, want); + if (ret != ERR_OK) { + TAG_LOGE(AAFwkTag::INTENT, "GenerateWant failed: %{public}d", ret); + DelayedSingleton::GetInstance()->RemoveExecuteIntent(param->insightIntentId_); + return ret; + } + + int32_t callerUserId = AbilityRuntime::UserController::GetInstance().GetCallerUserId(); + EventInfo eventInfo = BuildEventInfo(want, callerUserId); + switch (param->executeMode_) { + case AppExecFwk::ExecuteMode::UI_ABILITY_FOREGROUND: + TAG_LOGI(AAFwkTag::INTENT, "ExecuteMode UI_ABILITY_FOREGROUND."); + ret = StartAbilityWithInsightIntent( + want, param->userId_, DEFAULT_INVAL_VALUE, options.specifiedFullTokenId); + if (ret != ERR_OK) { + eventInfo.errReason = "StartAbilityWithInsightIntent error"; + SendIntentReport(eventInfo, ret, param->insightIntentName_); + } + break; + case AppExecFwk::ExecuteMode::UI_ABILITY_BACKGROUND: { + TAG_LOGI(AAFwkTag::INTENT, "ExecuteMode UI_ABILITY_BACKGROUND."); + ret = StartAbilityByCallWithInsightIntent( + want, callerToken, *param, param->userId_, options.specifiedFullTokenId); + if (ret != ERR_OK) { + eventInfo.errReason = "StartAbilityByCallWithInsightIntent error"; + SendIntentReport(eventInfo, ret, param->insightIntentName_); + } + break; + } + case AppExecFwk::ExecuteMode::UI_EXTENSION_ABILITY: + TAG_LOGE(AAFwkTag::INTENT, "executeMode UI_EXTENSION_ABILITY not supported"); + ret = ERR_INVALID_OPERATION; + break; + case AppExecFwk::ExecuteMode::SERVICE_EXTENSION_ABILITY: + TAG_LOGI(AAFwkTag::INTENT, "ExecuteMode SERVICE_EXTENSION_ABILITY."); + ret = StartExtensionAbilityWithInsightIntent( + want, AppExecFwk::ExtensionAbilityType::SERVICE, param->userId_); + if (ret != ERR_OK) { + eventInfo.errReason = "StartExtensionAbilityWithInsightIntent error"; + SendIntentReport(eventInfo, ret, param->insightIntentName_); + } + break; + default: + TAG_LOGE(AAFwkTag::INTENT, "ExecuteMode invalid: %{public}d", param->executeMode_); + ret = ERR_INVALID_VALUE; + break; + } + + if (ret != ERR_OK) { + DelayedSingleton::GetInstance()->RemoveExecuteIntent(param->insightIntentId_); + } + TAG_LOGI(AAFwkTag::INTENT, "ExecuteIntentCommon done, ret: %{public}d.", ret); + return ret; } int32_t AbilityManagerService::ExecuteIntent(uint64_t key, const sptr &callerToken, @@ -14019,6 +14158,7 @@ int32_t AbilityManagerService::ExecuteIntent(uint64_t key, const sptr(param); - int32_t ret = DelayedSingleton::GetInstance()->CheckAndUpdateParam(key, callerToken, - paramPtr, callerBundlename, ignoreAbilityName); + + int32_t ret = DelayedSingleton::GetInstance()->CheckAndUpdateParam( + key, callerToken, paramPtr, callerBundlename, ignoreAbilityName); if (ret != ERR_OK) { return ret; } + TAG_LOGI(AAFwkTag::INTENT, "execute insight intent, bundleName: %{public}s, moduleName: %{public}s, " - "intentName: %{public}s, intentId:%{public}" PRIu64", openLinkExecuteFlag: %{public}d, executeMode: %{public}d, " - "userId: %{public}d", - param.bundleName_.c_str(), param.moduleName_.c_str(), param.insightIntentName_.c_str(), param.insightIntentId_, - openLinkExecuteFlag, param.executeMode_, param.userId_); + "intentName: %{public}s, intentId:%{public}" PRIu64 ", openLinkExecuteFlag: %{public}d, " + "executeMode: %{public}d, userId: %{public}d, deviceId: %{private}s", + param.bundleName_.c_str(), param.moduleName_.c_str(), param.insightIntentName_.c_str(), + paramPtr->insightIntentId_, openLinkExecuteFlag, param.executeMode_, param.userId_, param.deviceId_.c_str()); - if (openLinkExecuteFlag) { - return IntentOpenLinkInner(paramPtr, infos, param.userId_); - } - - Want want; - ret = InsightIntentExecuteManager::GenerateWant(paramPtr, infos, want); - if (ret != ERR_OK) { - return ret; - } - int32_t callerUserId = AbilityRuntime::UserController::GetInstance().GetCallerUserId(); - EventInfo eventInfo = BuildEventInfo(want, callerUserId); - switch (param.executeMode_) { - case AppExecFwk::ExecuteMode::UI_ABILITY_FOREGROUND: - TAG_LOGI(AAFwkTag::INTENT, "ExecuteMode UI_ABILITY_FOREGROUND."); - ret = StartAbilityWithInsightIntent(want, param.userId_); - if (ret != ERR_OK) { - eventInfo.errReason = "StartAbilityWithInsightIntent error"; - SendIntentReport(eventInfo, ret, param.insightIntentName_); - } - break; - case AppExecFwk::ExecuteMode::UI_ABILITY_BACKGROUND: { - TAG_LOGI(AAFwkTag::INTENT, "ExecuteMode UI_ABILITY_BACKGROUND."); - ret = StartAbilityByCallWithInsightIntent(want, callerToken, param, param.userId_); - if (ret != ERR_OK) { - eventInfo.errReason = "StartAbilityByCallWithInsightIntent error"; - SendIntentReport(eventInfo, ret, param.insightIntentName_); - } - break; + if (!param.deviceId_.empty()) { + if (IsFloodAttackByCallerUid(IPCSkeleton::GetCallingUid())) { + TAG_LOGW(AAFwkTag::INTENT, "distributed intent flood attack"); + DelayedSingleton::GetInstance()->RemoveExecuteIntent( + paramPtr->insightIntentId_); + return INNER_ERR; } - case AppExecFwk::ExecuteMode::UI_EXTENSION_ABILITY: - TAG_LOGE(AAFwkTag::INTENT, "executeMode UI_EXTENSION_ABILITY not supported"); - ret = ERR_INVALID_OPERATION; - break; - case AppExecFwk::ExecuteMode::SERVICE_EXTENSION_ABILITY: - TAG_LOGI(AAFwkTag::INTENT, "ExecuteMode SERVICE_EXTENSION_ABILITY."); - ret = StartExtensionAbilityWithInsightIntent(want, AppExecFwk::ExtensionAbilityType::SERVICE, param.userId_); - if (ret != ERR_OK) { - eventInfo.errReason = "StartExtensionAbilityWithInsightIntent error"; - SendIntentReport(eventInfo, ret, param.insightIntentName_); - } - break; - default: - TAG_LOGE(AAFwkTag::ABILITYMGR, "invalid executeMode"); - ret = ERR_INVALID_OPERATION; - break; + + bool hasDistributedPermission = PermissionVerification::GetInstance()->VerifyCallingPermission( + PermissionConstants::PERMISSION_EXECUTE_DISTRIBUTED_INTENT); + if (!hasDistributedPermission) { + TAG_LOGE(AAFwkTag::INTENT, "distributed intent permission denied"); + return CHECK_PERMISSION_FAILED; + } + Want want; + auto ret = InsightIntentExecuteManager::GenerateWant(paramPtr, infos, want); + auto element = want.GetElement(); + if (ret != ERR_OK) { + TAG_LOGE(AAFwkTag::INTENT, "GenerateWant failed: %{public}d", ret); + DelayedSingleton::GetInstance()->RemoveExecuteIntent(paramPtr->insightIntentId_); + return ret; + } + TAG_LOGI(AAFwkTag::INTENT, "GenerateWant success"); + IntentCallerInfo callerInfo; + callerInfo.callerUid = IPCSkeleton::GetCallingUid(); + callerInfo.requestCode = paramPtr->insightIntentId_; + callerInfo.accessToken = IPCSkeleton::GetCallingTokenID(); + sptr callback = new (std::nothrow) AAFwk::RemoteIntentResultCallback(); + DistributedClient dmsClient; + want.SetDeviceId(param.deviceId_); + if (dmsClient.StartRemoteIntent(want, callerInfo, callback) != ERR_OK) { + TAG_LOGE(AAFwkTag::INTENT, "StartRemoteIntent failed"); + DelayedSingleton::GetInstance()->RemoveExecuteIntent( + paramPtr->insightIntentId_); + return ERR_INTENT_CONNECTION_FAILED; + } + return ERR_OK; + } + + AbilityRuntime::ExecuteIntentCommonOptions options(ignoreAbilityName, infos, key); + return ExecuteIntentCommon(callerToken, paramPtr, callerBundlename, options); +} + +bool AbilityManagerService::IsFloodAttackByCallerUid(int32_t callerUid) +{ + int64_t now = std::chrono::duration_cast( + std::chrono::steady_clock::now().time_since_epoch()).count(); + std::lock_guard lock(floodAttackMutex_); + auto &records = floodAttackStatistics_[callerUid]; + while (!records.empty() && (now - records.front() > FLOOD_ATTACK_INTERVAL_MAX)) { + records.pop_front(); } - if (ret == START_ABILITY_WAITING) { - TAG_LOGI(AAFwkTag::INTENT, "Top ability is foregrounding. The intent will be queued for execution"); - ret = ERR_OK; + TAG_LOGD(AAFwkTag::INTENT, "records size is :%{public}zu", records.size()); + if (records.size() >= FLOOD_ATTACK_NUMBER_MAX) { + return true; } - if (ret != ERR_OK) { - DelayedSingleton::GetInstance()->RemoveExecuteIntent(paramPtr->insightIntentId_); - } - TAG_LOGI(AAFwkTag::INTENT, "ExecuteIntent done, ret: %{public}d.", ret); - return ret; + records.emplace_back(now); + return false; } ErrCode AbilityManagerService::QueryEntityInfo(uint64_t key, sptr callerToken, @@ -14194,7 +14341,8 @@ int32_t AbilityManagerService::OnExecuteIntent(AbilityRequest &abilityRequest, return ERR_OK; } -int32_t AbilityManagerService::StartAbilityWithInsightIntent(const Want &want, int32_t userId, int requestCode) +int32_t AbilityManagerService::StartAbilityWithInsightIntent(const Want &want, int32_t userId, int requestCode, + uint64_t specifiedFullTokenId) { HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__); bool startWithAccount = want.GetBoolParam(START_ABILITY_TYPE, false); @@ -14210,7 +14358,8 @@ int32_t AbilityManagerService::StartAbilityWithInsightIntent(const Want &want, i StartAbilityWrapParam startAbilityWrapParam = { .want = want, .requestCode = requestCode, - .userId = userId + .userId = userId, + .specifiedFullTokenId = specifiedFullTokenId, }; int32_t ret = StartAbilityWrap(startAbilityWrapParam); if (ret != ERR_OK) { @@ -14227,7 +14376,8 @@ int32_t AbilityManagerService::StartExtensionAbilityWithInsightIntent(const Want } int32_t AbilityManagerService::StartAbilityByCallWithInsightIntent(const Want &want, - const sptr &callerToken, const InsightIntentExecuteParam ¶m, int32_t userId) + const sptr &callerToken, + const InsightIntentExecuteParam ¶m, int32_t userId, uint64_t specifiedFullTokenId) { TAG_LOGI(AAFwkTag::INTENT, "called"); sptr connect = sptr::MakeSptr(); @@ -14245,6 +14395,7 @@ int32_t AbilityManagerService::StartAbilityByCallWithInsightIntent(const Want &w abilityRequest.startSetting = nullptr; abilityRequest.want = want; abilityRequest.connect = connect; + abilityRequest.specifiedFullTokenId = specifiedFullTokenId; int32_t result = GenerateAbilityRequest(want, -1, abilityRequest, callerToken, GetValidUserId(userId)); if (result != ERR_OK) { TAG_LOGE(AAFwkTag::INTENT, "generate ability request error"); @@ -14258,7 +14409,8 @@ int32_t AbilityManagerService::StartAbilityByCallWithInsightIntent(const Want &w UpdateCallerInfoUtil::GetInstance().UpdateCallerInfo(abilityRequest.want, callerToken); result = OnExecuteIntent(abilityRequest, targetRecord); } else { - result = StartAbilityByCall(want, connect, callerToken, oriValidUserId); + result = StartAbilityByCall(want, connect, callerToken, oriValidUserId, + false, false, false, specifiedFullTokenId); } ResSchedUtil::GetInstance().ReportAbilityIntentExemptionInfoToRSS(abilityRequest.uid, 0); DelayedSingleton::GetInstance()->SetIntentExemptionInfo( @@ -14320,13 +14472,208 @@ int32_t AbilityManagerService::ExecuteInsightIntentDone(const sptrGrantUriPermission(result.uris, result.flags, callerBundleName, initiatorTokenId); } #endif // SUPPORT_UPMS - + int32_t callerUid = IPCSkeleton::GetCallingUid(); + uint32_t accessToken = IPCSkeleton::GetCallingTokenID(); ret = DelayedSingleton::GetInstance()->ExecuteIntentDone( - intentId, result.innerErr, result); + intentId, result.innerErr, result, callerUid, accessToken); FreezeUtil::GetInstance().AddLifecycleEvent(token, "ExecuteInsightIntentDone end"); return ret; } +int32_t AbilityManagerService::ExecuteInAppSkill(const std::string &bundleName, const std::string &moduleName, + const std::string &skillName, const std::string &arkTSPath, + const std::string &funcName, const std::shared_ptr &skillArgs, + const sptr &callback) +{ + TAG_LOGD(AAFwkTag::ABILITYMGR, "execute in-app skill called"); + + int32_t userId = IPCSkeleton::GetCallingUid() / BASE_USER_RANGE; + userId = userId != 0 ? userId : 100; + uint32_t callerTokenId = IPCSkeleton::GetCallingTokenID(); + std::string callerBundleName = InsightIntentGetcallerBundleName(); + + // 1. Query skill configuration from bundle framework + AppExecFwk::SkillInfo skillInfo; + auto ret = DelayedSingleton::GetInstance()->QuerySkillInfo( + bundleName, moduleName, skillName, userId, skillInfo); + if (ret != ERR_OK) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "query skill info failed"); + return ret; + } + + // 2. Verify caller permissions + ret = DelayedSingleton::GetInstance()->CheckSkillPermission(skillInfo); + if (ret != ERR_OK) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "check skill permission failed"); + return ret; + } + + // 3. Create execute record with requestCode and callback + std::string requestCode = DelayedSingleton::GetInstance()->CreateExecuteRecord( + nullptr, bundleName, callerBundleName, callerTokenId, callback); + + // 4. Generate Want with abilityName, srcEntries and requestCode + Want want; + AppExecFwk::ExtensionAbilityType targetType = AppExecFwk::ExtensionAbilityType::UNSPECIFIED; + ret = DelayedSingleton::GetInstance()->GenerateSkillWant( + skillInfo, want, userId, requestCode, targetType, arkTSPath, funcName, skillArgs); + if (ret != ERR_OK) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "generate skill want failed"); + return ret; + } + + // 5. Launch target based on type + if (targetType == AppExecFwk::ExtensionAbilityType::SERVICE) { + return StartExtensionAbilityWithSkill(want, userId); + } + return StartAbilityByCallWithSkill(want, nullptr, userId); +} + +int32_t AbilityManagerService::ExecuteInAppSkillWithTokenId(const AppExecFwk::SkillExecuteRequest &request, + const sptr &callback) +{ + TAG_LOGD(AAFwkTag::ABILITYMGR, "execute in-app skill with tokenId called"); + + // Derive userId and callerBundleName from explicit callerTokenId + Security::AccessToken::HapTokenInfo hapInfo; + auto ret = Security::AccessToken::AccessTokenKit::GetHapTokenInfo(request.callerTokenId, hapInfo); + if (ret != ERR_OK) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "failed to get hap token info for callerTokenId"); + return ERR_INVALID_VALUE; + } + int32_t userId = hapInfo.userID; + std::string callerBundleName = hapInfo.bundleName; + + // 1. Query skill configuration from bundle framework + AppExecFwk::SkillInfo skillInfo; + ret = DelayedSingleton::GetInstance()->QuerySkillInfo( + request.bundleName, request.moduleName, request.skillName, userId, skillInfo); + if (ret != ERR_OK) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "query skill info failed"); + return ret; + } + + // 2. Verify caller permissions + ret = DelayedSingleton::GetInstance()->CheckSkillPermission(skillInfo); + if (ret != ERR_OK) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "check skill permission failed"); + return ret; + } + + // 3. Create execute record with requestCode and callback + std::string requestCode = DelayedSingleton::GetInstance()->CreateExecuteRecord( + nullptr, request.bundleName, callerBundleName, request.callerTokenId, callback); + + // 4. Generate Want with abilityName, srcEntries and requestCode + Want want; + AppExecFwk::ExtensionAbilityType targetType = AppExecFwk::ExtensionAbilityType::UNSPECIFIED; + ret = DelayedSingleton::GetInstance()->GenerateSkillWant( + skillInfo, want, userId, requestCode, targetType, + request.scriptPath, request.functionName, request.skillArgs); + if (ret != ERR_OK) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "generate skill want failed"); + return ret; + } + + // 5. Launch target based on type + if (targetType == AppExecFwk::ExtensionAbilityType::SERVICE) { + return StartExtensionAbilityWithSkill(want, userId); + } + return StartAbilityByCallWithSkill(want, nullptr, userId); +} + +int32_t AbilityManagerService::StartAbilityByCallWithSkill(const Want &want, + const sptr &callerToken, int32_t userId) +{ + TAG_LOGI(AAFwkTag::ABILITYMGR, "start ability by call with skill intent"); + sptr connect = sptr::MakeSptr(); + if (connect == nullptr) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "invalid connect"); + return ERR_INVALID_VALUE; + } + + AbilityRequest abilityRequest; + abilityRequest.callType = AbilityCallType::CALL_REQUEST_TYPE; + abilityRequest.callerUid = IPCSkeleton::GetCallingUid(); + abilityRequest.callerToken = callerToken; + abilityRequest.startSetting = nullptr; + abilityRequest.want = want; + abilityRequest.connect = connect; + int32_t oriValidUserId = GetValidUserId(userId); + int32_t result = GenerateAbilityRequest(want, -1, abilityRequest, callerToken, oriValidUserId); + if (result != ERR_OK) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "generate ability request error"); + return result; + } + + std::shared_ptr targetRecord; + if (IsAbilityStarted(abilityRequest, targetRecord, oriValidUserId)) { + TAG_LOGI(AAFwkTag::ABILITYMGR, "ability already started"); + UpdateCallerInfoUtil::GetInstance().UpdateCallerInfo(abilityRequest.want, callerToken); + if (targetRecord == nullptr || targetRecord->GetScheduler() == nullptr) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "null scheduler"); + return ERR_INVALID_VALUE; + } + targetRecord->GetScheduler()->ExecuteSkill(abilityRequest.want); + result = ERR_OK; + } else { + result = StartAbilityByCall(want, connect, callerToken, oriValidUserId); + } + TAG_LOGD(AAFwkTag::ABILITYMGR, "StartAbilityByCallWithSkill result:%{public}d", result); + return result; +} + +int32_t AbilityManagerService::StartExtensionAbilityWithSkill(const Want &want, int32_t userId) +{ + TAG_LOGI(AAFwkTag::ABILITYMGR, "start extension ability with skill intent"); + return StartExtensionAbilityInner(want, nullptr, userId, + AppExecFwk::ExtensionAbilityType::SERVICE, true); +} + +int32_t AbilityManagerService::ExecuteSkillDone(const sptr &token, + const std::string &requestCode, int32_t resultCode, + const AppExecFwk::SkillExecuteResult &result) +{ + TAG_LOGD(AAFwkTag::ABILITYMGR, + "execute skill done with token, requestCode:%{public}s code:%{public}d", + requestCode.c_str(), resultCode); + auto abilityRecord = Token::GetAbilityRecordByToken(token); + CHECK_POINTER_AND_RETURN_LOG(abilityRecord, ERR_INVALID_VALUE, "Ability record is nullptr."); + if (!JudgeSelfCalled(abilityRecord)) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "not self called"); + return CHECK_PERMISSION_FAILED; + } + std::string bundleName = abilityRecord->GetAbilityInfo().bundleName; + auto ret = DelayedSingleton::GetInstance()->ExecuteSkillDone( + requestCode, resultCode, result, bundleName); + if (ret != ERR_OK) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "ExecuteSkillDone failed, ret:%{public}d", ret); + } + return ret; +} + +int32_t AbilityManagerService::QuerySkillType(const std::string &bundleName, const std::string &moduleName, + const std::string &skillName, int32_t &skillType) +{ + TAG_LOGD(AAFwkTag::ABILITYMGR, + "query skill type, bundle:%{public}s module:%{public}s skill:%{public}s", + bundleName.c_str(), moduleName.c_str(), skillName.c_str()); + + int32_t userId = IPCSkeleton::GetCallingUid() / BASE_USER_RANGE; + userId = (userId == 0) ? DEFAULT_USER_ID : userId; + AppExecFwk::SkillInfo skillInfo; + auto ret = DelayedSingleton::GetInstance()->QuerySkillInfo( + bundleName, moduleName, skillName, userId, skillInfo); + if (ret != ERR_OK) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "query skill info failed"); + return ret; + } + + skillType = static_cast(skillInfo.skillType); + TAG_LOGI(AAFwkTag::ABILITYMGR, "skill type:%{public}d", skillType); + return ERR_OK; +} + int32_t AbilityManagerService::SetApplicationAutoStartupByEDM(const AutoStartupInfo &info, bool flag, bool isHiddenStart) { @@ -15043,7 +15390,7 @@ int32_t AbilityManagerService::RestartApp(const AAFwk::Want &want, bool isAppRec DelayedSingleton::GetInstance()->GetRunningProcessInfoByPid(callerPid, processInfo); int32_t callerUid = IPCSkeleton::GetCallingUid(); int32_t userId = callerUid / BASE_USER_RANGE; - auto result = CheckRestartAppWant(want, processInfo.appCloneIndex, userId); + auto result = CheckRestartAppWant(want.GetElement(), processInfo.appCloneIndex, userId); if (result != ERR_OK) { TAG_LOGE(AAFwkTag::ABILITYMGR, "checkRestartAppWant error"); return result; @@ -15097,9 +15444,10 @@ bool AbilityManagerService::IsRestartAppLimit() return RestartAppManager::GetInstance().IsRestartAppFrequent(key, now); } -int32_t AbilityManagerService::CheckRestartAppWant(const AAFwk::Want &want, int32_t appIndex, int32_t userId) +int32_t AbilityManagerService::CheckRestartAppWant( + const AppExecFwk::ElementName &elementName, int32_t appIndex, int32_t userId) { - std::string bundleName = want.GetElement().GetBundleName(); + std::string bundleName = elementName.GetBundleName(); if (!CheckCallingTokenId(bundleName, userId, appIndex)) { TAG_LOGE(AAFwkTag::ABILITYMGR, "no itself called, no allowed"); return AAFwk::ERR_RESTART_APP_INCORRECT_ABILITY; @@ -15111,10 +15459,10 @@ int32_t AbilityManagerService::CheckRestartAppWant(const AAFwk::Want &want, int3 TAG_LOGD(AAFwkTag::ABILITYMGR, "bundleName: %{public}s, abilityName: %{public}s, appIndex: %{public}d, userId: %{public}d", - want.GetElement().GetBundleName().c_str(), want.GetElement().GetAbilityName().c_str(), appIndex, userId); + bundleName.c_str(), elementName.GetAbilityName().c_str(), appIndex, userId); AppExecFwk::AbilityInfo abilityInfo; auto queryResult = IN_PROCESS_CALL( - bms->QueryCloneAbilityInfo(want.GetElement(), abilityInfoFlag, appIndex, abilityInfo, userId)); + bms->QueryCloneAbilityInfo(elementName, abilityInfoFlag, appIndex, abilityInfo, userId)); if (queryResult != ERR_OK || abilityInfo.name.empty() || abilityInfo.bundleName.empty() || abilityInfo.type != AbilityType::PAGE) { TAG_LOGE(AAFwkTag::ABILITYMGR, "ability invalid or not UIAbility"); @@ -15235,7 +15583,7 @@ bool AbilityManagerService::IsEmbeddedOpenAllowed(sptr callerToke TAG_LOGE(AAFwkTag::ABILITYMGR, "target not allowed free install"); return false; } - want.SetParam(Want::PARAM_RESV_CALLER_BUNDLE_NAME, callerAbility->GetElementName().GetBundleName()); + want.SetParam(Want::PARAM_RESV_CALLER_BUNDLE_NAME, callerAbility->GetBundleName()); auto erms = std::make_shared(); return erms->DoProcess(want, callerUserId); } @@ -15282,7 +15630,7 @@ int32_t AbilityManagerService::QueryAtomicServiceStartupRule(sptr return ERR_OK; } - want.SetParam(Want::PARAM_RESV_CALLER_BUNDLE_NAME, callerAbility->GetElementName().GetBundleName()); + want.SetParam(Want::PARAM_RESV_CALLER_BUNDLE_NAME, callerAbility->GetBundleName()); auto erms = std::make_shared(); sptr replaceWant = nullptr; ret = erms->QueryAtomicServiceStartupRule(want, callerToken, userId, rule, replaceWant); @@ -15438,8 +15786,8 @@ bool AbilityManagerService::ShouldPreventStartAbility(const AbilityRequest &abil return false; } TAG_LOGE(AAFwkTag::ABILITYMGR, "without start serviceExtension %{public}s/%{public}s permission", - abilityRecord->GetElementName().GetBundleName().c_str(), - abilityRecord->GetElementName().GetAbilityName().c_str()); + abilityRecord->GetInfoBundleName().c_str(), + abilityRecord->GetAbilityName().c_str()); ReportPreventStartAbilityResult(callerAbilityInfo, abilityInfo); return true; } @@ -15781,11 +16129,11 @@ int32_t AbilityManagerService::PreStartInner(const FreeInstallInfo& taskInfo) return errCode; } CHECK_POINTER_AND_RETURN(freeInstallManager_, ERR_INVALID_VALUE); - freeInstallManager_->SetFreeInstallTaskSessionId(taskInfo.want.GetElement().GetBundleName(), + freeInstallManager_->SetFreeInstallTaskSessionId(taskInfo.want.GetBundle(), taskInfo.want.GetElement().GetAbilityName(), taskInfo.want.GetStringParam(Want::PARAM_RESV_START_TIME), sessionId); - freeInstallManager_->SetPreStartMissionCallStatus(taskInfo.want.GetElement().GetBundleName(), + freeInstallManager_->SetPreStartMissionCallStatus(taskInfo.want.GetBundle(), taskInfo.want.GetElement().GetAbilityName(), taskInfo.want.GetStringParam(Want::PARAM_RESV_START_TIME), true); @@ -15811,7 +16159,7 @@ int32_t AbilityManagerService::StartUIAbilityByPreInstall(const FreeInstallInfo TAG_LOGE(AAFwkTag::ABILITYMGR, "session id empty"); return ERR_INVALID_VALUE; } - auto bundleName = want.GetElement().GetBundleName(); + auto bundleName = want.GetBundle(); auto abilityName = want.GetElement().GetAbilityName(); auto startTime = want.GetStringParam(Want::PARAM_RESV_START_TIME); TAG_LOGI(AAFwkTag::ABILITYMGR, "call" @@ -16100,8 +16448,11 @@ ErrCode AbilityManagerService::IntentOpenLinkInner(const std::shared_ptrbundleName_, userId, callerUid, accessToken); DelayedSingleton::GetInstance()->ExecuteIntentDone( - param->insightIntentId_, result.innerErr, result); + param->insightIntentId_, result.innerErr, result, callerUid, accessToken); return ERR_OK; } //mapping error code 16000019->16000050 @@ -16112,6 +16463,21 @@ ErrCode AbilityManagerService::IntentOpenLinkInner(const std::shared_ptrGetBundleInfo(bundleName, AppExecFwk::BundleFlag::GET_BUNDLE_WITH_ABILITIES, bundleInfo, userId)); + if (!ret) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "get bundleInfo preloading failed, userId:%{public}d", userId); + return; + } + callerUid = bundleInfo.uid; + accessToken = IPCSkeleton::GetCallingTokenID(); +} + ErrCode AbilityManagerService::OpenLink(const Want& want, sptr callerToken, int32_t userId, int32_t requestCode, bool hideFailureTipDialog) { @@ -16122,7 +16488,7 @@ ErrCode AbilityManagerService::OpenLinkInner(const Want& want, sptr lock(delayedStartPidsLock_); + if (delayedStartPids_.count(callingPid) > 0) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "StartAbilityDelayed already in progress for pid:%{public}d", callingPid); + return ERR_DELAYED_PROCESS_EXIT_HAS_OTHER_UIABILITY; + } + delayedStartPids_.insert(callingPid); + } + auto removeDelayedPid = [this, callingPid]() { + std::lock_guard lock(delayedStartPidsLock_); + delayedStartPids_.erase(callingPid); + }; + std::vector> tokens; + IN_PROCESS_CALL_WITHOUT_RET(DelayedSingleton::GetInstance()->GetAbilityRecordsByProcessID( + callingPid, tokens)); + AppExecFwk::RunningProcessInfo processInfo; + DelayedSingleton::GetInstance()->GetRunningProcessInfoByChildProcessPid(callingPid, processInfo); + auto targetBundleName = param.want.GetBundle(); + auto iter = std::find(processInfo.bundleNames.begin(), processInfo.bundleNames.end(), targetBundleName); + if (iter == processInfo.bundleNames.end()) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "The specified ability does not exist."); + removeDelayedPid(); + return TARGET_BUNDLE_NOT_EXIST; + } + for (const auto &token : tokens) { + auto abilityRecord = Token::GetAbilityRecordByToken(token); + if (abilityRecord && abilityRecord->GetAbilityInfo().type == AppExecFwk::AbilityType::PAGE && + !abilityRecord->IsTerminating()) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "current process still has UIAbility"); + removeDelayedPid(); + return ERR_DELAYED_PROCESS_EXIT_HAS_OTHER_UIABILITY; + } + } + + auto result = StartAbilityDelayedInner(param.want, processInfo, callingPid); + if (result != ERR_OK) { + removeDelayedPid(); + } + return result; +} + +int AbilityManagerService::CheckDelayedStartBelongToCaller(const Want &want, + const AppExecFwk::RunningProcessInfo &processInfo) +{ + auto appIndex = want.GetIntParam(Want::PARAM_APP_CLONE_INDEX_KEY, -1); + if (processInfo.appMode == AppExecFwk::MultiAppModeType::APP_CLONE && + appIndex >= 0 && appIndex != processInfo.appCloneIndex) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "The UIAbility not belog to caller"); + return ERROR_UIABILITY_NOT_BELONG_TO_CALLER; + } + auto instanceKey = want.GetStringParam(Want::APP_INSTANCE_KEY); + auto isCreating = want.GetBoolParam(Want::CREATE_APP_INSTANCE_KEY, false); + if ((!instanceKey.empty() && instanceKey != processInfo.instanceKey) || isCreating) { + TAG_LOGD(AAFwkTag::ABILITYMGR, "multi instance scene"); + return ERROR_UIABILITY_NOT_BELONG_TO_CALLER; + } + std::string targetBundleName = want.GetBundle(); + auto iter = std::find(processInfo.bundleNames.begin(), processInfo.bundleNames.end(), targetBundleName); + CHECK_TRUE_RETURN_RET(iter == processInfo.bundleNames.end(), ERROR_UIABILITY_NOT_BELONG_TO_CALLER, + "The UIAbility not belog to caller"); + return ERR_OK; +} + +int AbilityManagerService::StartAbilityDelayedInner(const Want &want, + const AppExecFwk::RunningProcessInfo &processInfo, int32_t callingPid) +{ + auto bundleMgrHelper = AbilityUtil::GetBundleManagerHelper(); + CHECK_POINTER_AND_RETURN(bundleMgrHelper, INNER_ERR); + AppExecFwk::AbilityInfo abilityInfo; + auto callerUserId = AbilityRuntime::UserController::GetInstance().GetCallerUserId(); + CHECK_TRUE_RETURN_RET(!IN_PROCESS_CALL(bundleMgrHelper->QueryAbilityInfo(want, + AppExecFwk::AbilityInfoFlag::GET_ABILITY_INFO_WITH_APPLICATION, callerUserId, abilityInfo)), + TARGET_BUNDLE_NOT_EXIST, "bundle or ability not exist"); + if (abilityInfo.type != AppExecFwk::AbilityType::PAGE) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "target is not UIAbility, type:%{public}d", + static_cast(abilityInfo.type)); + return ERROR_UIABILITY_NOT_BELONG_TO_CALLER; + } + auto result = CheckDelayedStartBelongToCaller(want, processInfo); + if (result != ERR_OK) { + return result; + } + StartOptions startOptions; + startOptions.SetCurrentProcessName(processInfo.processName_); + startOptions.processOptions = std::make_shared(); + StartSelfUIAbilityRecordGuard guard(callingPid, abilityInfo.applicationInfo.accessTokenId); + startOptions.processOptions->selfPid = callingPid; + AbilityUtil::RemoveShowModeKey(const_cast(want)); +#ifdef SUPPORT_SCREEN + DmsUtil::GetInstance().UpdateFlagForCollaboration(want); +#endif + result = StartUIAbilityForOptionWrap(want, startOptions, nullptr, false, + DEFAULT_INVAL_VALUE, DEFAULT_INVAL_VALUE, 0, false, false, true); + if (result != ERR_OK) { + if (result == ERR_NULL_INTERCEPTOR_EXECUTER || result == ERR_NULL_AFTER_CHECK_EXECUTER) { + return START_UI_ABILITIES_INTERCEPTOR_CHECK_FAILED; + } + return result; + } + auto appMgr = AppMgrUtil::GetAppMgr(); + if (appMgr != nullptr) { + appMgr->CancelDelayedExitTask(callingPid); + } + return result; +} + +int AbilityManagerService::StartSelfUIAbilityByAppContext(const Want &want) +{ + if (!AppUtils::GetInstance().IsSupportDelayedProcessExit()) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "device not supported"); + return ERR_CAPABILITY_NOT_SUPPORT; + } + if (AppUtils::GetInstance().IsForbidStart()) { + TAG_LOGW(AAFwkTag::ABILITYMGR, "forbid start: %{public}s", want.GetElement().GetBundleName().c_str()); + return INNER_ERR; + } + if (CheckIfOperateRemote(want)) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "StartUIAbilities not support StartRemoteAbility"); + return START_UI_ABILITIES_NOT_SUPPORT_OPERATE_REMOTE; + } + if (AbilityRuntime::StartupUtil::IsStartPlugin(want)) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "StartUIAbilities not support StartPlugin"); + return START_UI_ABILITIES_NOT_SUPPORT_START_PLUGIN; + } +#ifdef SUPPORT_SCREEN + if (ImplicitStartProcessor::IsImplicitStartAction(want)) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "StartUIAbilities not support implicit start"); + return START_UI_ABILITIES_NOT_SUPPORT_IMPLICIT_START; + } +#endif + XCOLLIE_TIMER_LESS(__PRETTY_FUNCTION__); + StartAbilityWrapParam param; + param.want = want; + return StartAbilityDelayed(param); +} + int AbilityManagerService::StartSelfUIAbilityWithStartOptions(const Want &want, const StartOptions &options) { if (AppUtils::GetInstance().IsForbidStart()) { - TAG_LOGW(AAFwkTag::ABILITYMGR, "forbid start: %{public}s", want.GetElement().GetBundleName().c_str()); + TAG_LOGW(AAFwkTag::ABILITYMGR, "forbid start: %{public}s", want.GetBundle().c_str()); return INNER_ERR; } XCOLLIE_TIMER_LESS(__PRETTY_FUNCTION__); @@ -16628,7 +17133,7 @@ int AbilityManagerService::StartSelfUIAbilityWithPidResult(const Want &want, Sta int AbilityManagerService::StartSelfUIAbilityWithToken(const Want &want, sptr callerToken) { if (AppUtils::GetInstance().IsForbidStart()) { - TAG_LOGW(AAFwkTag::ABILITYMGR, "forbid start: %{public}s", want.GetElement().GetBundleName().c_str()); + TAG_LOGW(AAFwkTag::ABILITYMGR, "forbid start: %{public}s", want.GetBundle().c_str()); return INNER_ERR; } XCOLLIE_TIMER_LESS(__PRETTY_FUNCTION__); @@ -16643,7 +17148,7 @@ int AbilityManagerService::StartSelfUIAbilityWithStartOptionsAndToken(const Want const StartOptions &options, sptr callerToken) { if (AppUtils::GetInstance().IsForbidStart()) { - TAG_LOGW(AAFwkTag::ABILITYMGR, "forbid start: %{public}s", want.GetElement().GetBundleName().c_str()); + TAG_LOGW(AAFwkTag::ABILITYMGR, "forbid start: %{public}s", want.GetBundle().c_str()); return INNER_ERR; } XCOLLIE_TIMER_LESS(__PRETTY_FUNCTION__); @@ -16666,9 +17171,12 @@ bool AbilityManagerService::CheckCrossUser(const int32_t userId, AppExecFwk::Ext if (AAFwk::UIExtensionWrapper::IsEnterpriseAdmin(extensionType) || JudgeMultiUserConcurrency(userId)) { return true; } - if (AppUtils::GetInstance().IsConnectSupportCrossUser() && (extensionType == AppExecFwk::ExtensionAbilityType::DATASHARE - || extensionType == AppExecFwk::ExtensionAbilityType::SERVICE)) { - return true; + if (extensionType == AppExecFwk::ExtensionAbilityType::DATASHARE + || extensionType == AppExecFwk::ExtensionAbilityType::SERVICE) { + if (AppUtils::GetInstance().IsConnectSupportCrossUser() || + AAFwk::PermissionVerification::GetInstance()->IsSACall()) { + return true; + } } return false; } @@ -16792,7 +17300,7 @@ int32_t AbilityManagerService::StartUIAbilityWithCallback(const Want &want, sptr sptr callback) { if (AppUtils::GetInstance().IsForbidStart()) { - TAG_LOGW(AAFwkTag::ABILITYMGR, "forbid start: %{public}s", want.GetElement().GetBundleName().c_str()); + TAG_LOGW(AAFwkTag::ABILITYMGR, "forbid start: %{public}s", want.GetBundle().c_str()); return INNER_ERR; } sptr effectiveCallback = callback; @@ -16941,7 +17449,8 @@ int32_t AbilityManagerService::GetInsightIntentInfoByBundleName( int32_t userId) { TAG_LOGI(AAFwkTag::INTENT, "GetInsightIntentInfoByBundleName"); - int32_t ret = DelayedSingleton::GetInstance()->CheckGetInsightIntenInfoPermission(); + int32_t ret = DelayedSingleton::GetInstance()->CheckGetInsightIntenInfoPermission( + bundleName); if (ret != ERR_OK) { TAG_LOGD(AAFwkTag::INTENT, "not system app or permission denied"); return ret; @@ -17034,7 +17543,8 @@ int32_t AbilityManagerService::GetInsightIntentInfoByIntentName( int32_t userId) { TAG_LOGI(AAFwkTag::INTENT, "GetInsightIntentInfoByIntentName"); - int32_t ret = DelayedSingleton::GetInstance()->CheckGetInsightIntenInfoPermission(); + int32_t ret = DelayedSingleton::GetInstance()->CheckGetInsightIntenInfoPermission( + bundleName); if (ret != ERR_OK) { TAG_LOGD(AAFwkTag::INTENT, "not system app or permission denied"); return ret; @@ -17322,7 +17832,11 @@ int32_t AbilityManagerService::SetOnNewWantSkipScenarios(sptr cal TAG_LOGE(AAFwkTag::ABILITYMGR, "invalid callerToken"); return ERR_INVALID_CALLER; } - record->SetOnNewWantSkipScenarios(scenarios); + if (record->GetAbilityRecordType() != AbilityRecordType::UI_ABILITY) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "invalid ability type"); + return ERR_OK; + } + (std::static_pointer_cast(record))->SetOnNewWantSkipScenarios(scenarios); return ERR_OK; } @@ -17361,6 +17875,14 @@ ErrCode AbilityManagerService::IsUIAbilityAlreadyExist(const Want &want, const s return uiAbilityManager->IsUIAbilityAlreadyExist(want, specifiedFlag, appIndex, instanceKey, launchMode); } +ErrCode AbilityManagerService::IsSpecifiedUIAbilityAlreadyExist(const Want &want, const std::string &specifiedFlag, + int32_t appIndex, const std::string &instanceKey) +{ + auto uiAbilityManager = GetUIAbilityManagerByUid(IPCSkeleton::GetCallingUid()); + CHECK_POINTER_AND_RETURN(uiAbilityManager, ERR_INVALID_VALUE); + return uiAbilityManager->IsSpecifiedUIAbilityAlreadyExist(want, specifiedFlag, appIndex, instanceKey); +} + bool AbilityManagerService::IsAppCloneOrMultiInstance(const Want &want, const std::shared_ptr callerRecord, int32_t &targetAppIndex, const std::string &callerInstanceKey) { @@ -17447,6 +17969,79 @@ ErrCode AbilityManagerService::StartSelfUIAbilityInCurrentProcess(const Want &wa return StartAbility(useWant, useStartOptions, callerToken); } +ErrCode AbilityManagerService::StartSelfUIAbilityInChildProcess( + const Want &want, const std::string &specifiedFlag, sptr callerToken) +{ + if (!AppUtils::GetInstance().IsSupportNativeUIAbility()) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "device not supported"); + return ERR_CAPABILITY_NOT_SUPPORT; + } + auto callerRecord = Token::GetAbilityRecordByToken(callerToken); + if (callerRecord == nullptr || !JudgeSelfCalled(callerRecord)) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "not self call"); + return ERR_INVALID_VALUE; + } + + AppExecFwk::AbilityInfo abilityInfo; + auto checkRet = CheckStartSelfUIAbilityInChildProcess(want, specifiedFlag, callerRecord, abilityInfo); + if (checkRet != ERR_OK) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "CheckStartSelfUIAbilityInChildProcess failed, ret: %{public}d", checkRet); + return checkRet; + } + + AAFwk::StartOptions startOptions; + startOptions.processOptions = std::make_shared(); + startOptions.processOptions->processMode = ProcessMode::NEW_PROCESS_ATTACH_TO_PARENT; + startOptions.processOptions->callingPid = IPCSkeleton::GetCallingPid(); + startOptions.SetCurrentProcessName(START_SELF_UI_ABILITY_IN_CHILD_PROCESS_FLAG); + + auto useWant = want; + if (abilityInfo.launchMode == AppExecFwk::LaunchMode::SPECIFIED) { + useWant.SetParam(KEY_SPECIFIED_FLAG, specifiedFlag); + } + return StartAbility(useWant, startOptions, callerToken); +} + +ErrCode AbilityManagerService::CheckStartSelfUIAbilityInChildProcess(const Want &want, const std::string &specifiedFlag, + const std::shared_ptr &callerRecord, AppExecFwk::AbilityInfo &abilityInfo) +{ + std::string targetBundleName = want.GetBundle(); + std::string targetAbilityName = want.GetElement().GetAbilityName(); + if (targetBundleName.empty() || targetAbilityName.empty()) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "implicit start not allowed"); + return START_UI_ABILITIES_NOT_SUPPORT_IMPLICIT_START; + } + CHECK_TRUE_RETURN_RET(targetBundleName != callerRecord->GetApplicationInfo().bundleName, + ERROR_UIABILITY_NOT_BELONG_TO_CALLER, "The UIAbility not belog to caller"); + int32_t appIndex = want.GetIntParam(AAFwk::Want::PARAM_APP_CLONE_INDEX_KEY, -1); + auto callerPid = IPCSkeleton::GetCallingPid(); + AppExecFwk::RunningProcessInfo processInfo; + DelayedSingleton::GetInstance()->GetRunningProcessInfoByChildProcessPid(callerPid, processInfo); + if (IsAppCloneOrMultiInstance(want, callerRecord, appIndex, processInfo.instanceKey)) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "not support app clone and multi instance"); + return ERROR_UIABILITY_NOT_BELONG_TO_CALLER; + } + auto bundleMgrHelper = AbilityUtil::GetBundleManagerHelper(); + CHECK_POINTER_AND_RETURN(bundleMgrHelper, INNER_ERR); + auto callerUserId = AbilityRuntime::UserController::GetInstance().GetCallerUserId(); + CHECK_TRUE_RETURN_RET(IN_PROCESS_CALL(bundleMgrHelper->QueryCloneAbilityInfo(want.GetElement(), + AppExecFwk::AbilityInfoFlag::GET_ABILITY_INFO_WITH_APPLICATION, appIndex, abilityInfo, callerUserId)) != ERR_OK, + TARGET_BUNDLE_NOT_EXIST, "The specified ability not exist"); + + CHECK_TRUE_RETURN_RET(abilityInfo.type != AppExecFwk::AbilityType::PAGE, + TARGET_BUNDLE_NOT_EXIST, "not UIAbility"); + if (specifiedFlag != "" && abilityInfo.launchMode == AppExecFwk::LaunchMode::SPECIFIED) { + auto ret = IsSpecifiedUIAbilityAlreadyExist(want, specifiedFlag, appIndex, processInfo.instanceKey); + if (ret != ERR_OK) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "UIAbility already exist"); + return ret; + } + } + CHECK_TRUE_RETURN_RET(processInfo.state_ != AppExecFwk::AppProcessState::APP_STATE_FOREGROUND, + NOT_TOP_ABILITY, "caller not foreground"); + return ERR_OK; +} + int32_t AbilityManagerService::ClearPreloadedUIExtensionAbility(int32_t extensionAbilityId, int32_t userId) { TAG_LOGD(AAFwkTag::UI_EXT, "ClearPreloadedUIExtensionAbility called, id: %{public}d, userId: %{public}d", @@ -17674,15 +18269,34 @@ void AbilityManagerService::HandleAppDiedForRecovery(const sptr& TAG_LOGW(AAFwkTag::ABILITYMGR,"now is %{public}lld,timestamp is %{public}lld,no recovery",static_cast(now),static_cast(stamp)); } else { appRecoveryHistory_[uid] = now; - AAFwk::Want *newWant=new AAFwk::Want(); - newWant->SetElementName(abilityInfo.bundleName, abilityInfo.name); - newWant->SetParam(AAFwk::Want::PARAM_ABILITY_RECOVERY_RESTART, true); - StartAbility(*newWant,userId); + Want newWant; + newWant.SetElementName(abilityInfo.bundleName, abilityInfo.name); + newWant.SetParam(Want::PARAM_ABILITY_RECOVERY_RESTART, true); + StartAbility(newWant,userId); TAG_LOGI(AAFwkTag::ABILITYMGR,"CPP_CRASH recovery. bundleName is %{public}s",abilityInfo.bundleName.c_str()); } if (remote != nullptr) { AppRecoveryMgr::AppRecoveryMgr::GetInstance().RemoveOnRemoteDieCallback(remote); } } + +int32_t AbilityManagerService::AtomicServicePreprocess(const Want &want) +{ + if (!WantUtils::IsShortUrl(want)) { + return NOT_ATOMIC_URL; + } +#ifdef APP_DOMAIN_VERIFY_ENABLED + Want asyncWant = want; + ConvertCallbackTask task = [](int resultCode, AppDomainVerify::TargetInfo &targetInfo) { + TAG_LOGD(AAFwkTag::ABILITYMGR, + "AtomicServicePreprocess async callback, resultCode=%{public}d, targetType=%{public}u", + resultCode, targetInfo.targetType); + }; + sptr callbackTask = new ConvertCallbackImpl(std::move(task)); + sptr callback = callbackTask; + AppDomainVerify::AppDomainVerifyMgrClient::GetInstance()->ConvertToExplicitWant(asyncWant, callback); +#endif + return ATOMIC_URL; +} } // namespace AAFwk } // namespace OHOS diff --git a/services/abilitymgr/src/ability_manager_stub.cpp b/services/abilitymgr/src/ability_manager_stub.cpp index e0dcf379f1..1d5f5f86e9 100644 --- a/services/abilitymgr/src/ability_manager_stub.cpp +++ b/services/abilitymgr/src/ability_manager_stub.cpp @@ -346,8 +346,14 @@ int AbilityManagerStub::OnRemoteRequestInnerSeventh(uint32_t code, MessageParcel return StartSelfUIAbilityInCurrentProcessInner(data, reply); } if (interfaceCode == AbilityManagerInterfaceCode::EXECUTE_INTENT_FOR_DISTRIBUTED) { - return ExecuteIntentForDistributedInner(data, reply); - } + return ExecuteIntentForDistributedInner(data, reply); + } + if (interfaceCode == AbilityManagerInterfaceCode::START_SELF_UI_ABILITY_BY_APP_CONTEXT) { + return StartSelfUIAbilityByAppContextInner(data, reply); + } + if (interfaceCode == AbilityManagerInterfaceCode::START_SELF_UI_ABILITY_IN_CHILD_PROCESS) { + return StartSelfUIAbilityInChildProcessInner(data, reply); + } return ERR_CODE_NOT_EXIST; } @@ -1014,6 +1020,18 @@ int AbilityManagerStub::OnRemoteRequestInnerTwentySecond(uint32_t code, MessageP if (interfaceCode == AbilityManagerInterfaceCode::INSIGHT_INTENT_QUERY_ENTITY) { return QueryEntityInner(data, reply); } + if (interfaceCode == AbilityManagerInterfaceCode::EXECUTE_IN_APP_SKILL) { + return ExecuteInAppSkillInner(data, reply); + } + if (interfaceCode == AbilityManagerInterfaceCode::EXECUTE_IN_APP_SKILL_WITH_TOKEN_ID) { + return ExecuteInAppSkillWithTokenIdInner(data, reply); + } + if (interfaceCode == AbilityManagerInterfaceCode::EXECUTE_SKILL_DONE_WITH_TOKEN) { + return ExecuteSkillDoneWithTokenInner(data, reply); + } + if (interfaceCode == AbilityManagerInterfaceCode::QUERY_SKILL_TYPE) { + return QuerySkillTypeInner(data, reply); + } return ERR_CODE_NOT_EXIST; } @@ -5572,6 +5590,23 @@ int AbilityManagerStub::StartSelfUIAbilityInCurrentProcessInner(MessageParcel &d return NO_ERROR; } +int AbilityManagerStub::StartSelfUIAbilityInChildProcessInner(MessageParcel &data, MessageParcel &reply) +{ + std::shared_ptr want(data.ReadParcelable()); + if (want == nullptr) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "want null"); + return ERR_INVALID_VALUE; + } + std::string specifiedFlag = data.ReadString(); + sptr callerToken = nullptr; + if (data.ReadBool()) { + callerToken = data.ReadRemoteObject(); + } + int32_t result = StartSelfUIAbilityInChildProcess(*want, specifiedFlag, callerToken); + reply.WriteInt32(result); + return NO_ERROR; +} + int AbilityManagerStub::IsRestartAppLimitInner(MessageParcel &data, MessageParcel &reply) { reply.WriteBool(IsRestartAppLimit()); @@ -5672,5 +5707,118 @@ int32_t AbilityManagerStub::SetAppRecoveryFlagInner(MessageParcel &data, Message reply.WriteInt32(result); return NO_ERROR; } + +int32_t AbilityManagerStub::ExecuteInAppSkillInner(MessageParcel &data, MessageParcel &reply) +{ + TAG_LOGD(AAFwkTag::ABILITYMGR, "execute in-app skill stub"); + std::string bundleName = Str16ToStr8(data.ReadString16()); + std::string moduleName = Str16ToStr8(data.ReadString16()); + std::string skillName = Str16ToStr8(data.ReadString16()); + std::string scriptPath = Str16ToStr8(data.ReadString16()); + std::string functionName = Str16ToStr8(data.ReadString16()); + + auto *args = data.ReadParcelable(); + std::shared_ptr skillArgs; + if (args != nullptr) { + skillArgs = std::shared_ptr(args); + } else { + skillArgs = std::make_shared(); + } + + sptr callback = nullptr; + bool hasCallback = data.ReadBool(); + if (hasCallback) { + auto callbackObj = data.ReadRemoteObject(); + if (callbackObj != nullptr) { + callback = iface_cast(callbackObj); + } + } + + int32_t result = ExecuteInAppSkill( + bundleName, moduleName, skillName, scriptPath, functionName, skillArgs, callback); + reply.WriteInt32(result); + return NO_ERROR; +} + +int32_t AbilityManagerStub::ExecuteInAppSkillWithTokenIdInner(MessageParcel &data, MessageParcel &reply) +{ + TAG_LOGD(AAFwkTag::ABILITYMGR, "execute in-app skill with tokenId stub"); + AppExecFwk::SkillExecuteRequest request; + request.callerTokenId = data.ReadUint32(); + request.bundleName = Str16ToStr8(data.ReadString16()); + request.moduleName = Str16ToStr8(data.ReadString16()); + request.skillName = Str16ToStr8(data.ReadString16()); + request.scriptPath = Str16ToStr8(data.ReadString16()); + request.functionName = Str16ToStr8(data.ReadString16()); + + auto *args = data.ReadParcelable(); + if (args != nullptr) { + request.skillArgs = std::shared_ptr(args); + } else { + request.skillArgs = std::make_shared(); + } + + sptr callback = nullptr; + bool hasCallback = data.ReadBool(); + if (hasCallback) { + auto callbackObj = data.ReadRemoteObject(); + if (callbackObj != nullptr) { + callback = iface_cast(callbackObj); + } + } + + int32_t result = ExecuteInAppSkillWithTokenId(request, callback); + reply.WriteInt32(result); + return NO_ERROR; +} + +int32_t AbilityManagerStub::ExecuteSkillDoneWithTokenInner(MessageParcel &data, MessageParcel &reply) +{ + TAG_LOGD(AAFwkTag::ABILITYMGR, "execute skill done with token stub"); + auto token = data.ReadRemoteObject(); + if (token == nullptr) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "null token"); + return ERR_INVALID_VALUE; + } + std::string requestCode = data.ReadString(); + int32_t resultCode = data.ReadInt32(); + auto *result = data.ReadParcelable(); + if (result == nullptr) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "read result fail"); + return ERR_INVALID_VALUE; + } + int32_t ret = ExecuteSkillDone(token, requestCode, resultCode, *result); + reply.WriteInt32(ret); + delete result; + return NO_ERROR; +} + +int32_t AbilityManagerStub::QuerySkillTypeInner(MessageParcel &data, MessageParcel &reply) +{ + TAG_LOGD(AAFwkTag::ABILITYMGR, "query skill type stub"); + std::string bundleName = Str16ToStr8(data.ReadString16()); + std::string moduleName = Str16ToStr8(data.ReadString16()); + std::string skillName = Str16ToStr8(data.ReadString16()); + + int32_t skillType = 0; + int32_t result = QuerySkillType(bundleName, moduleName, skillName, skillType); + reply.WriteInt32(result); + if (result == ERR_OK) { + reply.WriteInt32(skillType); + } + return NO_ERROR; +} + +int32_t AbilityManagerStub::StartSelfUIAbilityByAppContextInner(MessageParcel &data, MessageParcel &reply) +{ + std::shared_ptr want(data.ReadParcelable()); + if (want == nullptr) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "want null"); + return ERR_INVALID_VALUE; + } + int32_t result = StartSelfUIAbilityByAppContext(*want); + reply.WriteInt32(result); + return NO_ERROR; +} } // namespace AAFwk } // namespace OHOS diff --git a/services/abilitymgr/src/ability_record.cpp b/services/abilitymgr/src/ability_record.cpp index 6ac95301e8..5440cef0fa 100644 --- a/services/abilitymgr/src/ability_record.cpp +++ b/services/abilitymgr/src/ability_record.cpp @@ -65,6 +65,7 @@ #include "locale_config.h" #endif #include "xcollie/process_kill_reason.h" +#include "parameters.h" namespace OHOS { using AbilityRuntime::FreezeUtil; @@ -131,6 +132,8 @@ const int32_t BY_CALL_TIMEOUT_MS = 5000; constexpr int32_t NON_BY_CALL = 0; constexpr int32_t CALL_TO_BACKGROUND = 1; constexpr int32_t CALL_TO_FOREGROUND = 2; +const bool BETA_VERSION = OHOS::system::GetParameter("const.logsystem.versiontype", "unknown") == "beta"; + auto g_addLifecycleEventTask = [](sptr token, std::string &methodName) { CHECK_POINTER_LOG(token, "token is nullptr"); @@ -230,11 +233,6 @@ void AbilityRecord::Init(const AbilityRequest &abilityRequest) abilityRequest.abilityInfo.bundleName, abilityRequest.abilityInfo.name, userId)) { keepAliveBundle_ = true; } - - if (abilityRequest.want.GetBoolParam(AbilityRuntime::GlobalConstant::GAME_PRELAUNCH, false)) { - TAG_LOGD(AAFwkTag::UIABILITY, "abilityRecord: Set game prelaunch flag from want"); - SetGameSAPreLaunch(true); - } } AbilityRecordType AbilityRecord::GetAbilityRecordType() @@ -334,6 +332,7 @@ int AbilityRecord::LoadAbility(bool isShellCall, bool isStartupHide, pid_t calli loadParam.isPreloadStart = isPreloadStart_; loadParam.selfPid = selfPid; loadParam.byCallStatus = GetByCallStatus(); + loadParam.isGamePrelaunch = IsGameSAPreLaunch(); auto userId = abilityInfo_.uid / BASE_USER_RANGE; bool isMainUIAbility = MainElementUtils::IsMainUIAbility(abilityInfo_.bundleName, abilityInfo_.name, userId); @@ -396,7 +395,7 @@ bool AbilityRecord::CanRestartResident() } // only for UIAbility -void AbilityRecord::ForegroundAbility(uint32_t sceneFlag, bool hasLastWant) +void AbilityRecord::ForegroundAbility(uint32_t sceneFlag) { HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__); isWindowStarted_ = true; @@ -409,16 +408,7 @@ void AbilityRecord::ForegroundAbility(uint32_t sceneFlag, bool hasLastWant) SetAbilityStateInner(AbilityState::FOREGROUNDING); #endif // SUPPORT_SCREEN lifeCycleStateInfo_.sceneFlag = sceneFlag; - Want want; - if (hasLastWant) { - if (HasLastWant()) { - SetWant(*lastWant_); - lifeCycleStateInfo_.isNewWant = true; - lastWant_ = nullptr; - } - SetBackgroundDrivenFlag(false); - } - want = GetWant(); + auto want = GetWant(); UpdateDmsCallerInfo(want); AbilityRuntime::ErrorMsgGuard errorMsgGuard(token_ ? token_->AsObject() : nullptr, reinterpret_cast(GetScheduler().GetRefPtr()), "ScheduleAbilityTransaction"); @@ -427,7 +417,7 @@ void AbilityRecord::ForegroundAbility(uint32_t sceneFlag, bool hasLastWant) want.SetParam(SPECIFIED_ABILITY_FLAG, GetSpecifiedFlag()); } lifecycleDeal_->ForegroundNew(want, lifeCycleStateInfo_, GetSessionInfo()); - want.RemoveParam(AbilityRuntime::GlobalConstant::GAME_PRELAUNCH); + RemoveSpecifiedWantParam(AbilityRuntime::GlobalConstant::GAME_PRELAUNCH); SetIsNewWant(false); if (!Rosen::SceneBoardJudgement::IsSceneBoardEnabled()) { lifeCycleStateInfo_.sceneFlag = 0; @@ -444,7 +434,7 @@ void AbilityRecord::ForegroundUIExtensionAbility(uint32_t sceneFlag) { HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__); TAG_LOGI(AAFwkTag::ABILITYMGR, "ForegroundUIExtensionAbility:%{public}s/%{public}s", - GetElementName().GetBundleName().c_str(), GetElementName().GetAbilityName().c_str()); + GetInfoBundleName().c_str(), GetInfoAbilityName().c_str()); CHECK_POINTER(lifecycleDeal_); if (IsAbilityState(AbilityState::BACKGROUND)) { @@ -469,10 +459,10 @@ void AbilityRecord::ForegroundUIExtensionAbility(uint32_t sceneFlag) void AbilityRecord::ProcessForegroundAbility(uint32_t tokenId, const ForegroundOptions &options) { HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__); - TAG_LOGD(AAFwkTag::ABILITYMGR, "ability record: %{public}s/%{public}s", GetElementName().GetBundleName().c_str(), - GetElementName().GetAbilityName().c_str()); - needCheckAutoStartupStatusBar_ = GetWant().GetBoolParam(HIDDEN_START_AUTOSTARTUP, false); - GetWant().RemoveParam(HIDDEN_START_AUTOSTARTUP); + TAG_LOGD(AAFwkTag::ABILITYMGR, "ability record: %{public}s/%{public}s", GetInfoBundleName().c_str(), + GetInfoAbilityName().c_str()); + needCheckAutoStartupStatusBar_ = GetBoolParam(HIDDEN_START_AUTOSTARTUP, false); + RemoveSpecifiedWantParam(HIDDEN_START_AUTOSTARTUP); #ifdef SUPPORT_UPMS { std::lock_guard guard(wantLock_); @@ -503,8 +493,8 @@ void AbilityRecord::ProcessForegroundAbility(uint32_t tokenId, const ForegroundO PostForegroundTimeoutTask(); if (IsAbilityState(AbilityState::FOREGROUND)) { - TAG_LOGD(AAFwkTag::ABILITYMGR, "Activate %{public}s/%{public}s", GetElementName().GetBundleName().c_str(), - GetElementName().GetAbilityName().c_str()); + TAG_LOGD(AAFwkTag::ABILITYMGR, "Activate %{public}s/%{public}s", GetInfoBundleName().c_str(), + GetInfoAbilityName().c_str()); if (IsFrozenByPreload()) { SetFrozenByPreload(false); auto ret = @@ -515,8 +505,8 @@ void AbilityRecord::ProcessForegroundAbility(uint32_t tokenId, const ForegroundO return; } // background to active state - TAG_LOGD(AAFwkTag::ABILITYMGR, "MoveToForeground, %{public}s/%{public}s", GetElementName().GetBundleName().c_str(), - GetElementName().GetAbilityName().c_str()); + TAG_LOGD(AAFwkTag::ABILITYMGR, "MoveToForeground, %{public}s/%{public}s", GetInfoBundleName().c_str(), + GetInfoAbilityName().c_str()); lifeCycleStateInfo_.sceneFlagBak = options.sceneFlag; ResSchedUtil::GetInstance().ReportEventToRSS(GetUid(), GetAbilityInfo().bundleName, "THAW_BY_FOREGROUND_ABILITY", GetPid(), GetCallerRecord() ? GetCallerRecord()->GetPid() : -1); @@ -537,8 +527,9 @@ void AbilityRecord::PostForegroundTimeoutTask() IsDebug(), IsPreloadStart(), IsPreloaded()); return; } + int radio = BETA_VERSION ? FOREGROUND_TIMEOUT_MULTIPLE_BETA : FOREGROUND_TIMEOUT_MULTIPLE; int foregroundTimeout = - AmsConfigurationParameter::GetInstance().GetAppStartTimeoutTime() * FOREGROUND_TIMEOUT_MULTIPLE; + AmsConfigurationParameter::GetInstance().GetAppStartTimeoutTime() * radio; if (InsightIntentExecuteParam::IsInsightIntentExecute(GetWant())) { foregroundTimeout = AmsConfigurationParameter::GetInstance().GetAppStartTimeoutTime() * INSIGHT_INTENT_TIMEOUT_MULTIPLE; @@ -753,7 +744,7 @@ void AbilityRecord::BackgroundAbility(const Closure &task) { HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__); TAG_LOGI(AAFwkTag::ABILITYMGR, "BackgroundLifecycle %{public}s/%{public}s", - GetElementName().GetBundleName().c_str(), GetElementName().GetAbilityName().c_str()); + GetInfoBundleName().c_str(), GetInfoAbilityName().c_str()); if (lifecycleDeal_ == nullptr) { TAG_LOGE(AAFwkTag::ABILITYMGR, "null lifecycleDeal_"); return; @@ -1565,9 +1556,7 @@ void SystemAbilityCallerRecord::SendResultToSystemAbility(int requestCode, void AbilityRecord::RemoveSpecifiedWantParam(const std::string &key) { std::lock_guard guard(wantLock_); - if (want_.HasParameter(key)) { - want_.RemoveParam(key); - } + want_.RemoveParam(key); } void AbilityRecord::RemoveCallerRequestCode(std::shared_ptr callerAbilityRecord, int32_t requestCode) @@ -1863,9 +1852,9 @@ void AbilityRecord::DumpAbilityState( void AbilityRecord::SetStartTime() { - if (startTime_ == 0) { - startTime_ = AbilityUtil::SystemTimeMillis(); - } + int64_t expected = 0; + int64_t desired = AbilityUtil::SystemTimeMillis(); + startTime_.compare_exchange_strong(expected, desired); } int64_t AbilityRecord::GetStartTime() const @@ -2134,6 +2123,48 @@ Want AbilityRecord::GetWant() const return want_; } +std::string AbilityRecord::GetAbilityName() const +{ + std::lock_guard guard(wantLock_); + return want_.GetElement().GetAbilityName(); +} + +std::string AbilityRecord::GetBundleName() const +{ + std::lock_guard guard(wantLock_); + return want_.GetBundle(); +} + +std::string AbilityRecord::GetModuleName() const +{ + std::lock_guard guard(wantLock_); + return want_.GetModuleName(); +} + +std::string AbilityRecord::GetStringParam(const std::string &key) const +{ + std::lock_guard guard(wantLock_); + return want_.GetStringParam(key); +} + +int AbilityRecord::GetIntParam(const std::string &key, int defaultValue) const +{ + std::lock_guard guard(wantLock_); + return want_.GetIntParam(key, defaultValue); +} + +bool AbilityRecord::GetBoolParam(const std::string &key, bool defaultValue) const +{ + std::lock_guard guard(wantLock_); + return want_.GetBoolParam(key, defaultValue); +} + +bool AbilityRecord::HasParameter(const std::string &key) const +{ + std::lock_guard guard(wantLock_); + return want_.HasParameter(key); +} + void AbilityRecord::RemoveSignatureInfo() { std::lock_guard guard(wantLock_); @@ -3313,7 +3344,7 @@ bool AbilityRecord::ReportAbilityConnectionRelations() auto callerPid = recordCallerInfo->callerPid; auto callerUid = recordCallerInfo->callerUid; auto callerBundleName = recordCallerInfo->callerBundleName; - auto targetBundleName = GetElementName().GetBundleName(); + auto targetBundleName = GetBundleName(); if (targetPid <= 0 || targetUid <= 0 || callerPid <= 0 || callerUid <= 0) { TAG_LOGE(AAFwkTag::ABILITYMGR, "Invalid target process: targetPid=%{public}d, targetUid=%{public}d, " "callerPid=%{public}d, callerUid=%{public}d", targetPid, targetUid, callerPid, callerUid); diff --git a/services/abilitymgr/src/ability_scheduler_proxy.cpp b/services/abilitymgr/src/ability_scheduler_proxy.cpp index 9c2663be09..c176e56fb6 100644 --- a/services/abilitymgr/src/ability_scheduler_proxy.cpp +++ b/services/abilitymgr/src/ability_scheduler_proxy.cpp @@ -1148,6 +1148,26 @@ void AbilitySchedulerProxy::OnExecuteIntent(const Want &want) TAG_LOGI(AAFwkTag::ABILITYMGR, "end"); } +void AbilitySchedulerProxy::ExecuteSkill(const Want &want) +{ + TAG_LOGD(AAFwkTag::ABILITYMGR, "execute skill proxy"); + + MessageParcel data; + MessageParcel reply; + MessageOption option(MessageOption::TF_ASYNC); + AAFwk::ExtendMaxIpcCapacityForInnerWant(data); + if (!WriteInterfaceToken(data)) { + return; + } + data.WriteParcelable(&want); + int32_t err = SendTransactCmd(IAbilityScheduler::SCHEDULE_EXECUTE_SKILL, data, reply, option); + if (err != NO_ERROR) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "fail, err: %{public}d", err); + } + + TAG_LOGD(AAFwkTag::ABILITYMGR, "end"); +} + int32_t AbilitySchedulerProxy::CreateModalUIExtension(const Want &want) { HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__); diff --git a/services/abilitymgr/src/ability_scheduler_stub.cpp b/services/abilitymgr/src/ability_scheduler_stub.cpp index 231e6c93b1..52e59a2ce0 100644 --- a/services/abilitymgr/src/ability_scheduler_stub.cpp +++ b/services/abilitymgr/src/ability_scheduler_stub.cpp @@ -150,6 +150,8 @@ int AbilitySchedulerStub::OnRemoteRequestInnerThird( return ShareDataInner(data, reply); case SCHEDULE_ONEXECUTE_INTENT: return OnExecuteIntentInner(data, reply); + case SCHEDULE_EXECUTE_SKILL: + return ExecuteSkillInner(data, reply); case CREATE_MODAL_UI_EXTENSION: return CreateModalUIExtensionInner(data, reply); case UPDATE_SESSION_TOKEN: @@ -724,6 +726,18 @@ int AbilitySchedulerStub::OnExecuteIntentInner(MessageParcel &data, MessageParce return NO_ERROR; } +int AbilitySchedulerStub::ExecuteSkillInner(MessageParcel &data, MessageParcel &reply) +{ + TAG_LOGD(AAFwkTag::ABILITYMGR, "execute skill stub"); + std::shared_ptr want(data.ReadParcelable()); + if (want == nullptr) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "null want"); + return ERR_INVALID_VALUE; + } + ExecuteSkill(*want); + return NO_ERROR; +} + int AbilitySchedulerStub::CreateModalUIExtensionInner(MessageParcel &data, MessageParcel &reply) { std::shared_ptr want(data.ReadParcelable()); diff --git a/services/abilitymgr/src/ability_start_with_wait_observer_manager/ability_start_with_wait_observer_manager.cpp b/services/abilitymgr/src/ability_start_with_wait_observer_manager/ability_start_with_wait_observer_manager.cpp index c361678a06..39b3df0cbd 100644 --- a/services/abilitymgr/src/ability_start_with_wait_observer_manager/ability_start_with_wait_observer_manager.cpp +++ b/services/abilitymgr/src/ability_start_with_wait_observer_manager/ability_start_with_wait_observer_manager.cpp @@ -134,7 +134,7 @@ void AbilityStartWithWaitObserverManager::NotifyAATerminateWait( TAG_LOGE(AAFwkTag::ABILITYMGR, "null abilityRecord"); return; } - auto observerId = abilityRecord->GetWant().GetIntParam(Want::START_ABILITY_WITH_WAIT_OBSERVER_ID_KEY, -1); + auto observerId = abilityRecord->GetIntParam(Want::START_ABILITY_WITH_WAIT_OBSERVER_ID_KEY, -1); if (observerId == -1) { TAG_LOGW(AAFwkTag::ABILITYMGR, "invaid observerId"); return; @@ -188,7 +188,7 @@ void AbilityStartWithWaitObserverManager::SetColdStartForShellCall(std::shared_p TAG_LOGE(AAFwkTag::ABILITYMGR, "null abilityRecord"); return; } - auto observerId = abilityRecord->GetWant().GetIntParam(Want::START_ABILITY_WITH_WAIT_OBSERVER_ID_KEY, -1); + auto observerId = abilityRecord->GetIntParam(Want::START_ABILITY_WITH_WAIT_OBSERVER_ID_KEY, -1); if (observerId == -1) { TAG_LOGW(AAFwkTag::ABILITYMGR, "invaid observerId"); return; diff --git a/services/abilitymgr/src/common_extension_manager.cpp b/services/abilitymgr/src/common_extension_manager.cpp index 041dc088c9..231a1ed500 100644 --- a/services/abilitymgr/src/common_extension_manager.cpp +++ b/services/abilitymgr/src/common_extension_manager.cpp @@ -44,14 +44,14 @@ int CommonExtensionManager::AttachAbilityThreadInner(const sptrGetElementName().GetBundleName().c_str(), - terminatingRecord->GetElementName().GetAbilityName().c_str(), userId_); + terminatingRecord->GetInfoBundleName().c_str(), + terminatingRecord->GetInfoAbilityName().c_str(), userId_); } auto tmpRecord = Token::GetAbilityRecordByToken(token); if (tmpRecord && tmpRecord != terminatingRecord) { TAG_LOGW(AAFwkTag::EXT, "Token:%{public}s/%{public}s, user:%{public}d", - tmpRecord->GetElementName().GetBundleName().c_str(), - tmpRecord->GetElementName().GetAbilityName().c_str(), userId_); + tmpRecord->GetInfoBundleName().c_str(), + tmpRecord->GetInfoAbilityName().c_str(), userId_); } } CHECK_POINTER_AND_RETURN(abilityRecord, ERR_INVALID_VALUE); diff --git a/services/abilitymgr/src/dialog_session/dialog_session_manager.cpp b/services/abilitymgr/src/dialog_session/dialog_session_manager.cpp index 0e12114e38..d2d83ffbab 100644 --- a/services/abilitymgr/src/dialog_session/dialog_session_manager.cpp +++ b/services/abilitymgr/src/dialog_session/dialog_session_manager.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023-2025 Huawei Device Co., Ltd. + * Copyright (c) 2023-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 @@ -41,6 +41,7 @@ constexpr int32_t ERMS_ISALLOW_RESULTCODE = 10; constexpr const char* SUPPORT_CLOSE_ON_BLUR = "supportCloseOnBlur"; constexpr const char* DIALOG_SESSION_ID = "dialogSessionId"; constexpr const char* PICKER_ERMS_POLICY = "ability.params.picker.erms.policy"; +constexpr const char* ATOMIC_SERVICE_SHORT_LINK = "atomicServiceShortLink"; } DialogSessionManager &DialogSessionManager::GetInstance() @@ -244,12 +245,17 @@ void DialogSessionManager::GenerateDialogCallerInfo(AbilityRequest &abilityReque dialogCallerInfo->userId = userId; dialogCallerInfo->needGrantUriPermission = needGrantUriPermission; dialogCallerInfo->callerAccessTokenId = abilityRequest.callerAccessTokenId; + dialogCallerInfo->requestCallback = abilityRequest.requestCallback; } void DialogSessionManager::NotifyAbilityRequestFailure(const std::string &dialogSessionId, const Want &want) { auto callerInfo = GetDialogCallerInfo(dialogSessionId); CHECK_POINTER(callerInfo); + if (callerInfo->requestCallback != nullptr) { + TAG_LOGI(AAFwkTag::ABILITYMGR, "callback request ability"); + callerInfo->requestCallback->OnRequestStartAbilityResult(false); + } auto requestId = callerInfo->targetWant.GetStringParam(KEY_REQUEST_ID); if (requestId.empty() || callerInfo->callerToken == nullptr) { TAG_LOGD(AAFwkTag::ABILITYMGR, "no need to handle ability request"); @@ -311,9 +317,16 @@ int DialogSessionManager::SendDialogResult(const Want &want, const std::string & sptr callerToken = dialogCallerInfo->callerToken; auto abilityMgr = DelayedSingleton::GetInstance(); CHECK_POINTER_AND_RETURN(abilityMgr, INNER_ERR); - int ret = abilityMgr->StartAbilityAsCallerDetails(targetWant, callerToken, callerToken, dialogCallerInfo->userId, - dialogCallerInfo->requestCode, false, dialogCallerInfo->type == SelectorType::APP_CLONE_SELECTOR, - dialogCallerInfo->callerAccessTokenId); + int32_t ret = ERR_OK; + int32_t atomicServiceShortLink = want.GetIntParam(ATOMIC_SERVICE_SHORT_LINK, 0); + if (atomicServiceShortLink == 1) { + ret = abilityMgr->OpenLink(targetWant, callerToken, dialogCallerInfo->userId, + dialogCallerInfo->requestCode, false); + } else { + ret = abilityMgr->StartAbilityAsCallerDetails(targetWant, callerToken, callerToken, dialogCallerInfo->userId, + dialogCallerInfo->requestCode, false, dialogCallerInfo->type == SelectorType::APP_CLONE_SELECTOR, + dialogCallerInfo->callerAccessTokenId, dialogCallerInfo->requestCallback); + } if (ret == ERR_OK) { ClearDialogContext(dialogSessionId); abilityMgr->RemoveSelectorIdentity(dialogCallerInfo->targetWant.GetIntParam(Want::PARAM_RESV_CALLER_TOKEN, 0)); @@ -389,6 +402,7 @@ int DialogSessionManager::CreateJumpModalDialog(AbilityRequest &abilityRequest, parameters.SetParam("deviceType", AAFwk::String::Box(OHOS::system::GetDeviceType())); parameters.SetParam("userId", AAFwk::Integer::Box(userId)); + parameters.SetParam(ATOMIC_SERVICE_SHORT_LINK, AAFwk::Integer::Box(abilityRequest.atomicServiceShortLink)); std::vector dialogAppInfos; std::string dialogSessionId = GenerateDialogSessionRecordCommon(abilityRequest, userId, parameters, @@ -419,6 +433,7 @@ int DialogSessionManager::CreateImplicitSelectorModalDialog(AbilityRequest &abil sessionWant.SetParam("showCaller", showCaller); sessionWant.SetParam("ohos.ability.params.showDefaultPicker", abilityRequest.want.GetBoolParam("ohos.ability.params.showDefaultPicker", false)); + sessionWant.SetParam(ATOMIC_SERVICE_SHORT_LINK, abilityRequest.atomicServiceShortLink); if (abilityRequest.want.HasParameter(APP_LAUNCH_TRUSTLIST)) { sessionWant.SetParam(APP_LAUNCH_TRUSTLIST, abilityRequest.want.GetStringArrayParam(APP_LAUNCH_TRUSTLIST)); @@ -459,6 +474,7 @@ int DialogSessionManager::CreateCloneSelectorModalDialog(AbilityRequest &ability if (replaceWant != "") { parameters.SetParam("ecological.replaceWant", AAFwk::String::Box(replaceWant)); } + parameters.SetParam(ATOMIC_SERVICE_SHORT_LINK, AAFwk::Integer::Box(abilityRequest.atomicServiceShortLink)); std::string dialogSessionId = GenerateDialogSessionRecordCommon(abilityRequest, userId, parameters, dialogAppInfos, SelectorType::APP_CLONE_SELECTOR); diff --git a/services/abilitymgr/src/disposed_observer.cpp b/services/abilitymgr/src/disposed_observer.cpp index aa83d046ba..be0d276448 100644 --- a/services/abilitymgr/src/disposed_observer.cpp +++ b/services/abilitymgr/src/disposed_observer.cpp @@ -18,12 +18,20 @@ #include "interceptor/disposed_rule_interceptor.h" #include "ability_record.h" #include "modal_system_ui_extension.h" +#include "want_params.h" namespace OHOS { namespace AAFwk { namespace { constexpr const char* UIEXTENSION_MODAL_TYPE = "ability.want.params.modalType"; constexpr const char* INTERCEPT_MISSION_ID = "intercept_missionId"; +constexpr const char* IS_EMBEDDABLE_SERVICE = "ohos.param.isCallerEmbeddableUIExtension"; + +bool IsEmbeddableStart(int32_t screenMode) +{ + return screenMode == AAFwk::EMBEDDED_FULL_SCREEN_MODE || + screenMode == AAFwk::EMBEDDED_HALF_SCREEN_MODE; +} } DisposedObserver::DisposedObserver(const AppExecFwk::DisposedRule &disposedRule, @@ -76,36 +84,66 @@ void DisposedObserver::OnPageShow(const AppExecFwk::PageStateData &pageStateData } } if (disposedRule_.componentType == AppExecFwk::ComponentType::UI_EXTENSION) { - auto abilityRecord = Token::GetAbilityRecordByToken(token_); - if (abilityRecord == nullptr || abilityRecord->GetAbilityInfo().type != AppExecFwk::AbilityType::PAGE) { - auto systemUIExtension = std::make_shared(); - Want want = *disposedRule_.want; - want.SetParam(UIEXTENSION_MODAL_TYPE, 1); - TAG_LOGD(AAFwkTag::ABILITYMGR, "modal system"); - bool ret = IN_PROCESS_CALL(systemUIExtension->CreateModalUIExtension(want)); - if (!ret) { - interceptor_->UnregisterObserver(pageStateData.uid); - TAG_LOGE(AAFwkTag::ABILITYMGR, "call failed"); - return; - } - } else { - Want want = *disposedRule_.want; - auto sessionInfo = abilityRecord->GetSessionInfo(); - if (sessionInfo != nullptr) { - want.SetParam(INTERCEPT_MISSION_ID, sessionInfo->persistentId); - } else { - want.SetParam(INTERCEPT_MISSION_ID, abilityRecord->GetMissionId()); - } - TAG_LOGD(AAFwkTag::ABILITYMGR, "modal app"); - int ret = abilityRecord->CreateModalUIExtension(want); - if (ret != ERR_OK) { - interceptor_->UnregisterObserver(pageStateData.uid); - TAG_LOGE(AAFwkTag::ABILITYMGR, "call failed"); - return; - } + int ret = ExecuteUIExtension(pageStateData); + if (ret != ERR_OK) { + interceptor_->UnregisterObserver(pageStateData.uid); + TAG_LOGE(AAFwkTag::ABILITYMGR, "call failed"); + return; } } interceptor_->UnregisterObserver(pageStateData.uid); } + +ErrCode DisposedObserver::ExecuteUIExtension(const AppExecFwk::PageStateData &pageStateData) +{ + auto abilityRecord = Token::GetAbilityRecordByToken(token_); + Want want = *disposedRule_.want; + + bool isEmbeddable = false; + if (abilityRecord != nullptr) { + const auto& abilityWant = abilityRecord->GetWant(); + if (abilityWant.HasParameter(AAFwk::SCREEN_MODE_KEY)) { + int32_t screenMode = abilityWant.GetIntParam(AAFwk::SCREEN_MODE_KEY, AAFwk::IDLE_SCREEN_MODE); + isEmbeddable = IsEmbeddableStart(screenMode); + } + } + + if (isEmbeddable) { + TAG_LOGD(AAFwkTag::ABILITYMGR, "Handle embeddable UIExtension"); + want.SetParam(IS_EMBEDDABLE_SERVICE, true); + int ret = abilityRecord->CreateModalUIExtension(want); + if (ret != ERR_OK) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "Handle embeddable UIExtension failed"); + return ret; + } + return ERR_OK; + } + + if (abilityRecord == nullptr || abilityRecord->GetAbilityInfo().type != AppExecFwk::AbilityType::PAGE) { + TAG_LOGD(AAFwkTag::ABILITYMGR, "Handle non-PAGE UIExtension (modal system path)"); + auto systemUIExtension = std::make_shared(); + want.SetParam(UIEXTENSION_MODAL_TYPE, 1); + bool ret = IN_PROCESS_CALL(systemUIExtension->CreateModalUIExtension(want)); + if (!ret) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "Modal system UIExtension creation failed"); + return ret; + } + return ERR_OK; + } + + TAG_LOGD(AAFwkTag::ABILITYMGR, "Handle PAGE type UIExtension (modal app path)"); + auto sessionInfo = abilityRecord->GetSessionInfo(); + if (sessionInfo != nullptr) { + want.SetParam(INTERCEPT_MISSION_ID, sessionInfo->persistentId); + } else { + want.SetParam(INTERCEPT_MISSION_ID, abilityRecord->GetMissionId()); + } + int ret = abilityRecord->CreateModalUIExtension(want); + if (ret != ERR_OK) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "Modal app UIExtension creation failed"); + return ret; + } + return ERR_OK; +} } // namespace AAFwk } // namespace OHOS diff --git a/services/abilitymgr/src/extension_record/base_extension_record.cpp b/services/abilitymgr/src/extension_record/base_extension_record.cpp index 4341e9cbc7..059ea393ff 100644 --- a/services/abilitymgr/src/extension_record/base_extension_record.cpp +++ b/services/abilitymgr/src/extension_record/base_extension_record.cpp @@ -334,6 +334,16 @@ pid_t BaseExtensionRecord::GetClientPid() const return clientPid_; } +void BaseExtensionRecord::SetRequestId(const std::string &requestId) +{ + requestId_ = requestId; +} + +std::string BaseExtensionRecord::GetRequestId() const +{ + return requestId_; +} + void BaseExtensionRecord::PostUIExtensionAbilityTimeoutTask(uint32_t messageId) { if (IsDebug()) { diff --git a/services/abilitymgr/src/extension_record/extension_record_factory.cpp b/services/abilitymgr/src/extension_record/extension_record_factory.cpp index c9de021158..17b2b83f7a 100644 --- a/services/abilitymgr/src/extension_record/extension_record_factory.cpp +++ b/services/abilitymgr/src/extension_record/extension_record_factory.cpp @@ -45,6 +45,9 @@ const std::map EXTENSIO PRE_CHECK_FLAG_NONE }}, { AppExecFwk::ExtensionAbilityType::AGENT_UI, { PROCESS_MODE_AGENT_UI, PROCESS_MODE_AGENT_UI, PRE_CHECK_FLAG_NONE }}, + { AppExecFwk::ExtensionAbilityType::SYSPICKER_MEDIACONTROL, + { PROCESS_MODE_BUNDLE, PROCESS_MODE_SUPPORT_DEFAULT | PROCESS_MODE_CALLER_INSTANCE, + PRE_CHECK_FLAG_NONE }}, }; uint32_t GetPreCheckFlag(ExtensionAbilityType type) diff --git a/services/abilitymgr/src/extension_record/extension_record_manager.cpp b/services/abilitymgr/src/extension_record/extension_record_manager.cpp index 8e1f36de2e..d09c24ccf8 100644 --- a/services/abilitymgr/src/extension_record/extension_record_manager.cpp +++ b/services/abilitymgr/src/extension_record/extension_record_manager.cpp @@ -236,9 +236,10 @@ int32_t ExtensionRecordManager::GetOrCreateExtensionRecord(const AAFwk::AbilityR auto hostPid = IPCSkeleton::GetCallingPid(); auto result = IsPreloadExtensionRecord(abilityRequest, hostPid, extensionRecord, isLoaded); if (result) { - std::string abilityName = abilityRequest.want.GetElement().GetAbilityName(); - std::string bundleName = abilityRequest.want.GetElement().GetBundleName(); - std::string moduleName = abilityRequest.want.GetElement().GetModuleName(); + auto wantEle = abilityRequest.want.GetElement(); + std::string abilityName = wantEle.GetAbilityName(); + std::string bundleName = wantEle.GetBundleName(); + std::string moduleName = wantEle.GetModuleName(); auto extensionRecordMapKey = std::make_tuple(abilityName, bundleName, moduleName, hostPid); RemovePreloadUIExtensionRecord(extensionRecordMapKey); HandlePreloadUIExtensionLoaded(extensionRecord); @@ -402,6 +403,17 @@ int32_t ExtensionRecordManager::UpdateProcessName(const AAFwk::AbilityRequest &a abilityRecord->SetProcessName(process); break; } + case PROCESS_MODE_CALLER_INSTANCE: { + if (callerRecord == nullptr) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "callerRecord is null"); + return ERR_INVALID_VALUE; + } + std::string process = abilityRequest.abilityInfo.bundleName + SEPARATOR + abilityRequest.abilityInfo.name + + SEPARATOR + std::to_string(callerRecord->GetPid()); + appendAppIndex(process); + abilityRecord->SetProcessName(process); + break; + } default: // AppExecFwk::ExtensionProcessMode::UNDEFINED or AppExecFwk::ExtensionProcessMode::BUNDLE // no need to update if (!abilityRequest.moduleProcess.empty()) { @@ -448,12 +460,12 @@ int32_t ExtensionRecordManager::AddPreloadUIExtensionRecord( extensionRecord = extensionRecords_[extensionRecordId]; CHECK_POINTER_AND_RETURN(extensionRecord, ERR_INVALID_VALUE); auto hostPid = extensionRecord->hostPid_; - auto preLoadUIExtensionInfo = std::make_tuple(abilityRecord->GetWant().GetElement().GetAbilityName(), - abilityRecord->GetWant().GetElement().GetBundleName(), - abilityRecord->GetWant().GetElement().GetModuleName(), hostPid); + auto preLoadUIExtensionInfo = std::make_tuple(abilityRecord->GetAbilityName(), + abilityRecord->GetBundleName(), + abilityRecord->GetModuleName(), hostPid); TAG_LOGD(AAFwkTag::ABILITYMGR, "hostPid: %{public}d, elementName:%{public}s/%{public}s", - hostPid, abilityRecord->GetWant().GetElement().GetBundleName().c_str(), - abilityRecord->GetWant().GetElement().GetAbilityName().c_str()); + hostPid, abilityRecord->GetBundleName().c_str(), + abilityRecord->GetAbilityName().c_str()); std::lock_guard lock(preloadUIExtensionMapMutex_); preloadUIExtensionMap_[preLoadUIExtensionInfo].push_back(extensionRecord); return ERR_OK; @@ -477,9 +489,10 @@ bool ExtensionRecordManager::IsPreloadExtensionRecord(const AAFwk::AbilityReques const pid_t &hostPid, std::shared_ptr &extensionRecord, bool &isLoaded) { TAG_LOGD(AAFwkTag::ABILITYMGR, "call."); - std::string abilityName = abilityRequest.want.GetElement().GetAbilityName(); - std::string bundleName = abilityRequest.want.GetElement().GetBundleName(); - std::string moduleName = abilityRequest.want.GetElement().GetModuleName(); + auto wantEle = abilityRequest.want.GetElement(); + std::string abilityName = wantEle.GetAbilityName(); + std::string bundleName = wantEle.GetBundleName(); + std::string moduleName = wantEle.GetModuleName(); auto extensionRecordMapKey = std::make_tuple(abilityName, bundleName, moduleName, hostPid); TAG_LOGD(AAFwkTag::ABILITYMGR, "hostBundleName: %{public}d, bundleName: %{public}s", hostPid, bundleName.c_str()); @@ -702,7 +715,7 @@ int32_t ExtensionRecordManager::CreateExtensionRecord(const AAFwk::AbilityReques abilityRecord->SetUIExtensionAbilityId(extensionRecordId); extensionRecord->hostPid_ = (hostPid == AAFwk::DEFAULT_INVAL_VALUE) ? IPCSkeleton::GetCallingPid() : hostPid; //add uiextension record register state observer object. - if (abilityRecord->GetWant().GetBoolParam(IS_PRELOAD_UIEXTENSION_ABILITY, false)) { + if (abilityRecord->GetBoolParam(IS_PRELOAD_UIEXTENSION_ABILITY, false)) { auto ret = extensionRecord->RegisterStateObserver(hostBundleName); if (ret != ERR_OK) { TAG_LOGE(AAFwkTag::ABILITYMGR, "register failed, err: %{public}d", ret); @@ -897,8 +910,8 @@ void ExtensionRecordManager::GetCallerTokenList( } TAG_LOGD(AAFwkTag::ABILITYMGR, "ability:%{public}s/%{public}s, pid: %{public}d, tokenId: %{public}d", - callerAbilityRecord->GetWant().GetElement().GetBundleName().c_str(), - callerAbilityRecord->GetWant().GetElement().GetAbilityName().c_str(), callerAbilityRecord->GetPid(), + callerAbilityRecord->GetBundleName().c_str(), + callerAbilityRecord->GetAbilityName().c_str(), callerAbilityRecord->GetPid(), callerAbilityRecord->GetApplicationInfo().accessTokenId); auto callerExtensionRecordId = callerAbilityRecord->GetUIExtensionAbilityId(); @@ -935,8 +948,8 @@ bool ExtensionRecordManager::IsFocused( } TAG_LOGD(AAFwkTag::ABILITYMGR, "ability:%{public}s/%{public}s, pid: %{public}d, tokenId: %{public}d", - abilityRecord->GetWant().GetElement().GetBundleName().c_str(), - abilityRecord->GetWant().GetElement().GetAbilityName().c_str(), abilityRecord->GetPid(), + abilityRecord->GetBundleName().c_str(), + abilityRecord->GetAbilityName().c_str(), abilityRecord->GetPid(), abilityRecord->GetApplicationInfo().accessTokenId); if (!AAFwk::UIExtensionWrapper::IsUIExtension(abilityRecord->GetAbilityInfo().extensionAbilityType)) { diff --git a/services/abilitymgr/src/free_install_manager.cpp b/services/abilitymgr/src/free_install_manager.cpp index 7ac687c11a..ecd7947972 100644 --- a/services/abilitymgr/src/free_install_manager.cpp +++ b/services/abilitymgr/src/free_install_manager.cpp @@ -120,7 +120,7 @@ int FreeInstallManager::StartFreeInstall(const Want &want, int32_t userId, int r sptr callerToken, std::shared_ptr param) { HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__); - TAG_LOGI(AAFwkTag::FREE_INSTALL, "StartFreeInstall:%{public}s", want.GetElement().GetBundleName().c_str()); + TAG_LOGI(AAFwkTag::FREE_INSTALL, "StartFreeInstall:%{public}s", want.GetBundle().c_str()); bool skipPermissionCheck = param != nullptr ? param->skipStartFreeInstallPermissionCheck : false; if (!skipPermissionCheck && !VerifyStartFreeInstallPermission(callerToken)) { TAG_LOGE(AAFwkTag::FREE_INSTALL, "permission denied"); @@ -164,7 +164,7 @@ int FreeInstallManager::StartFreeInstall(const Want &want, int32_t userId, int r auto future = info.promise->get_future(); std::future_status status = future.wait_for(std::chrono::milliseconds(DELAY_LOCAL_FREE_INSTALL_TIMEOUT)); if (status == std::future_status::timeout) { - RemoveFreeInstallInfo(info.want.GetElement().GetBundleName(), info.want.GetElement().GetAbilityName(), + RemoveFreeInstallInfo(info.want.GetBundle(), info.want.GetElement().GetAbilityName(), info.want.GetStringParam(Want::PARAM_RESV_START_TIME)); return FREE_INSTALL_TIMEOUT; } @@ -266,9 +266,10 @@ void FreeInstallManager::NotifyDmsCallback(const Want &want, int resultCode) return; } + auto srcAbilityName = want.GetElement().GetAbilityName(); for (auto it = dmsFreeInstallCbs_.begin(); it != dmsFreeInstallCbs_.end();) { std::string abilityName = (*it).want.GetElement().GetAbilityName(); - if (want.GetElement().GetAbilityName() == abilityName) { + if (srcAbilityName == abilityName) { HandleDMSCallback(resultCode, *it); it = dmsFreeInstallCbs_.erase(it); } else { @@ -290,16 +291,16 @@ void FreeInstallManager::NotifyFreeInstallResult(int32_t recordId, const Want &w } bool isFromRemote = want.GetBoolParam(FROM_REMOTE_KEY, false); + auto srcBundleName = want.GetBundle(); + auto srcAbilityName = want.GetElement().GetAbilityName(); + auto srcStartTime = want.GetStringParam(Want::PARAM_RESV_START_TIME); + auto srcUri = want.GetUriString(); for (auto it = freeInstallList_.begin(); it != freeInstallList_.end();) { FreeInstallInfo &freeInstallInfo = *it; - std::string bundleName = freeInstallInfo.want.GetElement().GetBundleName(); - std::string abilityName = freeInstallInfo.want.GetElement().GetAbilityName(); - std::string startTime = freeInstallInfo.want.GetStringParam(Want::PARAM_RESV_START_TIME); - std::string url = freeInstallInfo.want.GetUriString(); - if (want.GetElement().GetBundleName() != bundleName || - want.GetElement().GetAbilityName() != abilityName || - want.GetStringParam(Want::PARAM_RESV_START_TIME) != startTime || - want.GetUriString() != url) { + if (srcBundleName != freeInstallInfo.want.GetBundle() || + srcAbilityName != freeInstallInfo.want.GetElement().GetAbilityName() || + srcStartTime != freeInstallInfo.want.GetStringParam(Want::PARAM_RESV_START_TIME) || + srcUri != freeInstallInfo.want.GetUriString()) { it++; continue; } @@ -330,7 +331,7 @@ void FreeInstallManager::HandleOnFreeInstallSuccess(int32_t recordId, FreeInstal if (isAsync) { std::string startTime = freeInstallInfo.want.GetStringParam(Want::PARAM_RESV_START_TIME); - std::string bundleName = freeInstallInfo.want.GetElement().GetBundleName(); + std::string bundleName = freeInstallInfo.want.GetBundle(); std::string abilityName = freeInstallInfo.want.GetElement().GetAbilityName(); if (freeInstallInfo.isPreStartMissionCalled) { StartAbilityByPreInstall(recordId, freeInstallInfo, bundleName, abilityName, startTime); @@ -378,7 +379,7 @@ void FreeInstallManager::HandleOnFreeInstallFail(int32_t recordId, FreeInstallIn return; } - std::string bundleName = freeInstallInfo.want.GetElement().GetBundleName(); + std::string bundleName = freeInstallInfo.want.GetBundle(); std::string abilityName = freeInstallInfo.want.GetElement().GetAbilityName(); DelayedSingleton::GetInstance()->OnInstallFinished( recordId, bundleName, abilityName, startTime, resultCode); @@ -564,7 +565,7 @@ int FreeInstallManager::ConnectFreeInstall(const Want &want, int32_t userId, { auto bundleMgrHelper = AbilityUtil::GetBundleManagerHelper(); CHECK_POINTER_AND_RETURN(bundleMgrHelper, GET_ABILITY_SERVICE_FAILED); - std::string wantDeviceId = want.GetElement().GetDeviceID(); + std::string wantDeviceId = want.GetDeviceId(); if (!(localDeviceId == wantDeviceId || wantDeviceId.empty())) { TAG_LOGE(AAFwkTag::FREE_INSTALL, "deviceID empty"); return INVALID_PARAMETERS_ERR; @@ -574,7 +575,7 @@ int FreeInstallManager::ConnectFreeInstall(const Want &want, int32_t userId, bool isAgentConnect = extensionType == AppExecFwk::ExtensionAbilityType::AGENT; if (!isSaCall) { std::string wantAbilityName = want.GetElement().GetAbilityName(); - std::string wantBundleName = want.GetElement().GetBundleName(); + std::string wantBundleName = want.GetBundle(); if (wantBundleName.empty() || wantAbilityName.empty()) { TAG_LOGE(AAFwkTag::FREE_INSTALL, "wantBundleName or wantAbilityName empty."); return INVALID_PARAMETERS_ERR; @@ -595,7 +596,7 @@ int FreeInstallManager::ConnectFreeInstall(const Want &want, int32_t userId, std::vector extensionInfos; TAG_LOGD(AAFwkTag::FREE_INSTALL, "bundleName: %{public}s, moduleName: %{public}s, abilityName: %{public}s, userId: %{public}d", - want.GetElement().GetBundleName().c_str(), want.GetElement().GetModuleName().c_str(), + want.GetBundle().c_str(), want.GetModuleName().c_str(), want.GetElement().GetAbilityName().c_str(), userId); if (!IN_PROCESS_CALL(bundleMgrHelper->QueryAbilityInfo( want, AppExecFwk::AbilityInfoFlag::GET_ABILITY_INFO_WITH_APPLICATION, userId, abilityInfo)) && @@ -644,9 +645,9 @@ void FreeInstallManager::PostUpgradeAtomicServiceTask(int resultCode, const Want } TAG_LOGD(AAFwkTag::FREE_INSTALL, "bundleName: %{public}s, moduleName: %{public}s, abilityName: %{public}s, userId: %{public}d", - want.GetElement().GetBundleName().c_str(), want.GetElement().GetModuleName().c_str(), + want.GetBundle().c_str(), want.GetModuleName().c_str(), want.GetElement().GetAbilityName().c_str(), userId); - std::string nameKey = want.GetElement().GetBundleName() + want.GetElement().GetModuleName(); + std::string nameKey = want.GetBundle() + want.GetModuleName(); bool needUpgrade = false; { std::lock_guard lock(freeInstallManager->timestampMapLock_); @@ -695,7 +696,7 @@ void FreeInstallManager::RemoveFreeInstallInfo(const std::string &bundleName, co { std::lock_guard lock(freeInstallListLock_); for (auto it = freeInstallList_.begin(); it != freeInstallList_.end();) { - if ((*it).want.GetElement().GetBundleName() == bundleName && + if ((*it).want.GetBundle() == bundleName && (*it).want.GetElement().GetAbilityName() == abilityName && (*it).want.GetStringParam(Want::PARAM_RESV_START_TIME) == startTime) { it = freeInstallList_.erase(it); @@ -737,7 +738,7 @@ int FreeInstallManager::SetAppRunningState(Want &want) return ERR_NULL_APP_MGR_CLIENT; } - bool isAppRunning = appMgr->GetAppRunningStateByBundleName(want.GetElement().GetBundleName()); + bool isAppRunning = appMgr->GetAppRunningStateByBundleName(want.GetBundle()); TAG_LOGI(AAFwkTag::FREE_INSTALL, "isAppRunning:%{public}d", static_cast(isAppRunning)); want.SetParam(KEY_IS_APP_RUNNING, isAppRunning); return ERR_OK; @@ -748,7 +749,7 @@ bool FreeInstallManager::GetFreeInstallTaskInfo(const std::string& bundleName, c { std::lock_guard lock(freeInstallListLock_); for (auto it = freeInstallList_.begin(); it != freeInstallList_.end();) { - if ((*it).want.GetElement().GetBundleName() == bundleName && + if ((*it).want.GetBundle() == bundleName && (*it).want.GetElement().GetAbilityName() == abilityName && (*it).want.GetStringParam(Want::PARAM_RESV_START_TIME) == startTime) { taskInfo = *it; @@ -777,7 +778,7 @@ void FreeInstallManager::SetSCBCallStatus(const std::string& bundleName, const s { std::lock_guard lock(freeInstallListLock_); for (auto it = freeInstallList_.begin(); it != freeInstallList_.end();) { - if ((*it).want.GetElement().GetBundleName() == bundleName && + if ((*it).want.GetBundle() == bundleName && (*it).want.GetElement().GetAbilityName() == abilityName && (*it).want.GetStringParam(Want::PARAM_RESV_START_TIME) == startTime) { (*it).isStartUIAbilityBySCBCalled = scbCallStatus; @@ -792,7 +793,7 @@ void FreeInstallManager::SetPreStartMissionCallStatus(const std::string& bundleN { std::lock_guard lock(freeInstallListLock_); for (auto it = freeInstallList_.begin(); it != freeInstallList_.end();) { - if ((*it).want.GetElement().GetBundleName() == bundleName && + if ((*it).want.GetBundle() == bundleName && (*it).want.GetElement().GetAbilityName() == abilityName && (*it).want.GetStringParam(Want::PARAM_RESV_START_TIME) == startTime) { (*it).isPreStartMissionCalled = preStartMissionCallStatus; @@ -807,7 +808,7 @@ void FreeInstallManager::SetFreeInstallTaskSessionId(const std::string& bundleNa { std::lock_guard lock(freeInstallListLock_); for (auto it = freeInstallList_.begin(); it != freeInstallList_.end();) { - if ((*it).want.GetElement().GetBundleName() == bundleName && + if ((*it).want.GetBundle() == bundleName && (*it).want.GetElement().GetAbilityName() == abilityName && (*it).want.GetStringParam(Want::PARAM_RESV_START_TIME) == startTime) { (*it).want.SetParam(KEY_SESSION_ID, sessionId); @@ -822,7 +823,7 @@ void FreeInstallManager::NotifyInsightIntentFreeInstallResult(const Want &want, HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__); TAG_LOGI(AAFwkTag::FREE_INSTALL, "insight install result:%{public}d", resultCode); if (resultCode != ERR_OK) { - RemoveFreeInstallInfo(want.GetElement().GetBundleName(), want.GetElement().GetAbilityName(), + RemoveFreeInstallInfo(want.GetBundle(), want.GetElement().GetAbilityName(), want.GetStringParam(Want::PARAM_RESV_START_TIME)); NotifyInsightIntentExecuteDone(want, ERR_INVALID_VALUE); return; @@ -835,17 +836,17 @@ void FreeInstallManager::NotifyInsightIntentFreeInstallResult(const Want &want, } for (auto it = freeInstallList_.begin(); it != freeInstallList_.end();) { - std::string bundleName = (*it).want.GetElement().GetBundleName(); + std::string bundleName = (*it).want.GetBundle(); std::string abilityName = (*it).want.GetElement().GetAbilityName(); std::string startTime = (*it).want.GetStringParam(Want::PARAM_RESV_START_TIME); - if (want.GetElement().GetBundleName() != bundleName || + if (want.GetBundle() != bundleName || want.GetElement().GetAbilityName() != abilityName || want.GetStringParam(Want::PARAM_RESV_START_TIME) != startTime) { it++; continue; } - auto moduleName = (*it).want.GetElement().GetModuleName(); + auto moduleName = (*it).want.GetModuleName(); auto insightIntentName = (*it).want.GetStringParam(AppExecFwk::INSIGHT_INTENT_EXECUTE_PARAM_NAME); auto executeMode = static_cast( it->want.GetIntParam(AppExecFwk::INSIGHT_INTENT_EXECUTE_PARAM_MODE, 0)); diff --git a/services/abilitymgr/src/implicit_start_processor.cpp b/services/abilitymgr/src/implicit_start_processor.cpp index b5591feb23..c32bc201e4 100644 --- a/services/abilitymgr/src/implicit_start_processor.cpp +++ b/services/abilitymgr/src/implicit_start_processor.cpp @@ -647,7 +647,8 @@ int ImplicitStartProcessor::GenerateAbilityRequestByAppIndexes(int32_t userId, A IN_PROCESS_CALL_WITHOUT_RET(bms->QueryCloneAbilityInfo(request.want.GetElement(), abilityInfoFlag, appIndex, abilityInfo, userId)); if (abilityInfo.name.empty() || abilityInfo.bundleName.empty()) { - int32_t ret = FindExtensionInfo(request.want, abilityInfoFlag, userId, appIndex, abilityInfo); + int32_t ret = FindExtensionInfo(request.want.GetElement(), abilityInfoFlag, userId, appIndex, + abilityInfo); if (ret != ERR_OK) { TAG_LOGE(AAFwkTag::ABILITYMGR, "query info failed"); return ret; @@ -674,8 +675,8 @@ int ImplicitStartProcessor::GenerateAbilityRequestByAppIndexes(int32_t userId, A return ERR_OK; } -int ImplicitStartProcessor::FindExtensionInfo(const Want &want, int32_t flags, int32_t userId, - int32_t appIndex, AppExecFwk::AbilityInfo &abilityInfo) +int ImplicitStartProcessor::FindExtensionInfo(const AppExecFwk::ElementName &elementName, int32_t flags, + int32_t userId, int32_t appIndex, AppExecFwk::AbilityInfo &abilityInfo) { HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__); auto bms = GetBundleManagerHelper(); @@ -683,8 +684,8 @@ int ImplicitStartProcessor::FindExtensionInfo(const Want &want, int32_t flags, i AppExecFwk::ExtensionAbilityInfo extensionInfo; TAG_LOGD(AAFwkTag::ABILITYMGR, "abilityName: %{public}s, appIndex: %{public}d, userId: %{public}d", - want.GetElement().GetAbilityName().c_str(), appIndex, userId); - IN_PROCESS_CALL_WITHOUT_RET(bms->QueryCloneExtensionAbilityInfoWithAppIndex(want.GetElement(), + elementName.GetAbilityName().c_str(), appIndex, userId); + IN_PROCESS_CALL_WITHOUT_RET(bms->QueryCloneExtensionAbilityInfoWithAppIndex(elementName, flags, appIndex, extensionInfo, userId)); if (extensionInfo.bundleName.empty() || extensionInfo.name.empty()) { TAG_LOGE(AAFwkTag::ABILITYMGR, "extensionInfo empty."); @@ -761,7 +762,7 @@ std::vector ImplicitStartProcessor::SplitStr(const std::string& str bool ImplicitStartProcessor::CheckImplicitStartExtensionIsValid(const AbilityRequest &request, const AppExecFwk::ExtensionAbilityInfo &extensionInfo) { - if (!request.want.GetElement().GetBundleName().empty()) { + if (!request.want.GetBundle().empty()) { return true; } TAG_LOGD( @@ -805,7 +806,17 @@ int32_t ImplicitStartProcessor::ImplicitStartAbilityInner(const Want &targetWant break; default: StartAbilityWrapParam startAbilityWrapParam = { - targetWant, request.callerToken, request.requestCode, false, userId, false, 0, false, true }; + .want = targetWant, + .callerToken = request.callerToken, + .requestCode = request.requestCode, + .isPendingWantCaller = false, + .userId = userId, + .isStartAsCaller = false, + .specifyTokenId = 0, + .isForegroundToRestartApp = false, + .isImplicit = true, + .requestCallback = request.requestCallback, + }; result = abilityMgr->StartAbilityWrap(startAbilityWrapParam); break; } @@ -818,9 +829,10 @@ int ImplicitStartProcessor::CallStartAbilityInner(int32_t userId, { EventInfo eventInfo; eventInfo.userId = userId; - eventInfo.bundleName = want.GetElement().GetBundleName(); - eventInfo.moduleName = want.GetElement().GetModuleName(); - eventInfo.abilityName = want.GetElement().GetAbilityName(); + auto element = want.GetElement(); + eventInfo.bundleName = element.GetBundleName(); + eventInfo.moduleName = element.GetModuleName(); + eventInfo.abilityName = element.GetAbilityName(); if (callType == AbilityCallType::INVALID_TYPE) { eventInfo.calleeId = static_cast(CalleeId::IMPLICIT_START_PROCESSOR_CALL_START_ABILITY_INNER); @@ -1067,7 +1079,7 @@ bool ImplicitStartProcessor::IsActionImplicitStart(const Want &want, bool findDe return false; } - if (want.GetElement().GetBundleName() != "") { + if (want.GetBundle() != "") { return false; } diff --git a/services/abilitymgr/src/insight_intent/insight_intent_execute_manager.cpp b/services/abilitymgr/src/insight_intent/insight_intent_execute_manager.cpp index 2be1270f16..668b2aceb8 100644 --- a/services/abilitymgr/src/insight_intent/insight_intent_execute_manager.cpp +++ b/services/abilitymgr/src/insight_intent/insight_intent_execute_manager.cpp @@ -21,6 +21,7 @@ #include "ability_config.h" #include "ability_util.h" #include "ability_manager_errors.h" +#include "distributed_ability_runtime/distributed_client.h" #include "extract_insight_intent_profile.h" #include "hilog_tag_wrapper.h" #include "insight_intent_execute_callback_interface.h" @@ -137,13 +138,15 @@ InsightIntentExecuteManager::~InsightIntentExecuteManager() = default; int32_t InsightIntentExecuteManager::CheckAndUpdateParam(uint64_t key, const sptr &callerToken, const std::shared_ptr ¶m, std::string callerBundleName, - const bool ignoreAbilityName) + const bool ignoreAbilityName, bool isDistributed, const std::string &srcDeviceId, uint64_t requestCode, + uint64_t specifiedFullTokenId) { - int32_t result = CheckCallerPermission(); + std::string targetBundleName = (param != nullptr) ? param->bundleName_ : ""; + int32_t result = CheckCallerPermission(specifiedFullTokenId, callerBundleName, targetBundleName); if (result != ERR_OK && (param == nullptr || !param->isServiceMatch_)) { return result; } - if (callerToken == nullptr && (param == nullptr || !param->isServiceMatch_)) { + if ((!isDistributed && callerToken == nullptr) && (param == nullptr || !param->isServiceMatch_)) { TAG_LOGE(AAFwkTag::INTENT, "null callerToken"); return ERR_INVALID_VALUE; } @@ -151,13 +154,15 @@ int32_t InsightIntentExecuteManager::CheckAndUpdateParam(uint64_t key, const spt TAG_LOGE(AAFwkTag::INTENT, "null param"); return ERR_INVALID_VALUE; } + if (param->bundleName_.empty() || param->moduleName_.empty() || (!ignoreAbilityName && param->abilityName_.empty()) || param->insightIntentName_.empty()) { TAG_LOGE(AAFwkTag::INTENT, "invalid param"); return ERR_INVALID_VALUE; } uint64_t intentId = 0; - result = AddRecord(key, callerToken, param->bundleName_, intentId, callerBundleName); + result = AddRecord(key, callerToken, param->bundleName_, + intentId, callerBundleName, isDistributed, srcDeviceId, requestCode); if (result != ERR_OK) { return result; } @@ -244,7 +249,8 @@ int32_t InsightIntentExecuteManager::UpdateEntryDecoratorParams(Want &want, Exec } int32_t InsightIntentExecuteManager::AddRecord(uint64_t key, const sptr &callerToken, - const std::string &bundleName, uint64_t &intentId, const std::string &callerBundleName) + const std::string &bundleName, uint64_t &intentId, const std::string &callerBundleName, + bool isDistributed, const std::string &deviceId, uint64_t requestCode) { std::lock_guard lock(mutex_); intentId = ++intentIdCount_; @@ -254,6 +260,9 @@ int32_t InsightIntentExecuteManager::AddRecord(uint64_t key, const sptrcallerToken = callerToken; record->bundleName = bundleName; record->callerBundleName = callerBundleName; + record->isDistributed = isDistributed; + record->deviceId = deviceId; + record->requestCode = requestCode; if (callerToken != nullptr) { record->deathRecipient = sptr::MakeSptr(intentId); callerToken->AddDeathRecipient(record->deathRecipient); @@ -265,7 +274,6 @@ int32_t InsightIntentExecuteManager::AddRecord(uint64_t key, const sptr lock(mutex_); EventInfo eventInfo; auto findResult = records_.find(intentId); @@ -307,13 +315,31 @@ int32_t InsightIntentExecuteManager::ExecuteIntentDone(uint64_t intentId, int32_ return ERR_INVALID_OPERATION; } record->state = InsightIntentExecuteState::EXECUTE_DONE; - sptr remoteCallback = iface_cast(record->callerToken); - if (remoteCallback == nullptr) { - TAG_LOGE(AAFwkTag::INTENT, "intentExecuteCallback empty," - " intentId: %{public}" PRIu64 ", records_ size: %{public}zu", intentId, records_.size()); - return ERR_INVALID_VALUE; + + if (record->isDistributed) { + std::string msg = result.ToJsonString(); + Want want; + want.SetElementName(record->deviceId, record->bundleName, "", ""); + IntentCallerInfo callerInfo; + callerInfo.callerUid = callerUid; + callerInfo.requestCode = record->requestCode; + callerInfo.accessToken = accessToken; + DistributedClient dmsClient; + int32_t ret = dmsClient.SendIntentResult(want, callerInfo, msg); + if (ret != ERR_OK) { + TAG_LOGE(AAFwkTag::INTENT, "SendIntentResult failed: %{public}d", ret); + } + } else { + sptr remoteCallback = + iface_cast(record->callerToken); + if (remoteCallback == nullptr) { + TAG_LOGE(AAFwkTag::INTENT, "intentExecuteCallback empty," + " intentId: %{public}" PRIu64 ", records_ size: %{public}zu", intentId, records_.size()); + return ERR_INVALID_VALUE; + } + remoteCallback->OnExecuteDone(record->key, resultCode, result); } - remoteCallback->OnExecuteDone(record->key, resultCode, result); + if (record->callerToken != nullptr) { record->callerToken->RemoveDeathRecipient(record->deathRecipient); record->callerToken = nullptr; @@ -404,13 +430,13 @@ int32_t InsightIntentExecuteManager::UpdateFuncDecoratorParams( } if (param->abilityName_.empty()) { - param->abilityName_ = GetMainElementName(param->bundleName_, param->moduleName_); + param->abilityName_ = GetMainElementName(param->bundleName_, param->moduleName_, param->userId_); } if (param->abilityName_.empty()) { TAG_LOGE(AAFwkTag::INTENT, "ability name empty"); return ERR_INVALID_VALUE; } - want.SetElementName("", param->bundleName_, param->abilityName_, param->moduleName_); + want.SetElementName(param->deviceId_, param->bundleName_, param->abilityName_, param->moduleName_); std::string srcEntrance = info.decoratorFile; want.SetParam(INSIGHT_INTENT_SRC_ENTRANCE, srcEntrance); @@ -441,7 +467,7 @@ int32_t InsightIntentExecuteManager::UpdateFuncDecoratorParams( } std::string InsightIntentExecuteManager::GetMainElementName(const std::string &bundleName, - const std::string &moduleName) + const std::string &moduleName, int32_t userId) { auto bms = AbilityUtil::GetBundleManagerHelper(); if (bms == nullptr) { @@ -449,7 +475,9 @@ std::string InsightIntentExecuteManager::GetMainElementName(const std::string &b return ""; } - const int32_t userId = IPCSkeleton::GetCallingUid() / AppExecFwk::Constants::BASE_USER_RANGE; + if (userId == DEFAULT_INVAL_VALUE) { + userId = IPCSkeleton::GetCallingUid() / AppExecFwk::Constants::BASE_USER_RANGE; + } std::vector abilityInfos; if (IN_PROCESS_CALL(bms->GetLauncherAbilityInfoSync(bundleName, userId, abilityInfos)) != ERR_OK) { TAG_LOGE(AAFwkTag::INTENT, "get launcher ability info failed"); @@ -496,7 +524,7 @@ int32_t InsightIntentExecuteManager::UpdatePageDecoratorParams( return ERR_INVALID_VALUE; } param->abilityName_ = uiAbilityName; - want.SetElementName("", param->bundleName_, param->abilityName_, param->moduleName_); + want.SetElementName(param->deviceId_, param->bundleName_, param->abilityName_, param->moduleName_); want.SetParam(INSIGHT_INTENT_PAGE_PARAM_PAGEPATH, pagePath); want.SetParam(INSIGHT_INTENT_PAGE_PARAM_NAVIGATIONID, navigationId); want.SetParam(INSIGHT_INTENT_PAGE_PARAM_NAVDESTINATIONNAME, navDestinationName); @@ -618,7 +646,7 @@ int32_t InsightIntentExecuteManager::GenerateWant( std::chrono::system_clock::now().time_since_epoch()).count()); want.SetParam(Want::PARAM_RESV_START_TIME, startTime); want.AddFlags(Want::FLAG_INSTALL_ON_DEMAND); - } else if (decoratorInfo.decoratorType == "" && !param->isServiceMatch_) { + } else if (decoratorInfo.decoratorType == "" && !param->isServiceMatch_ && param->deviceId_.empty()) { // decoratorType is empty indicate no decorator TAG_LOGE(AAFwkTag::INTENT, "insight intent srcEntry invalid"); return ERR_INVALID_VALUE; @@ -627,11 +655,19 @@ int32_t InsightIntentExecuteManager::GenerateWant( want.SetParam(INSIGHT_INTENT_EXECUTE_PARAM_NAME, param->insightIntentName_); want.SetParam(INSIGHT_INTENT_EXECUTE_PARAM_MODE, param->executeMode_); want.SetParam(INSIGHT_INTENT_EXECUTE_PARAM_ID, std::to_string(param->insightIntentId_)); + want.SetParam(INSIGHT_INTENT_PARAM_USER_ID, param->userId_); if (param->displayId_ != INVALID_DISPLAY_ID) { want.SetParam(Want::PARAM_RESV_DISPLAY_ID, param->displayId_); TAG_LOGD(AAFwkTag::INTENT, "Generate want with displayId: %{public}d", param->displayId_); } + if (!param->deviceId_.empty()) { + want.SetDeviceId(param->deviceId_); + } + if (!param->uris_.empty()) { + want.SetParam(INSIGHT_INTENT_EXECUTE_PARAM_URI, param->uris_); + } + auto intRet = AddWantUirsAndFlagsFromParam(param, want); if (intRet != ERR_OK) { return intRet; @@ -662,34 +698,81 @@ int32_t InsightIntentExecuteManager::IsValidCall(const Want &want) return ERR_OK; } -int32_t InsightIntentExecuteManager::CheckCallerPermission() +int32_t InsightIntentExecuteManager::CheckCallerPermission(uint64_t specifiedFullTokenId, + const std::string &callerBundleName, const std::string &targetBundleName) { - bool isSystemAppCall = PermissionVerification::GetInstance()->JudgeCallerIsAllowedToUseSystemAPI(); + TAG_LOGI(AAFwkTag::INTENT, "specifiedFullTokenId: %{public}" PRIu64, specifiedFullTokenId); + bool isSystemAppCall = false; + if (specifiedFullTokenId != 0) { + isSystemAppCall = PermissionVerification::GetInstance()->JudgeCallerIsAllowedToUseSystemAPIByTokenId( + specifiedFullTokenId); + } else { + isSystemAppCall = PermissionVerification::GetInstance()->JudgeCallerIsAllowedToUseSystemAPI(); + } + if (PermissionVerification::GetInstance()->IsShellCall()) { + TAG_LOGD(AAFwkTag::INTENT, "shell caller, skip permission check"); + return ERR_OK; + } + if (isSystemAppCall && !callerBundleName.empty() && callerBundleName == targetBundleName) { + TAG_LOGI(AAFwkTag::INTENT, "system app execute own intent, skip permission check"); + return ERR_OK; + } if (!isSystemAppCall) { TAG_LOGE(AAFwkTag::INTENT, "system-api cannot use"); return ERR_NOT_SYSTEM_APP; } bool isCallingPerm = PermissionVerification::GetInstance()->VerifyCallingPermission( - EXECUTE_INSIGHT_INTENT_PERMISSION); + EXECUTE_INSIGHT_INTENT_PERMISSION, specifiedFullTokenId); if (!isCallingPerm) { - TAG_LOGE(AAFwkTag::INTENT, "permission %{public}s verification failed", EXECUTE_INSIGHT_INTENT_PERMISSION); + TAG_LOGE(AAFwkTag::INTENT, + "permission %{public}s verification failed", + EXECUTE_INSIGHT_INTENT_PERMISSION); return ERR_PERMISSION_DENIED; } return ERR_OK; } -int32_t InsightIntentExecuteManager::CheckGetInsightIntenInfoPermission() +std::string InsightIntentExecuteManager::GetCallerBundleNameByUid() { - bool isSystemAppCall = PermissionVerification::GetInstance()->JudgeCallerIsAllowedToUseSystemAPI(); + int32_t callerUid = IPCSkeleton::GetCallingUid(); + auto bundleMgr = AbilityUtil::GetBundleManagerHelper(); + std::string bundleName; + if (bundleMgr != nullptr) { + IN_PROCESS_CALL(bundleMgr->GetNameForUid(callerUid, bundleName)); + } + return bundleName; +} + +int32_t InsightIntentExecuteManager::CheckGetInsightIntenInfoPermission( + const std::string &targetBundleName) +{ + if (PermissionVerification::GetInstance()->IsShellCall()) { + TAG_LOGD(AAFwkTag::INTENT, "shell caller, skip permission check"); + return ERR_OK; + } + + bool isSystemAppCall = + PermissionVerification::GetInstance()->JudgeCallerIsAllowedToUseSystemAPI(); + if (isSystemAppCall && !targetBundleName.empty()) { + auto callerBundle = GetCallerBundleNameByUid(); + if (!callerBundle.empty() && callerBundle == targetBundleName) { + TAG_LOGI(AAFwkTag::INTENT, "caller query own intent info, skip permission check"); + return ERR_OK; + } + } + if (!isSystemAppCall) { TAG_LOGE(AAFwkTag::INTENT, "system-api cannot use"); return ERR_NOT_SYSTEM_APP; } - bool isCallingPerm = PermissionVerification::GetInstance()->VerifyGetBundleInfoPrivilegedPermission(); + bool isCallingPerm = + PermissionVerification::GetInstance()->VerifyGetBundleInfoPrivilegedPermission(); if (!isCallingPerm) { - TAG_LOGE(AAFwkTag::INTENT, "permission %{public}s verification failed", PERMISSION_GET_BUNDLE_INFO_PRIVILEGED); + TAG_LOGE(AAFwkTag::INTENT, + "permission %{public}s verification failed", + PERMISSION_GET_BUNDLE_INFO_PRIVILEGED); return ERR_PERMISSION_DENIED; } return ERR_OK; diff --git a/services/abilitymgr/src/insight_intent/insight_intent_execute_param.cpp b/services/abilitymgr/src/insight_intent/insight_intent_execute_param.cpp index b6195fdd87..90128e22f6 100644 --- a/services/abilitymgr/src/insight_intent/insight_intent_execute_param.cpp +++ b/services/abilitymgr/src/insight_intent/insight_intent_execute_param.cpp @@ -160,6 +160,7 @@ bool InsightIntentExecuteParam::ReadFromParcel(Parcel &parcel) navigationId_ = Str16ToStr8(parcel.ReadString16()); navDestinationName_ = Str16ToStr8(parcel.ReadString16()); isServiceMatch_ = parcel.ReadBool(); + deviceId_ = Str16ToStr8(parcel.ReadString16()); return true; } @@ -200,6 +201,7 @@ bool InsightIntentExecuteParam::Marshalling(Parcel &parcel) const parcel.WriteString16(Str8ToStr16(navigationId_)); parcel.WriteString16(Str8ToStr16(navDestinationName_)); parcel.WriteBool(isServiceMatch_); + parcel.WriteString16(Str8ToStr16(deviceId_)); return true; } @@ -266,6 +268,7 @@ bool InsightIntentExecuteParam::GenerateFromWant(const AAFwk::Want &want, executeParam.navDestinationName_ = wantParams.GetStringParam(INSIGHT_INTENT_PAGE_PARAM_NAVDESTINATIONNAME); executeParam.queryEntityClassName_ = wantParams.GetStringParam(INSIGHT_INTENT_QUERY_ENTITY_CLASS_NAME); executeParam.queryType_ = wantParams.GetStringParam(INSIGHT_INTENT_QUERY_TYPE); + executeParam.userId_ = wantParams.GetIntParam(INSIGHT_INTENT_PARAM_USER_ID, -1); auto queryParams = wantParams.GetWantParams(INSIGHT_INTENT_QUERY_ENTITY_PARAM_PARAM); executeParam.queryParams_ = std::make_shared(queryParams); return true; diff --git a/services/abilitymgr/src/insight_intent/insight_intent_execute_result.cpp b/services/abilitymgr/src/insight_intent/insight_intent_execute_result.cpp index e7f0f0a445..36faaee070 100644 --- a/services/abilitymgr/src/insight_intent/insight_intent_execute_result.cpp +++ b/services/abilitymgr/src/insight_intent/insight_intent_execute_result.cpp @@ -15,9 +15,25 @@ #include "insight_intent_execute_result.h" +#include "nlohmann/json.hpp" + namespace OHOS { namespace AppExecFwk { using WantParams = OHOS::AAFwk::WantParams; + +namespace { +constexpr const char *KEY_INNER_ERR = "innerErr"; +constexpr const char *KEY_CODE = "code"; +constexpr const char *KEY_FLAGS = "flags"; +constexpr const char *KEY_RESULT = "result"; +constexpr const char *KEY_URIS = "uris"; +constexpr const char *KEY_IS_DECORATOR = "isDecorator"; +constexpr const char *KEY_IS_NEED_DELAY_RESULT = "isNeedDelayResult"; +constexpr const char *KEY_IS_QUERY_ENTITY = "isQueryEntity"; +constexpr const char *KEY_QUERY_RESULTS = "queryResults"; +constexpr int32_t CYCLE_LIMIT = 1000; +} // namespace + bool InsightIntentExecuteResult::ReadFromParcel(Parcel &parcel) { innerErr = parcel.ReadInt32(); @@ -29,9 +45,17 @@ bool InsightIntentExecuteResult::ReadFromParcel(Parcel &parcel) flags = parcel.ReadInt32(); isDecorator = parcel.ReadBool(); isQueryEntity = parcel.ReadBool(); - queryResults.resize(parcel.ReadInt32()); - for (size_t i = 0; i < queryResults.size(); i++) { - queryResults[i] = std::shared_ptr(parcel.ReadParcelable()); + int32_t resultSize = parcel.ReadInt32(); + if (resultSize < 0 || resultSize > CYCLE_LIMIT) { + return false; + } + queryResults.clear(); + for (int32_t i = 0; i < resultSize; i++) { + auto temp = std::shared_ptr(parcel.ReadParcelable()); + if (temp == nullptr) { + return false; + } + queryResults.push_back(temp); } return true; } @@ -84,6 +108,96 @@ InsightIntentExecuteResult *InsightIntentExecuteResult::Unmarshalling(Parcel &pa return res; } +void InsightIntentExecuteResult::FromJsonString(const std::string &jsonStr) +{ + nlohmann::json jsonObject = nlohmann::json::parse(jsonStr, nullptr, false); + if (jsonObject.is_discarded() || !jsonObject.is_object()) { + return; + } + + if (jsonObject.contains(KEY_INNER_ERR) && jsonObject.at(KEY_INNER_ERR).is_number_integer()) { + innerErr = jsonObject.at(KEY_INNER_ERR).get(); + } + if (jsonObject.contains(KEY_CODE) && jsonObject.at(KEY_CODE).is_number_integer()) { + code = jsonObject.at(KEY_CODE).get(); + } + if (jsonObject.contains(KEY_FLAGS) && jsonObject.at(KEY_FLAGS).is_number_integer()) { + flags = jsonObject.at(KEY_FLAGS).get(); + } + if (jsonObject.contains(KEY_IS_DECORATOR) && jsonObject.at(KEY_IS_DECORATOR).is_boolean()) { + isDecorator = jsonObject.at(KEY_IS_DECORATOR).get(); + } + if (jsonObject.contains(KEY_IS_NEED_DELAY_RESULT) && jsonObject.at(KEY_IS_NEED_DELAY_RESULT).is_boolean()) { + isNeedDelayResult = jsonObject.at(KEY_IS_NEED_DELAY_RESULT).get(); + } + if (jsonObject.contains(KEY_IS_QUERY_ENTITY) && jsonObject.at(KEY_IS_QUERY_ENTITY).is_boolean()) { + isQueryEntity = jsonObject.at(KEY_IS_QUERY_ENTITY).get(); + } + + if (jsonObject.contains(KEY_URIS) && jsonObject.at(KEY_URIS).is_array()) { + uris.clear(); + for (const auto &item : jsonObject.at(KEY_URIS)) { + if (item.is_string()) { + uris.emplace_back(item.get()); + } + } + } + + if (jsonObject.contains(KEY_RESULT)) { + const auto &resultJson = jsonObject.at(KEY_RESULT); + if (resultJson.is_object()) { + result = std::make_shared(); + OHOS::AAFwk::from_json(resultJson, *result); + } else if (resultJson.is_null()) { + result = nullptr; + } + } + + if (jsonObject.contains(KEY_QUERY_RESULTS) && jsonObject.at(KEY_QUERY_RESULTS).is_array()) { + queryResults.clear(); + for (const auto &item : jsonObject.at(KEY_QUERY_RESULTS)) { + if (!item.is_object()) { + continue; + } + auto queryResult = std::make_shared(); + OHOS::AAFwk::from_json(item, *queryResult); + queryResults.emplace_back(queryResult); + } + } +} + +std::string InsightIntentExecuteResult::ToJsonString() const +{ + nlohmann::json jsonObject; + jsonObject[KEY_INNER_ERR] = innerErr; + jsonObject[KEY_CODE] = code; + jsonObject[KEY_FLAGS] = flags; + jsonObject[KEY_URIS] = uris; + jsonObject[KEY_IS_DECORATOR] = isDecorator; + jsonObject[KEY_IS_NEED_DELAY_RESULT] = isNeedDelayResult; + jsonObject[KEY_IS_QUERY_ENTITY] = isQueryEntity; + + if (result != nullptr) { + nlohmann::json resultJson; + OHOS::AAFwk::to_json(resultJson, *result); + jsonObject[KEY_RESULT] = resultJson; + } else { + jsonObject[KEY_RESULT] = nullptr; + } + + nlohmann::json queryResultsJson = nlohmann::json::array(); + for (const auto &item : queryResults) { + if (item == nullptr) { + continue; + } + nlohmann::json itemJson; + OHOS::AAFwk::to_json(itemJson, *item); + queryResultsJson.emplace_back(itemJson); + } + jsonObject[KEY_QUERY_RESULTS] = queryResultsJson; + return jsonObject.dump(); +} + bool InsightIntentExecuteResult::CheckResult(std::shared_ptr result) { return true; diff --git a/services/abilitymgr/src/interceptor/control_interceptor.cpp b/services/abilitymgr/src/interceptor/control_interceptor.cpp index 127904d976..e4edaf133d 100644 --- a/services/abilitymgr/src/interceptor/control_interceptor.cpp +++ b/services/abilitymgr/src/interceptor/control_interceptor.cpp @@ -48,9 +48,10 @@ ErrCode ControlInterceptor::DoProcess(AbilityInterceptorParam param) controlParam.SetParam(INTERCEPT_PARAMETERS, interceptParam); } controlWant->SetParams(controlParam); - controlWant->SetParam(INTERCEPT_BUNDLE_NAME, param.want.GetElement().GetBundleName()); - controlWant->SetParam(INTERCEPT_ABILITY_NAME, param.want.GetElement().GetAbilityName()); - controlWant->SetParam(INTERCEPT_MODULE_NAME, param.want.GetElement().GetModuleName()); + auto wantEle = param.want.GetElement(); + controlWant->SetParam(INTERCEPT_BUNDLE_NAME, wantEle.GetBundleName()); + controlWant->SetParam(INTERCEPT_ABILITY_NAME, wantEle.GetAbilityName()); + controlWant->SetParam(INTERCEPT_MODULE_NAME, wantEle.GetModuleName()); controlRule.controlWant = controlWant; } int ret = IN_PROCESS_CALL(AbilityManagerClient::GetInstance()->StartAbility(*controlRule.controlWant, diff --git a/services/abilitymgr/src/interceptor/ecological_rule_interceptor.cpp b/services/abilitymgr/src/interceptor/ecological_rule_interceptor.cpp index 84bcde2c1b..fad0fb46e3 100644 --- a/services/abilitymgr/src/interceptor/ecological_rule_interceptor.cpp +++ b/services/abilitymgr/src/interceptor/ecological_rule_interceptor.cpp @@ -86,7 +86,7 @@ bool EcologicalRuleInterceptor::NoNeedErms(const AbilityInterceptorParam ¶m) return true; } if (param.want.GetStringParam(Want::PARAM_RESV_CALLER_BUNDLE_NAME) == - param.want.GetElement().GetBundleName()) { + param.want.GetBundle()) { TAG_LOGD(AAFwkTag::ECOLOGICAL_RULE, "same bundle"); StartAbilityUtils::ermsSupportBackToCallerFlag = true; return true; @@ -101,7 +101,7 @@ bool EcologicalRuleInterceptor::NoNeedErms(const AbilityInterceptorParam ¶m) bool EcologicalRuleInterceptor::DoProcess(Want &want, int32_t userId) { HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__); - if (want.GetStringParam(Want::PARAM_RESV_CALLER_BUNDLE_NAME) == want.GetElement().GetBundleName()) { + if (want.GetStringParam(Want::PARAM_RESV_CALLER_BUNDLE_NAME) == want.GetBundle()) { TAG_LOGD(AAFwkTag::ECOLOGICAL_RULE, "same bundle"); StartAbilityUtils::ermsSupportBackToCallerFlag = true; return true; @@ -148,7 +148,7 @@ ErrCode EcologicalRuleInterceptor::QueryAtomicServiceStartupRule(Want &want, spt int32_t userId, AtomicServiceStartupRule &rule, sptr &replaceWant) { HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__); - CHECK_TRUE_RETURN_RET(want.GetStringParam(Want::PARAM_RESV_CALLER_BUNDLE_NAME) == want.GetElement().GetBundleName(), + CHECK_TRUE_RETURN_RET(want.GetStringParam(Want::PARAM_RESV_CALLER_BUNDLE_NAME) == want.GetBundle(), ERR_INVALID_CALLER, "same bundle"); std::string supportErms = OHOS::system::GetParameter(ABILITY_SUPPORT_ECOLOGICAL_RULEMGRSERVICE, "true"); CHECK_TRUE_RETURN_RET(supportErms == "false", ERR_CAPABILITY_NOT_SUPPORT, "not support erms"); @@ -293,7 +293,7 @@ void EcologicalRuleInterceptor::InitErmsCallerInfo(const Want &want, } callerInfo.userId = userId; - if (want.GetElement().GetBundleName().empty() && abilityInfo != nullptr) { + if (want.GetBundle().empty() && abilityInfo != nullptr) { callerInfo.targetBundleName = abilityInfo->bundleName; } diff --git a/services/abilitymgr/src/interceptor/extension_control_interceptor.cpp b/services/abilitymgr/src/interceptor/extension_control_interceptor.cpp index 2db9613c29..30f157f0ef 100644 --- a/services/abilitymgr/src/interceptor/extension_control_interceptor.cpp +++ b/services/abilitymgr/src/interceptor/extension_control_interceptor.cpp @@ -124,7 +124,7 @@ bool ExtensionControlInterceptor::GetCallerAbilityInfo(const AbilityInterceptorP if (StartAbilityUtils::GetCallerAbilityInfo(param.callerToken, callerAbilityInfo)) { if (callerAbilityInfo.type != AppExecFwk::AbilityType::EXTENSION || callerAbilityInfo.extensionAbilityType == AppExecFwk::ExtensionAbilityType::SERVICE || - callerAbilityInfo.bundleName == param.want.GetElement().GetBundleName()) { + callerAbilityInfo.bundleName == param.want.GetBundle()) { TAG_LOGD(AAFwkTag::ABILITYMGR, "not other extension."); return true; } diff --git a/services/abilitymgr/src/interceptor/kiosk_interceptor.cpp b/services/abilitymgr/src/interceptor/kiosk_interceptor.cpp index 54391e471b..d718858d08 100644 --- a/services/abilitymgr/src/interceptor/kiosk_interceptor.cpp +++ b/services/abilitymgr/src/interceptor/kiosk_interceptor.cpp @@ -35,7 +35,7 @@ int KioskInterceptor::DoProcess(AbilityInterceptorParam param) if (!kioskManager.IsInKioskMode()) { return ERR_OK; } - auto bundleName = param.want.GetElement().GetBundleName(); + auto bundleName = param.want.GetBundle(); if (!kioskManager.IsInWhiteList(bundleName)) { return ERR_KIOSK_MODE_NOT_IN_WHITELIST; } diff --git a/services/abilitymgr/src/interceptor/start_other_app_interceptor.cpp b/services/abilitymgr/src/interceptor/start_other_app_interceptor.cpp index 97997fb788..87c78844dd 100644 --- a/services/abilitymgr/src/interceptor/start_other_app_interceptor.cpp +++ b/services/abilitymgr/src/interceptor/start_other_app_interceptor.cpp @@ -134,7 +134,7 @@ bool StartOtherAppInterceptor::CheckAncoShellCall(const AppExecFwk::ApplicationI bool StartOtherAppInterceptor::CheckStartOtherApp(const Want want) { - return want.GetStringParam(Want::PARAM_RESV_CALLER_BUNDLE_NAME) != want.GetElement().GetBundleName(); + return want.GetStringParam(Want::PARAM_RESV_CALLER_BUNDLE_NAME) != want.GetBundle(); } bool StartOtherAppInterceptor::CheckCallerApiBelow12(const AppExecFwk::ApplicationInfo &applicationInfo) diff --git a/services/abilitymgr/src/mission/mission_ability_record.cpp b/services/abilitymgr/src/mission/mission_ability_record.cpp index f183131862..e3b0f92f82 100644 --- a/services/abilitymgr/src/mission/mission_ability_record.cpp +++ b/services/abilitymgr/src/mission/mission_ability_record.cpp @@ -183,7 +183,7 @@ void MissionAbilityRecord::ProcessForegroundAbility(const std::shared_ptr::GetInstance()->MoveToForeground(token_); } @@ -210,7 +210,7 @@ void MissionAbilityRecord::ProcessForegroundAbility(bool isRecent, const Ability { HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__); TAG_LOGD(AAFwkTag::ABILITYMGR, "SUPPORT_GRAPHICS: ability record: %{public}s/%{public}s", - GetElementName().GetBundleName().c_str(), GetElementName().GetAbilityName().c_str()); + GetBundleName().c_str(), GetAbilityName().c_str()); #ifdef SUPPORT_UPMS { std::lock_guard guard(wantLock_); @@ -237,12 +237,12 @@ void MissionAbilityRecord::ProcessForegroundAbility(bool isRecent, const Ability PostForegroundTimeoutTask(); if (IsAbilityState(AbilityState::FOREGROUND)) { TAG_LOGD(AAFwkTag::ABILITYMGR, "Activate %{public}s/%{public}s", - GetElementName().GetBundleName().c_str(), GetElementName().GetAbilityName().c_str()); + GetBundleName().c_str(), GetAbilityName().c_str()); ForegroundAbility(sceneFlag); } else { // background to active state TAG_LOGD(AAFwkTag::ABILITYMGR, "MoveToForeground, %{public}s/%{public}s", - GetElementName().GetBundleName().c_str(), GetElementName().GetAbilityName().c_str()); + GetBundleName().c_str(), GetAbilityName().c_str()); lifeCycleStateInfo_.sceneFlagBak = sceneFlag; DelayedSingleton::GetInstance()->MoveToForeground(token_); } diff --git a/services/abilitymgr/src/mission/mission_list_manager.cpp b/services/abilitymgr/src/mission/mission_list_manager.cpp index b3655c161f..a6313f89d3 100644 --- a/services/abilitymgr/src/mission/mission_list_manager.cpp +++ b/services/abilitymgr/src/mission/mission_list_manager.cpp @@ -178,13 +178,13 @@ int MissionListManager::StartAbility(AbilityRequest &abilityRequest) if (currentTopAbility && !currentTopAbility->GetRestartAppFlag()) { auto state = currentTopAbility->GetAbilityState(); TAG_LOGD(AAFwkTag::ABILITYMGR, "current top: %{public}s/%{public}s, state: %{public}s", - currentTopAbility->GetElementName().GetBundleName().c_str(), - currentTopAbility->GetElementName().GetAbilityName().c_str(), + currentTopAbility->GetBundleName().c_str(), + currentTopAbility->GetAbilityName().c_str(), AbilityRecord::ConvertAbilityState(state).c_str()); if (state == FOREGROUNDING) { TAG_LOGI(AAFwkTag::ABILITYMGR, "top ability:%{public}s/%{public}s foregrounding", - currentTopAbility->GetElementName().GetBundleName().c_str(), - currentTopAbility->GetElementName().GetAbilityName().c_str()); + currentTopAbility->GetBundleName().c_str(), + currentTopAbility->GetAbilityName().c_str()); EnqueueWaitingAbility(abilityRequest); return START_ABILITY_WAITING; } @@ -194,8 +194,8 @@ int MissionListManager::StartAbility(AbilityRequest &abilityRequest) if (callerAbility) { auto state = callerAbility->GetAbilityState(); TAG_LOGD(AAFwkTag::ABILITYMGR, "callerAbility is: %{public}s/%{public}s, state: %{public}s", - callerAbility->GetElementName().GetBundleName().c_str(), - callerAbility->GetElementName().GetAbilityName().c_str(), + callerAbility->GetBundleName().c_str(), + callerAbility->GetAbilityName().c_str(), AbilityRecord::ConvertAbilityState(state).c_str()); } @@ -1039,7 +1039,7 @@ int MissionListManager::AttachAbilityThread(const sptr &sched if (abilityRecord->IsStartedByCall()) { (void)abilityRecord->PromotePriority(); - if (abilityRecord->GetWant().GetBoolParam(Want::PARAM_RESV_CALL_TO_FOREGROUND, false)) { + if (abilityRecord->GetBoolParam(Want::PARAM_RESV_CALL_TO_FOREGROUND, false)) { abilityRecord->SetStartToForeground(true); abilityRecord->PostForegroundTimeoutTask(); DelayedSingleton::GetInstance()->MoveToForeground(token); @@ -1077,8 +1077,8 @@ void MissionListManager::OnAbilityRequestDone(const sptr &token, auto abilityRecord = GetAliveAbilityRecordByToken(token); CHECK_POINTER(abilityRecord); TAG_LOGD(AAFwkTag::ABILITYMGR, "Ability is %{public}s/%{public}s, start to foreground.", - abilityRecord->GetElementName().GetBundleName().c_str(), - abilityRecord->GetElementName().GetAbilityName().c_str()); + abilityRecord->GetBundleName().c_str(), + abilityRecord->GetAbilityName().c_str()); abilityRecord->ForegroundAbility(abilityRecord->lifeCycleStateInfo_.sceneFlagBak); } } @@ -1225,8 +1225,8 @@ int MissionListManager::AbilityTransactionDone(const sptr &token, } abilityRecord->RemoveSignatureInfo(); TAG_LOGD(AAFwkTag::ABILITYMGR, "ability: %{public}s/%{public}s, state: %{public}s", - abilityRecord->GetElementName().GetBundleName().c_str(), - abilityRecord->GetElementName().GetAbilityName().c_str(), abilityState.c_str()); + abilityRecord->GetBundleName().c_str(), + abilityRecord->GetAbilityName().c_str(), abilityState.c_str()); if (targetState == AbilityState::BACKGROUND) { abilityRecord->SaveAbilityState(saveData); @@ -1320,8 +1320,8 @@ void MissionListManager::CompleteForegroundSuccess(MissionAbilityRecordPtr abili // ability do not save window mode abilityRecord->RemoveWindowMode(); TAG_LOGD(AAFwkTag::ABILITYMGR, "ability: %{public}s/%{public}s", - abilityRecord->GetElementName().GetBundleName().c_str(), - abilityRecord->GetElementName().GetAbilityName().c_str()); + abilityRecord->GetBundleName().c_str(), + abilityRecord->GetAbilityName().c_str()); abilityRecord->SetAbilityState(AbilityState::FOREGROUND); AbilityStartWithWaitObserverManager::GetInstance().NotifyAATerminateWait(abilityRecord); @@ -1657,8 +1657,8 @@ int MissionListManager::TerminateAbilityInner(const std::shared_ptrGetElementName().GetBundleName().c_str(), - abilityRecord->GetElementName().GetAbilityName().c_str()); + abilityRecord->GetBundleName().c_str(), + abilityRecord->GetAbilityName().c_str()); if (abilityRecord->IsTerminating() && !abilityRecord->IsForeground()) { TAG_LOGE(AAFwkTag::ABILITYMGR, "ability terminating"); return ERR_OK; @@ -1693,8 +1693,8 @@ int MissionListManager::TerminateAbilityLocked(MissionAbilityRecordPtr abilityRe { CHECK_POINTER_AND_RETURN(abilityRecord, ERR_INVALID_VALUE); TAG_LOGD(AAFwkTag::ABILITYMGR, "terminate ability locked, ability is %{public}s/%{public}s.", - abilityRecord->GetElementName().GetBundleName().c_str(), - abilityRecord->GetElementName().GetAbilityName().c_str()); + abilityRecord->GetBundleName().c_str(), + abilityRecord->GetAbilityName().c_str()); // remove AbilityRecord out of list RemoveTerminatingAbility(abilityRecord, flag); abilityRecord->SendResultToCallers(); @@ -1760,8 +1760,8 @@ void MissionListManager::RemoveTerminatingAbility(MissionAbilityRecordPtr abilit { CHECK_POINTER(abilityRecord); TAG_LOGD(AAFwkTag::ABILITYMGR, "Remove terminating ability, ability is %{public}s/%{public}s.", - abilityRecord->GetElementName().GetBundleName().c_str(), - abilityRecord->GetElementName().GetAbilityName().c_str()); + abilityRecord->GetBundleName().c_str(), + abilityRecord->GetAbilityName().c_str()); if (GetAbilityFromTerminateListInner(abilityRecord->GetToken())) { abilityRecord->SetNextAbilityRecord(nullptr); TAG_LOGD(AAFwkTag::ABILITYMGR, "Find ability in terminating list, return."); @@ -2627,8 +2627,8 @@ void MissionListManager::OnAbilityDied(std::shared_ptr abilityRec return; } TAG_LOGD(AAFwkTag::ABILITYMGR, "OnAbilityDied come, ability is %{public}s/%{public}s", - abilityRecord->GetElementName().GetBundleName().c_str(), - abilityRecord->GetElementName().GetAbilityName().c_str()); + abilityRecord->GetBundleName().c_str(), + abilityRecord->GetAbilityName().c_str()); if (abilityRecord->GetAbilityInfo().type != AbilityType::PAGE) { TAG_LOGE(AAFwkTag::ABILITYMGR, "ability type not page"); return; @@ -3403,7 +3403,7 @@ int MissionListManager::CallAbilityLocked(const AbilityRequest &abilityRequest) // new version started by call type auto ret = ResolveAbility(targetAbilityRecord, abilityRequest); - bool isStartToForeground = targetAbilityRecord->GetWant().GetBoolParam(Want::PARAM_RESV_CALL_TO_FOREGROUND, false); + bool isStartToForeground = targetAbilityRecord->GetBoolParam(Want::PARAM_RESV_CALL_TO_FOREGROUND, false); if (ret == ResolveResultType::OK_HAS_REMOTE_OBJ || (ret == ResolveResultType::OK_NO_REMOTE_OBJ && targetAbilityRecord->GetStartTime() > 0)) { TAG_LOGD(AAFwkTag::ABILITYMGR, "target ability has been resolved."); @@ -3432,8 +3432,8 @@ int MissionListManager::CallAbilityLocked(const AbilityRequest &abilityRequest) // schedule target ability TAG_LOGD(AAFwkTag::ABILITYMGR, "load ability record: %{public}s/%{public}s", - targetAbilityRecord->GetElementName().GetBundleName().c_str(), - targetAbilityRecord->GetElementName().GetAbilityName().c_str()); + targetAbilityRecord->GetBundleName().c_str(), + targetAbilityRecord->GetAbilityName().c_str()); // flag the first ability. auto currentTopAbility = GetCurrentTopAbilityLocked(); diff --git a/services/abilitymgr/src/modular_object/modular_object_event_receiver.cpp b/services/abilitymgr/src/modular_object/modular_object_event_receiver.cpp index f5951f8ddf..02b24927af 100644 --- a/services/abilitymgr/src/modular_object/modular_object_event_receiver.cpp +++ b/services/abilitymgr/src/modular_object/modular_object_event_receiver.cpp @@ -122,7 +122,7 @@ void ModularObjectEventReceiver::HandleEventUserSwitched(const EventFwk::CommonE void ModularObjectEventReceiver::HandleBundleScanFinished(const EventFwk::CommonEventData &data) { - uint32_t userId = AppExecFwk::OsAccountManagerWrapper::GetCurrentActiveAccountId(); + int32_t userId = AppExecFwk::OsAccountManagerWrapper::GetCurrentActiveAccountId(); if (userId == 0) { TAG_LOGI(AAFwkTag::EXT, "use MAIN_USER_ID(%{public}d) instead of current userId: (%{public}d)", MAIN_USER_ID, userId); @@ -136,7 +136,7 @@ void ModularObjectEventReceiver::HandleBundleScanFinished(const EventFwk::Common void ModularObjectEventReceiver::HandleBundleInstall(const EventFwk::CommonEventData &data) { const AAFwk::Want& want = data.GetWant(); - std::string bundleName = want.GetElement().GetBundleName(); + std::string bundleName = want.GetBundle(); TAG_LOGI(AAFwkTag::EXT, "handle common event package add, bundleName: %{public}s", bundleName.c_str()); if (bundleName.empty()) { TAG_LOGE(AAFwkTag::EXT, "bundle name is empty"); @@ -157,7 +157,7 @@ void ModularObjectEventReceiver::HandleBundleInstall(const EventFwk::CommonEvent void ModularObjectEventReceiver::HandleBundleRemoved(const EventFwk::CommonEventData &data) { const AAFwk::Want& want = data.GetWant(); - std::string bundleName = want.GetElement().GetBundleName(); + std::string bundleName = want.GetBundle(); TAG_LOGI(AAFwkTag::EXT, "handle common event package remove, bundleName: %{public}s", bundleName.c_str()); if (bundleName.empty()) { TAG_LOGE(AAFwkTag::EXT, "bundle name is empty"); @@ -178,7 +178,7 @@ void ModularObjectEventReceiver::HandleBundleRemoved(const EventFwk::CommonEvent void ModularObjectEventReceiver::HandleBundleChanged(const EventFwk::CommonEventData &data) { const AAFwk::Want& want = data.GetWant(); - std::string bundleName = want.GetElement().GetBundleName(); + std::string bundleName = want.GetBundle(); TAG_LOGI(AAFwkTag::EXT, "handle common event package changed, bundleName: %{public}s", bundleName.c_str()); if (bundleName.empty()) { TAG_LOGE(AAFwkTag::EXT, "bundle name is empty"); diff --git a/services/abilitymgr/src/modular_object_utils.cpp b/services/abilitymgr/src/modular_object_utils.cpp index fe1dbc4e8e..e8340507b4 100644 --- a/services/abilitymgr/src/modular_object_utils.cpp +++ b/services/abilitymgr/src/modular_object_utils.cpp @@ -27,6 +27,7 @@ #include "bundle_mgr_helper.h" #include "hilog_tag_wrapper.h" #include "ipc_skeleton.h" +#include "modular_object_manager.h" #include "modular_object_rdb_storage_mgr.h" #include "os_account_manager_wrapper.h" #include "parameters.h" @@ -35,6 +36,11 @@ #include "running_process_info.h" #include "scene_board_judgement.h" +namespace { +constexpr int32_t MOE_MAX_CONNECTIONS_PER_CALLER = 5; +constexpr int32_t MOE_MAX_INSTANCES = 20; +} + using namespace OHOS::AppExecFwk; namespace OHOS { @@ -90,6 +96,10 @@ int32_t ModularObjectUtils::CheckPermission(const AbilityRequest &abilityRequest if (ret != ERR_OK) { return ret; } + ret = CheckInProcessLaunchMode(targetExtensionInfo.launchMode, abilityRequest.uid); + if (ret != ERR_OK) { + return ret; + } ret = CheckCallerForeground(); if (ret != ERR_OK) { return ret; @@ -118,6 +128,20 @@ int32_t ModularObjectUtils::CheckExtensionEnabled(const ModularObjectExtensionIn return ERR_OK; } +int32_t ModularObjectUtils::CheckInProcessLaunchMode(MoeLaunchMode launchMode, int32_t targetUid) +{ + if (launchMode != MoeLaunchMode::IN_PROCESS) { + return ERR_OK; + } + int32_t callingUid = IPCSkeleton::GetCallingUid(); + if (callingUid != targetUid) { + TAG_LOGE(AAFwkTag::EXT, "IN_PROCESS not support cross-app, callerUid: %{public}d, targetUid: %{public}d", + callingUid, targetUid); + return ERR_MOE_CROSS_APP_IN_PROCESS; + } + return ERR_OK; +} + int32_t ModularObjectUtils::CheckCallerForeground() { pid_t callingPid = IPCSkeleton::GetCallingPid(); @@ -287,5 +311,101 @@ bool ModularObjectUtils::GetPidToCheckByCallerToken(sptr callerTo } return false; } + +std::shared_ptr ModularObjectUtils::QueryConfig(const AbilityRequest &abilityRequest) +{ + auto mgr = DelayedSingleton::GetInstance(); + if (mgr == nullptr) { + TAG_LOGE(AAFwkTag::EXT, "ModularObjectManager is null"); + return nullptr; + } + std::vector infos; + int32_t userId = abilityRequest.userId; + const auto &bundleName = abilityRequest.abilityInfo.bundleName; + int32_t appIndex = abilityRequest.appInfo.appIndex; + if (mgr->QuerySelfModularObjectExtensionInfos(userId, bundleName, appIndex, infos) != ERR_OK) { + TAG_LOGD(AAFwkTag::EXT, "query modular object infos failed"); + return nullptr; + } + const auto &abilityName = abilityRequest.abilityInfo.name; + for (const auto &info : infos) { + if (info.abilityName == abilityName) { + return std::make_shared(info); + } + } + return nullptr; +} + +int32_t ModularObjectUtils::SetupNewRecord(const AbilityRequest &abilityRequest, + std::shared_ptr &targetService, const std::string &serviceKey) +{ + if (targetService == nullptr) { + TAG_LOGE(AAFwkTag::EXT, "targetService is null"); + return ERR_INVALID_VALUE; + } + auto config = QueryConfig(abilityRequest); + if (config == nullptr) { + return ERR_INVALID_VALUE; + } + // Determine processName + std::string process; + if (config->launchMode == MoeLaunchMode::IN_PROCESS) { + pid_t callingPid = IPCSkeleton::GetCallingPid(); + AppExecFwk::RunningProcessInfo processInfo; + auto procRet = IN_PROCESS_CALL( + DelayedSingleton::GetInstance()->GetRunningProcessInfoByPid( + callingPid, processInfo)); + if (procRet != ERR_OK || processInfo.processName_.empty()) { + TAG_LOGE(AAFwkTag::EXT, "GetRunningProcessInfoByPid failed in IN_PROCESS mode, ret:%{public}d", procRet); + return INNER_ERR; + } + process = processInfo.processName_; + } else { + switch (config->processMode) { + case MoeProcessMode::BUNDLE: + process = abilityRequest.abilityInfo.bundleName + ":" + + abilityRequest.abilityInfo.extensionTypeName; + break; + case MoeProcessMode::TYPE: + process = abilityRequest.abilityInfo.bundleName + ":" + + abilityRequest.abilityInfo.name; + break; + case MoeProcessMode::INSTANCE: + process = abilityRequest.abilityInfo.bundleName + ":" + + abilityRequest.abilityInfo.name + ":" + + std::to_string(targetService->GetRecordId()); + break; + default: + break; + } + int32_t appCloneIndex = abilityRequest.appInfo.appIndex; + if (appCloneIndex > 0) { + process = process + ":" + std::to_string(appCloneIndex); + } + } + if (!process.empty()) { + targetService->SetProcessName(process); + TAG_LOGI(AAFwkTag::EXT, "ModularObject processName: %{public}s", process.c_str()); + } + // Save requestId for disconnect serviceKey reconstruction + auto pos = serviceKey.rfind('_'); + if (pos != std::string::npos) { + targetService->SetRequestId(serviceKey.substr(pos + 1)); + } + return ERR_OK; +} + +int32_t ModularObjectUtils::CheckLimits(int32_t instanceCount, int32_t connectionCount) +{ + if (instanceCount >= MOE_MAX_INSTANCES) { + TAG_LOGE(AAFwkTag::EXT, "MoeAbility instance limit reached, count: %{public}d", instanceCount); + return ERR_MOE_INSTANCE_LIMIT; + } + if (connectionCount >= MOE_MAX_CONNECTIONS_PER_CALLER) { + TAG_LOGE(AAFwkTag::EXT, "MoeAbility connection limit reached, count: %{public}d", connectionCount); + return ERR_MOE_CONNECTION_LIMIT; + } + return ERR_OK; +} } // namespace AAFwk } // namespace OHOS diff --git a/services/abilitymgr/src/preload_manager_service.cpp b/services/abilitymgr/src/preload_manager_service.cpp index bf7f5dd44d..8b96cc3c01 100644 --- a/services/abilitymgr/src/preload_manager_service.cpp +++ b/services/abilitymgr/src/preload_manager_service.cpp @@ -104,9 +104,6 @@ int32_t PreloadManagerService::LaunchGameCustomized(const std::string &bundleNam return CHECK_PERMISSION_FAILED; } - // Set game prelaunch flag - launchWant.SetParam(AbilityRuntime::GlobalConstant::GAME_PRELAUNCH, true); - StartAbilityWrapParam startAbilityWrapParam; startAbilityWrapParam.want = launchWant; startAbilityWrapParam.userId = userId; diff --git a/services/abilitymgr/src/remote_intent_result_callback.cpp b/services/abilitymgr/src/remote_intent_result_callback.cpp index 3a940e2cf4..060fb988a6 100644 --- a/services/abilitymgr/src/remote_intent_result_callback.cpp +++ b/services/abilitymgr/src/remote_intent_result_callback.cpp @@ -26,12 +26,27 @@ void RemoteIntentResultCallback::OnIntentResult(uint64_t requestCode, int32_t re { TAG_LOGI(AAFwkTag::ABILITYMGR, "OnIntentResult requestCode=%{public}" PRIu64 ", resultCode=%{public}d", requestCode, resultCode); + if (resultMsg.empty()) { + AppExecFwk::InsightIntentExecuteResult errorResult{}; + errorResult.innerErr = AbilityRuntime::InsightIntentInnerErr::INSIGHT_INTENT_EXECUTE_REPLY_FAILED; + DelayedSingleton::GetInstance()->ExecuteIntentDone( + requestCode, resultCode, errorResult); + } else { + AppExecFwk::InsightIntentExecuteResult result; + result.FromJsonString(resultMsg); + DelayedSingleton::GetInstance()->ExecuteIntentDone( + requestCode, resultCode, result); + } } void RemoteIntentResultCallback::OnLinkDisconnected(uint64_t requestCode, int32_t reason) { TAG_LOGI(AAFwkTag::ABILITYMGR, "OnLinkDisconnected requestCode=%{public}" PRIu64 ", reason=%{public}d", requestCode, reason); + AppExecFwk::InsightIntentExecuteResult errorResult{}; + errorResult.innerErr = AbilityRuntime::InsightIntentInnerErr::INSIGHT_INTENT_EXECUTE_REPLY_FAILED; + DelayedSingleton::GetInstance()->ExecuteIntentDone( + requestCode, ERR_INTENT_DEVICE_DISCONNECTED, errorResult); } } // namespace AAFwk } // namespace OHOS diff --git a/services/abilitymgr/src/scene_board/ui_ability_lifecycle_manager.cpp b/services/abilitymgr/src/scene_board/ui_ability_lifecycle_manager.cpp index 9c2e054c34..e4ef474e2a 100644 --- a/services/abilitymgr/src/scene_board/ui_ability_lifecycle_manager.cpp +++ b/services/abilitymgr/src/scene_board/ui_ability_lifecycle_manager.cpp @@ -85,6 +85,7 @@ constexpr const char* IS_CALLING_FROM_DMS = "supportCollaborativeCallingFromDmsI constexpr int REMOVE_STARTING_BUNDLE_TIMEOUT_MICRO_SECONDS = 5000000; // 5s constexpr int32_t BY_CALL_TIMEOUT = 10 * 1000 * 1000; // 10s constexpr int32_t START_SELF_TIMEOUT = 10 * 1000 * 1000; // 10s +constexpr int32_t START_SELF_TIMEOUT_KILL_DELAY = 3 * 1000 * 1000; // 3s constexpr int32_t SCENE_FLAG_BYCALL = 4; auto g_deleteLifecycleEventTask = [](const sptr &token) { @@ -257,10 +258,8 @@ int UIAbilityLifecycleManager::StartUIAbility(AbilityRequest &abilityRequest, sp return ERR_OK; } TAG_LOGD(AAFwkTag::ABILITYMGR, "StartUIAbility, specifyTokenId is %{public}u.", abilityRequest.specifyTokenId); - auto want = uiAbilityRecord->GetWant(); - if (want.GetBoolParam(IS_CALLING_FROM_DMS, false) && !(sessionInfo->isNewWant)) { - want.RemoveParam(IS_CALLING_FROM_DMS); - uiAbilityRecord->SetWant(want); + if (uiAbilityRecord->GetBoolParam(IS_CALLING_FROM_DMS, false) && !(sessionInfo->isNewWant)) { + uiAbilityRecord->RemoveSpecifiedWantParam(IS_CALLING_FROM_DMS); } uiAbilityRecord->SetSpecifyTokenId(abilityRequest.specifyTokenId); UpdateAbilityRecordLaunchReason(abilityRequest, uiAbilityRecord); @@ -589,7 +588,7 @@ int UIAbilityLifecycleManager::AttachAbilityThread(const sptr } if (abilityRecord->IsStartedByCall()) { (void)abilityRecord->PromotePriority(); - if (abilityRecord->GetWant().GetBoolParam(Want::PARAM_RESV_CALL_TO_FOREGROUND, false)) { + if (abilityRecord->GetBoolParam(Want::PARAM_RESV_CALL_TO_FOREGROUND, false)) { abilityRecord->SetStartToForeground(true); abilityRecord->PostForegroundTimeoutTask(); abilityRecord->SetAbilityState(AbilityState::FOREGROUNDING); @@ -620,10 +619,10 @@ void UIAbilityLifecycleManager::OnAbilityRequestDone(const sptr & auto abilityRecord = GetAbilityRecordByToken(token); CHECK_POINTER(abilityRecord); TAG_LOGI(AAFwkTag::ABILITYMGR, "Ability is %{public}s/%{public}s, start to foreground.", - abilityRecord->GetElementName().GetBundleName().c_str(), - abilityRecord->GetElementName().GetAbilityName().c_str()); - bool hasLastWant = abilityRecord->IsLastWantBackgroundDriven(); - abilityRecord->ForegroundAbility(abilityRecord->lifeCycleStateInfo_.sceneFlagBak, hasLastWant); + abilityRecord->GetInfoBundleName().c_str(), + abilityRecord->GetInfoAbilityName().c_str()); + abilityRecord->UpdateWantByLastWant(); + abilityRecord->ForegroundAbility(abilityRecord->lifeCycleStateInfo_.sceneFlagBak); } } @@ -647,8 +646,8 @@ int UIAbilityLifecycleManager::AbilityTransactionDone(const sptr } abilityRecord->RemoveSignatureInfo(); TAG_LOGD(AAFwkTag::ABILITYMGR, "ability: %{public}s/%{public}s, state: %{public}s", - abilityRecord->GetElementName().GetBundleName().c_str(), - abilityRecord->GetElementName().GetAbilityName().c_str(), abilityState.c_str()); + abilityRecord->GetInfoBundleName().c_str(), + abilityRecord->GetInfoAbilityName().c_str(), abilityState.c_str()); if (targetState == AbilityState::BACKGROUND) { abilityRecord->SaveAbilityState(saveData); @@ -1086,15 +1085,7 @@ int UIAbilityLifecycleManager::DispatchForeground(const UIAbilityRecordPtr &abil if (abilityRecord->GetNativeState() == AbilityNativeState::ATTACHED) { TAG_LOGI(AAFwkTag::ABILITYMGR, "NativeModule foreground is pending"); abilityRecord->SetNativeState(AbilityNativeState::CREATED); - auto timeoutTask = [wThis = weak_from_this(), abilityRecord]() { - auto pThis = wThis.lock(); - if (pThis != nullptr && abilityRecord->GetNativeState() == AbilityNativeState::CREATED) { - TAG_LOGW(AAFwkTag::ABILITYMGR, "Start self Timeout"); - std::lock_guard guard(pThis->sessionLock_); - pThis->HandleForegroundTimeout(abilityRecord); - } - }; - ffrt::submit(std::move(timeoutTask), ffrt::task_attr().delay(START_SELF_TIMEOUT)); + PostStartSelfTimeoutEvent(abilityRecord); return ERR_OK; } if (abilityRecord->GetNativeState() == AbilityNativeState::ON_FOREGROUND) { @@ -1232,9 +1223,8 @@ void UIAbilityLifecycleManager::CompleteForegroundSuccess(const UIAbilityRecordP CHECK_POINTER(abilityRecord); // ability do not save window mode abilityRecord->RemoveWindowMode(); - TAG_LOGD(AAFwkTag::ABILITYMGR, "ability: %{public}s/%{public}s", - abilityRecord->GetElementName().GetBundleName().c_str(), - abilityRecord->GetElementName().GetAbilityName().c_str()); + TAG_LOGD(AAFwkTag::ABILITYMGR, "ability: %{public}s/%{public}s", abilityRecord->GetInfoBundleName().c_str(), + abilityRecord->GetInfoAbilityName().c_str()); abilityRecord->SetAbilityState(AbilityState::FOREGROUND); abilityRecord->UpdateAbilityVisibilityState(); AbilityStartWithWaitObserverManager::GetInstance().NotifyAATerminateWait(abilityRecord); @@ -1247,9 +1237,8 @@ void UIAbilityLifecycleManager::CompleteForegroundSuccess(const UIAbilityRecordP } if (abilityRecord->IsGameSAPreLaunch()) { TAG_LOGI(AAFwkTag::ABILITYMGR, "Game SA prelaunch detected, schedule NotifyCompleteGamePreLaunch task"); - auto self(weak_from_this()); std::weak_ptr weakAbilityRecord(abilityRecord); - auto task = [self, weakAbilityRecord]() { + auto task = [self = weak_from_this(), weakAbilityRecord]() { auto selfObj = self.lock(); auto abilityRecordObj = weakAbilityRecord.lock(); if (selfObj == nullptr || abilityRecordObj == nullptr) { @@ -1264,9 +1253,10 @@ void UIAbilityLifecycleManager::CompleteForegroundSuccess(const UIAbilityRecordP ffrt::submit(task, ffrt::task_attr().delay(gamePreLaunchCompleteTime_)); } - if (abilityRecord->HasLastWant()) { + abilityRecord->SetShouldUpdateWant(true); + if (abilityRecord->UpdateWantByLastWant()) { TAG_LOGD(AAFwkTag::ABILITYMGR, "has last want"); - abilityRecord->ForegroundAbility(0, true); + abilityRecord->ForegroundAbility(0); } else if (abilityRecord->GetPendingState() == AbilityState::BACKGROUND) { if (abilityRecord->GetPrelaunchFlag()) { HandlePrelaunchBackground(abilityRecord); @@ -1603,6 +1593,11 @@ int32_t UIAbilityLifecycleManager::StartSelf(const UIAbilityRecordPtr &abilityRe return ERR_INVALID_VALUE; } + if (abilityRecord->GetNativeState() == AbilityNativeState::ATTACHED) { + TAG_LOGW(AAFwkTag::ABILITYMGR, "state error"); + return ERR_UI_ABILITY_IS_STARTING; + } + if (abilityRecord->GetNativeState() == AbilityNativeState::NONE) { TAG_LOGW(AAFwkTag::ABILITYMGR, "not a NativeModule ability"); return ERR_CAPABILITY_NOT_SUPPORT; @@ -1892,8 +1887,8 @@ void UIAbilityLifecycleManager::CallUIAbilityBySCB(const sptr &sess uiAbilityRecord->lifeCycleStateInfo_.sceneFlagBak = params.sceneFlag; uiAbilityRecord->SetInstanceKey(sessionInfo->instanceKey); - MoreAbilityNumbersSendEventInfo(sessionInfo->userId, sessionInfo->want.GetElement().GetBundleName(), - sessionInfo->want.GetElement().GetAbilityName(), sessionInfo->want.GetElement().GetModuleName()); + MoreAbilityNumbersSendEventInfo(sessionInfo->userId, sessionInfo->want.GetBundle(), + sessionInfo->want.GetElement().GetAbilityName(), sessionInfo->want.GetModuleName()); sessionAbilityMap_.emplace(sessionInfo->persistentId, uiAbilityRecord); uiAbilityRecord->SetSessionInfo(sessionInfo); @@ -2015,7 +2010,6 @@ int UIAbilityLifecycleManager::NotifySCBPendingActivation(sptr &ses TAG_LOGD(AAFwkTag::ABILITYMGR, "callback request ability"); abilityRequest.requestCallback->OnRequestStartAbilityResult(true); } - const_cast(abilityRequest).want.RemoveParam(KEY_REQUEST_ID); TAG_LOGI(AAFwkTag::ABILITYMGR, "scb call, NotifySCBPendingActivation for callerSession, target: %{public}s" "requestId:%{public}s, splitRatio:%{public}d, windowMode:%{public}d", sessionInfo->want.GetElement().GetAbilityName().c_str(), requestId.c_str(), @@ -2040,7 +2034,6 @@ int UIAbilityLifecycleManager::NotifySCBPendingActivation(sptr &ses TAG_LOGI(AAFwkTag::ABILITYMGR, "notify request success, requestId:%{public}s", requestId.c_str()); callerRecord->NotifyAbilityRequestSuccess(requestId, abilityRequest.want.GetElement()); } - const_cast(abilityRequest).want.RemoveParam(KEY_REQUEST_ID); } if (abilityRequest.requestCallback != nullptr) { TAG_LOGD(AAFwkTag::ABILITYMGR, "callback request ability"); @@ -2209,6 +2202,10 @@ bool UIAbilityLifecycleManager::GetContentAndTypeId(uint32_t msgId, std::string case AbilityManagerService::TERMINATE_TIMEOUT_MSG: msgContent += "terminate timeout."; break; + case AbilityManagerService::START_SELF_TIMEOUT_MSG: + msgContent += "startSelf timeout."; + typeId = AppExecFwk::AppfreezeManager::TypeAttribute::CRITICAL_TIMEOUT; + break; default: return false; } @@ -2233,7 +2230,7 @@ void UIAbilityLifecycleManager::CompleteBackground(const UIAbilityRecordPtr &abi if (abilityRecord->GetPendingState() == AbilityState::FOREGROUND) { abilityRecord->PostForegroundTimeoutTask(); abilityRecord->SetAbilityState(AbilityState::FOREGROUNDING); - abilityRecord->SetBackgroundDrivenFlag(abilityRecord->HasLastWant()); + abilityRecord->SetShouldUpdateWant(abilityRecord->HasLastWant()); DelayedSingleton::GetInstance()->MoveToForeground(abilityRecord->GetToken()); } else if (abilityRecord->GetPendingState() == AbilityState::BACKGROUND) { TAG_LOGD(AAFwkTag::ABILITYMGR, "not continuous startup."); @@ -2357,8 +2354,8 @@ int UIAbilityLifecycleManager::CloseUIAbility(const UIAbilityRecordPtr &abilityR std::lock_guard guard(sessionLock_); CHECK_POINTER_AND_RETURN(abilityRecord, ERR_UI_ABILITY_MANAGER_NULL_ABILITY_RECORD); TAG_LOGI(AAFwkTag::ABILITYMGR, "CloseUIAbility call: %{public}s/%{public}s", - abilityRecord->GetElementName().GetBundleName().c_str(), - abilityRecord->GetElementName().GetAbilityName().c_str()); + abilityRecord->GetInfoBundleName().c_str(), + abilityRecord->GetInfoAbilityName().c_str()); if (abilityRecord->IsTerminating() && !abilityRecord->IsForeground()) { TAG_LOGI(AAFwkTag::ABILITYMGR, "ability on terminating"); return ERR_OK; @@ -2732,6 +2729,42 @@ void UIAbilityLifecycleManager::HandleForegroundTimeout(const UIAbilityRecordPtr DelayedSingleton::GetInstance()->AttachTimeOut(abilityRecord->GetToken()); } +void UIAbilityLifecycleManager::HandleStartSelfTimeout(const UIAbilityRecordPtr &abilityRecord, bool isHalf) +{ + if (abilityRecord == nullptr || abilityRecord->GetNativeState() != AbilityNativeState::CREATED) { + return; + } + TAG_LOGW(AAFwkTag::ABILITYMGR, "Start self Timeout"); + OnTimeOut(AbilityManagerService::START_SELF_TIMEOUT_MSG, abilityRecord->GetRecordId(), isHalf); + if (isHalf) { + return; + } + std::lock_guard guard(sessionLock_); + HandleForegroundTimeout(abilityRecord); + auto pid = abilityRecord->GetPid(); + auto killTask = [pid]() { + TAG_LOGI(AAFwkTag::ABILITYMGR, "Kill process %{public}d for StartSelfTimeout", pid); + auto appMgr = AppMgrUtil::GetAppMgr(); + if (appMgr != nullptr) { + appMgr->KillProcessByPidForExit(pid, "StartSelfTimeout"); + } + }; + ffrt::submit(std::move(killTask), ffrt::task_attr().delay(START_SELF_TIMEOUT_KILL_DELAY)); +} + +void UIAbilityLifecycleManager::PostStartSelfTimeoutEvent(const UIAbilityRecordPtr &abilityRecord) +{ + auto halfTimeout = START_SELF_TIMEOUT / 2; + auto halfTimeoutTask = [pThis = shared_from_this(), abilityRecord]() { + pThis->HandleStartSelfTimeout(abilityRecord, true); + }; + ffrt::submit(std::move(halfTimeoutTask), ffrt::task_attr().delay(halfTimeout)); + auto timeoutTask = [pThis = shared_from_this(), abilityRecord]() { + pThis->HandleStartSelfTimeout(abilityRecord, false); + }; + ffrt::submit(std::move(timeoutTask), ffrt::task_attr().delay(START_SELF_TIMEOUT)); +} + void UIAbilityLifecycleManager::OnAbilityDied(UIAbilityRecordPtr abilityRecord) { TAG_LOGD(AAFwkTag::ABILITYMGR, "call OnAbilityDied"); @@ -2829,7 +2862,7 @@ void UIAbilityLifecycleManager::HandleLegacyAcceptWantDone(SpecifiedRequest &spe const std::string &flag, const AAFwk::Want &want) { TAG_LOGI(AAFwkTag::ABILITYMGR, "HandleLegacyAcceptWantDone, ability:%{public}s/%{public}s", - want.GetElement().GetBundleName().c_str(), want.GetElement().GetAbilityName().c_str()); + want.GetBundle().c_str(), want.GetElement().GetAbilityName().c_str()); auto &abilityRequest = specifiedRequest.abilityRequest; auto callerAbility = GetAbilityRecordByToken(abilityRequest.callerToken); abilityRequest.specifiedFlag = flag; @@ -4324,14 +4357,14 @@ int32_t UIAbilityLifecycleManager::CleanUIAbility( HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__); if (DelayedSingleton::GetInstance()->CleanAbilityByUserRequest(abilityRecord->GetToken())) { TAG_LOGI(AAFwkTag::ABILITYMGR, "user clean ability: %{public}s/%{public}s success", - abilityRecord->GetElementName().GetBundleName().c_str(), - abilityRecord->GetElementName().GetAbilityName().c_str()); + abilityRecord->GetInfoBundleName().c_str(), + abilityRecord->GetInfoAbilityName().c_str()); return ERR_OK; } TAG_LOGI(AAFwkTag::ABILITYMGR, "can not force kill when user request clean ability, schedule lifecycle:%{public}s/%{public}s", - abilityRecord->GetElementName().GetBundleName().c_str(), - abilityRecord->GetElementName().GetAbilityName().c_str()); + abilityRecord->GetInfoBundleName().c_str(), + abilityRecord->GetInfoAbilityName().c_str()); return CloseUIAbility(abilityRecord, -1, nullptr, true, false); } @@ -4388,7 +4421,7 @@ void UIAbilityLifecycleManager::AddSpecifiedRequest(std::shared_ptrabilityRequest; TAG_LOGI(AAFwkTag::ABILITYMGR, "AddSpecifiedRequest: %{public}d, %{public}s/%{public}s", request->requestId, - abilityRequest.want.GetElement().GetBundleName().c_str(), + abilityRequest.want.GetBundle().c_str(), abilityRequest.want.GetElement().GetAbilityName().c_str()); auto instanceKey = abilityRequest.want.GetStringParam(Want::APP_INSTANCE_KEY); auto accessTokenIdStr = std::to_string(abilityRequest.abilityInfo.applicationInfo.accessTokenId); @@ -4723,8 +4756,8 @@ int32_t UIAbilityLifecycleManager::NotifyStartupExceptionBySCB(int32_t requestId auto abilityRecord = it->second; if (abilityRecord != nullptr) { TAG_LOGW(AAFwkTag::ABILITYMGR, "startup exception: %{public}s/%{public}s", - abilityRecord->GetElementName().GetBundleName().c_str(), - abilityRecord->GetElementName().GetAbilityName().c_str()); + abilityRecord->GetInfoBundleName().c_str(), + abilityRecord->GetInfoAbilityName().c_str()); SendAbilityEvent(abilityRecord->GetAbilityInfo(), reason); } tmpAbilityMap_.erase(it); @@ -4758,7 +4791,7 @@ ErrCode UIAbilityLifecycleManager::IsUIAbilityAlreadyExist(const Want &want, std::lock_guard guard(sessionLock_); tempSessionAbilityMap = sessionAbilityMap_; } - std::string moduleName = want.GetElement().GetModuleName(); + std::string moduleName = want.GetModuleName(); std::string abilityName = want.GetElement().GetAbilityName(); for (auto it = tempSessionAbilityMap.begin(); it != tempSessionAbilityMap.end(); it++) { @@ -4782,6 +4815,30 @@ ErrCode UIAbilityLifecycleManager::IsUIAbilityAlreadyExist(const Want &want, return ERR_OK; } +ErrCode UIAbilityLifecycleManager::IsSpecifiedUIAbilityAlreadyExist(const Want &want, + const std::string &specifiedFlag, int32_t appIndex, const std::string &instanceKey) +{ + std::lock_guard guard(sessionLock_); + std::string bundleName = want.GetElement().GetBundleName(); + std::string moduleName = want.GetElement().GetModuleName(); + std::string abilityName = want.GetElement().GetAbilityName(); + + for (auto it = sessionAbilityMap_.begin(); it != sessionAbilityMap_.end(); it++) { + if (it->second == nullptr) { + continue; + } + if (it->second->GetSpecifiedFlag() == specifiedFlag && + it->second->GetAbilityInfo().name == abilityName && + it->second->GetAbilityInfo().bundleName == bundleName && + (moduleName.empty() || it->second->GetAbilityInfo().moduleName == moduleName) && + it->second->GetAppIndex() == appIndex && it->second->GetInstanceKey() == instanceKey) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "specifiedFlag already exists"); + return ERROR_UIABILITY_IS_ALREADY_EXIST; + } + } + return ERR_OK; +} + void UIAbilityLifecycleManager::SendAbilityEvent(const AppExecFwk::AbilityInfo &abilityInfo, const std::string &reason) const { diff --git a/services/abilitymgr/src/scene_board/ui_ability_record.cpp b/services/abilitymgr/src/scene_board/ui_ability_record.cpp index fac0284a43..48551e3ab2 100644 --- a/services/abilitymgr/src/scene_board/ui_ability_record.cpp +++ b/services/abilitymgr/src/scene_board/ui_ability_record.cpp @@ -16,6 +16,8 @@ #include "ui_ability_record.h" #include "ability_util.h" +#include "global_constant.h" +#include "hilog_tag_wrapper.h" #include "native_ability_util.h" namespace OHOS { @@ -39,6 +41,10 @@ std::shared_ptr UIAbilityRecord::CreateAbilityRecord(const Abil abilityRecord->abilityNativeState_ = AbilityNativeState::NORMAL; } } + if (abilityRecord->want_.GetBoolParam(AbilityRuntime::GlobalConstant::GAME_PRELAUNCH, false)) { + TAG_LOGI(AAFwkTag::UIABILITY, "abilityRecord: Set game prelaunch flag from want"); + abilityRecord->SetGameSAPreLaunch(true); + } return abilityRecord; } @@ -60,5 +66,25 @@ void UIAbilityRecord::AttachNative() SetNativeState(AbilityNativeState::ATTACHED); } } + +bool UIAbilityRecord::UpdateWantByLastWant() +{ + if (!ShouldUpdateWant()) { + return false; + } + SetShouldUpdateWant(false); + std::shared_ptr lastWant; + { + std::lock_guard lock(wantLock_); + if (lastWant_ == nullptr) { + return false; + } + lastWant = lastWant_; + lastWant_ = nullptr; + } + SetWant(*lastWant); + SetIsNewWant(true); + return true; +} } // namespace AAFwk } // namespace OHOS \ No newline at end of file diff --git a/services/abilitymgr/src/skill/skill_execute_callback_proxy.cpp b/services/abilitymgr/src/skill/skill_execute_callback_proxy.cpp new file mode 100644 index 0000000000..74af10db68 --- /dev/null +++ b/services/abilitymgr/src/skill/skill_execute_callback_proxy.cpp @@ -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. + */ + +#include "skill/skill_execute_callback_proxy.h" +#include "hilog_tag_wrapper.h" + +namespace OHOS { +namespace AAFwk { + +void SkillExecuteCallbackProxy::OnExecuteDone(const std::string &requestCode, int32_t resultCode, + const AppExecFwk::SkillExecuteResult &result) +{ + TAG_LOGD(AAFwkTag::ABILITYMGR, + "skill execute callback proxy, requestCode:%{public}s code:%{public}d", + requestCode.c_str(), resultCode); + MessageParcel data; + if (!data.WriteInterfaceToken(ISkillExecuteCallback::GetDescriptor())) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "write interface token failed"); + return; + } + if (!data.WriteString(requestCode)) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "write requestCode failed"); + return; + } + if (!data.WriteInt32(resultCode)) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "write resultCode failed"); + return; + } + if (!data.WriteParcelable(&result)) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "write result failed"); + return; + } + sptr remote = Remote(); + if (remote == nullptr) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "null remote"); + return; + } + MessageParcel reply; + MessageOption option(MessageOption::TF_ASYNC); + int32_t error = remote->SendRequest(ON_SKILL_EXECUTE_DONE, data, reply, option); + if (error != ERR_OK) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "SendRequest fail, error:%{public}d", error); + } +} +} // namespace AAFwk +} // namespace OHOS diff --git a/services/abilitymgr/src/skill/skill_execute_callback_stub.cpp b/services/abilitymgr/src/skill/skill_execute_callback_stub.cpp new file mode 100644 index 0000000000..d2d71f3162 --- /dev/null +++ b/services/abilitymgr/src/skill/skill_execute_callback_stub.cpp @@ -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. + */ + +#include "skill/skill_execute_callback_stub.h" +#include "hilog_tag_wrapper.h" + +namespace OHOS { +namespace AAFwk { + +SkillExecuteCallbackStub::SkillExecuteCallbackStub() {} + +SkillExecuteCallbackStub::~SkillExecuteCallbackStub() {} + +int32_t SkillExecuteCallbackStub::OnRemoteRequest( + uint32_t code, MessageParcel &data, MessageParcel &reply, MessageOption &option) +{ + if (data.ReadInterfaceToken() != ISkillExecuteCallback::GetDescriptor()) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "interface token not match"); + return ERR_INVALID_STATE; + } + + if (code == ON_SKILL_EXECUTE_DONE) { + return OnExecuteDoneInner(data, reply); + } + return IPCObjectStub::OnRemoteRequest(code, data, reply, option); +} + +int32_t SkillExecuteCallbackStub::OnExecuteDoneInner(MessageParcel &data, MessageParcel &reply) +{ + std::string requestCode = data.ReadString(); + int32_t resultCode = data.ReadInt32(); + auto *result = data.ReadParcelable(); + if (result == nullptr) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "null result"); + return ERR_INVALID_VALUE; + } + OnExecuteDone(requestCode, resultCode, *result); + delete result; + return ERR_OK; +} +} // namespace AAFwk +} // namespace OHOS diff --git a/services/abilitymgr/src/skill/skill_execute_manager.cpp b/services/abilitymgr/src/skill/skill_execute_manager.cpp new file mode 100644 index 0000000000..047769210b --- /dev/null +++ b/services/abilitymgr/src/skill/skill_execute_manager.cpp @@ -0,0 +1,377 @@ +/* + * 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 "skill_execute_manager.h" + +#include + +#include "ability_event_handler.h" +#include "ability_manager_errors.h" +#include "ability_manager_service.h" +#include "bundle_mgr_helper.h" +#include "hilog_tag_wrapper.h" +#include "in_process_call_wrapper.h" +#include "iservice_registry.h" +#include "permission_verification.h" +#include "system_ability_definition.h" +#ifdef SUPPORT_UPMS +#include "uri_permission_manager_client.h" +#endif + +namespace OHOS { +namespace AAFwk { + +SkillExecuteManager::SkillExecuteManager() {} + +SkillExecuteManager::~SkillExecuteManager() {} + +sptr SkillExecuteManager::GetSkillManagerProxy() +{ + auto samgr = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager(); + if (samgr == nullptr) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "failed to get SystemAbilityManager"); + return nullptr; + } + auto bmsObj = samgr->GetSystemAbility(BUNDLE_MGR_SERVICE_SYS_ABILITY_ID); + if (bmsObj == nullptr) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "failed to get BMS from samgr"); + return nullptr; + } + auto bundleMgr = iface_cast(bmsObj); + if (bundleMgr == nullptr) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "failed to cast IBundleMgr"); + return nullptr; + } + return bundleMgr->GetSkillManagerProxy(); +} + +int32_t SkillExecuteManager::QuerySkillInfo(const std::string &bundleName, const std::string &moduleName, + const std::string &skillName, int32_t userId, AppExecFwk::SkillInfo &skillInfo) +{ + TAG_LOGD(AAFwkTag::ABILITYMGR, + "query skill info, bundle:%{public}s module:%{public}s skill:%{public}s", + bundleName.c_str(), moduleName.c_str(), skillName.c_str()); + + auto skillMgr = GetSkillManagerProxy(); + if (skillMgr == nullptr) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "failed to get SkillManager proxy"); + return ERR_INVALID_VALUE; + } + + uint32_t flags = static_cast(AppExecFwk::SkillInfoFlag::GET_SKILL_INFO_WITH_SRC_ENTRIES) | + static_cast(AppExecFwk::SkillInfoFlag::GET_SKILL_INFO_WITH_PERMISSIONS) | + static_cast(AppExecFwk::SkillInfoFlag::GET_SKILL_INFO_WITH_REQUEST_PERMISSIONS); + + auto ret = skillMgr->GetSkillInfo(bundleName, moduleName, skillName, flags, userId, skillInfo); + if (ret != ERR_OK) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "GetSkillInfo failed, ret:%{public}d", ret); + return ret; + } + return ERR_OK; +} + +int32_t SkillExecuteManager::CheckSkillPermission(const AppExecFwk::SkillInfo &skillInfo) +{ + TAG_LOGD(AAFwkTag::ABILITYMGR, "check skill permission for skill:%{public}s", + skillInfo.skillName.c_str()); + + auto permVerif = PermissionVerification::GetInstance(); + if (!permVerif->JudgeCallerIsAllowedToUseSystemAPI()) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "caller not allowed to use system API"); + return ERR_NOT_SYSTEM_APP; + } + + for (const auto &perm : skillInfo.permissions) { + if (!permVerif->VerifyCallingPermission(perm)) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "caller lacks permission:%{public}s", perm.c_str()); + return CHECK_PERMISSION_FAILED; + } + } + return ERR_OK; +} + +int32_t SkillExecuteManager::GenerateSkillWant(const AppExecFwk::SkillInfo &skillInfo, Want &want, + int32_t userId, const std::string &requestCode, AppExecFwk::ExtensionAbilityType &targetType, + const std::string &scriptPath, const std::string &functionName, + const std::shared_ptr &skillArgs) +{ + std::string abilityName = skillInfo.abilityName; + if (abilityName.empty()) { + abilityName = ResolveDefaultAbilityName(skillInfo.bundleName, skillInfo.moduleName, userId); + if (abilityName.empty()) { + TAG_LOGE(AAFwkTag::ABILITYMGR, + "no abilityName in skillProfile and no default ability found for bundle:%{public}s module:%{public}s", + skillInfo.bundleName.c_str(), skillInfo.moduleName.c_str()); + return ERR_INVALID_VALUE; + } + TAG_LOGD(AAFwkTag::ABILITYMGR, + "abilityName not specified, resolved default ability:%{public}s", abilityName.c_str()); + } + + targetType = ResolveTargetType(skillInfo.bundleName, skillInfo.moduleName, abilityName, userId); + TAG_LOGD(AAFwkTag::ABILITYMGR, + "generate skill want, bundle:%{public}s ability:%{public}s type:%{public}d", + skillInfo.bundleName.c_str(), abilityName.c_str(), static_cast(targetType)); + + want.SetElementName("", skillInfo.bundleName, abilityName, skillInfo.moduleName); + AppExecFwk::SkillExecuteParam::WriteToWant(want, skillInfo.bundleName, skillInfo.moduleName, + skillInfo.skillName, scriptPath, functionName, skillArgs, skillInfo.srcEntries, requestCode, + skillInfo.hapPath); + return ERR_OK; +} + +std::string SkillExecuteManager::CreateExecuteRecord(const sptr &callerToken, + const std::string &targetBundleName, const std::string &callerBundleName, + uint32_t callerTokenId, + const sptr &callback, + const std::string &externalRequestCode) +{ + std::lock_guard lock(mutex_); + uint64_t currentSeq = ++requestCodeSeq_; + std::string requestCode; + if (!externalRequestCode.empty()) { + requestCode = externalRequestCode; + } else { + requestCode = std::to_string(currentSeq); + } + auto record = std::make_shared(); + record->requestCode = requestCode; + record->callerToken = callerToken; + record->targetBundleName = targetBundleName; + record->callerBundleName = callerBundleName; + record->callerTokenId = callerTokenId; + record->requestCodeSeq = currentSeq; + record->state = SkillExecuteState::EXECUTING; + record->callback = callback; + + if (callerToken != nullptr) { + auto deathRecipient = sptr::MakeSptr( + [this](const std::string &reqCode) { OnCallerDied(reqCode); }, requestCode); + callerToken->AddDeathRecipient(deathRecipient); + record->deathRecipient = deathRecipient; + } + + records_[requestCode] = record; + PostSkillExecuteTimeout(requestCode, currentSeq); + TAG_LOGD(AAFwkTag::ABILITYMGR, + "create execute record, requestCode:%{public}s", requestCode.c_str()); + return requestCode; +} + +int32_t SkillExecuteManager::ExecuteSkillDone(const std::string &requestCode, int32_t resultCode, + const AppExecFwk::SkillExecuteResult &result, const std::string &callerBundleName) +{ + TAG_LOGD(AAFwkTag::ABILITYMGR, + "execute skill done, requestCode:%{public}s code:%{public}d", + requestCode.c_str(), resultCode); + std::lock_guard lock(mutex_); + auto it = records_.find(requestCode); + if (it == records_.end()) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "record not found, requestCode:%{public}s", requestCode.c_str()); + return ERR_INVALID_VALUE; + } + + auto record = it->second; + if (record->targetBundleName != callerBundleName) { + TAG_LOGE(AAFwkTag::ABILITYMGR, + "bundleName %{public}s and %{public}s mismatch", + callerBundleName.c_str(), record->targetBundleName.c_str()); + return ERR_INVALID_VALUE; + } + if (record->state != SkillExecuteState::EXECUTING) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "invalid state:%{public}d", static_cast(record->state)); + return ERR_INVALID_VALUE; + } + +#ifdef SUPPORT_UPMS + if (!result.uris.empty() && !record->callerBundleName.empty()) { + std::vector uriList; + for (const auto &uriStr : result.uris) { + uriList.emplace_back(uriStr); + } + auto &uriPermClient = UriPermissionManagerClient::GetInstance(); + auto ret = uriPermClient.GrantUriPermission( + uriList, result.flags, record->callerBundleName, 0, record->callerTokenId); + if (ret != ERR_OK) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "GrantUriPermission failed, ret:%{public}d", ret); + } + } +#endif + + RemoveSkillExecuteTimeoutLocked(record->requestCodeSeq); + record->state = SkillExecuteState::EXECUTE_DONE; + if (record->callback != nullptr) { + record->callback->OnExecuteDone(requestCode, resultCode, result); + } + RemoveRecord(requestCode); + return ERR_OK; +} + +void SkillExecuteManager::RemoveRecord(const std::string &requestCode) +{ + auto it = records_.find(requestCode); + if (it == records_.end()) { + return; + } + auto record = it->second; + if (record->callerToken != nullptr && record->deathRecipient != nullptr) { + record->callerToken->RemoveDeathRecipient(record->deathRecipient); + } + records_.erase(it); +} + +void SkillExecuteManager::OnCallerDied(const std::string &requestCode) +{ + TAG_LOGD(AAFwkTag::ABILITYMGR, "caller died, requestCode:%{public}s", requestCode.c_str()); + std::lock_guard lock(mutex_); + auto it = records_.find(requestCode); + if (it != records_.end()) { + RemoveSkillExecuteTimeoutLocked(it->second->requestCodeSeq); + it->second->state = SkillExecuteState::REMOTE_DIED; + RemoveRecord(requestCode); + } +} + +std::string SkillExecuteManager::ResolveDefaultAbilityName(const std::string &bundleName, + const std::string &moduleName, int32_t userId) +{ + auto bundleMgrHelper = DelayedSingleton::GetInstance(); + if (bundleMgrHelper == nullptr) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "failed to get BundleMgrHelper"); + return ""; + } + + AppExecFwk::BundleInfo bundleInfo; + auto flags = static_cast(AppExecFwk::GetBundleInfoFlag::GET_BUNDLE_INFO_WITH_HAP_MODULE) | + static_cast(AppExecFwk::GetBundleInfoFlag::GET_BUNDLE_INFO_WITH_ABILITY); + auto ret = IN_PROCESS_CALL(bundleMgrHelper->GetBundleInfoV9(bundleName, flags, bundleInfo, userId)); + if (ret != ERR_OK) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "GetBundleInfoV9 failed for bundle:%{public}s", + bundleName.c_str()); + return ""; + } + + for (const auto &hapModuleInfo : bundleInfo.hapModuleInfos) { + if (hapModuleInfo.moduleName != moduleName) { + continue; + } + std::string mainElement; + if (hapModuleInfo.isModuleJson) { + mainElement = hapModuleInfo.mainElementName; + } else { + mainElement = hapModuleInfo.mainAbility; + } + TAG_LOGD(AAFwkTag::ABILITYMGR, + "resolved default ability for module:%{public}s", + moduleName.c_str()); + return mainElement; + } + + TAG_LOGE(AAFwkTag::ABILITYMGR, "module not found:%{public}s in bundle:%{public}s", + moduleName.c_str(), bundleName.c_str()); + return ""; +} + +AppExecFwk::ExtensionAbilityType SkillExecuteManager::ResolveTargetType(const std::string &bundleName, + const std::string &moduleName, const std::string &abilityName, int32_t userId) +{ + auto bundleMgrHelper = DelayedSingleton::GetInstance(); + if (bundleMgrHelper == nullptr) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "failed to get BundleMgrHelper"); + return AppExecFwk::ExtensionAbilityType::UNSPECIFIED; + } + + Want queryWant; + queryWant.SetElementName("", bundleName, abilityName, moduleName); + std::vector extensionInfos; + auto flags = static_cast( + AppExecFwk::GetExtensionAbilityInfoFlag::GET_EXTENSION_ABILITY_INFO_DEFAULT); + bool ret = IN_PROCESS_CALL(bundleMgrHelper->QueryExtensionAbilityInfos( + queryWant, flags, userId, extensionInfos)); + if (ret && !extensionInfos.empty()) { + auto type = extensionInfos[0].type; + TAG_LOGD(AAFwkTag::ABILITYMGR, + "abilityName:%{public}s is extension, type:%{public}d", + abilityName.c_str(), static_cast(type)); + return type; + } + + TAG_LOGD(AAFwkTag::ABILITYMGR, + "abilityName:%{public}s is not extension, default to UIAbility", abilityName.c_str()); + return AppExecFwk::ExtensionAbilityType::UNSPECIFIED; +} + +void SkillExecuteManager::PostSkillExecuteTimeout( + const std::string &requestCode, uint64_t requestCodeSeq) +{ + auto handler = DelayedSingleton::GetInstance()->GetEventHandler(); + if (handler == nullptr) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "null event handler"); + return; + } + uint32_t timeout = static_cast( + AmsConfigurationParameter::GetInstance().GetAppStartTimeoutTime()) * + static_cast(GlobalConstant::SKILL_EXECUTE_TIMEOUT_MULTIPLE); + seqToRequestCodeMap_[requestCodeSeq] = requestCode; + auto event = EventWrap(AbilityManagerService::SKILL_EXECUTE_TIMEOUT_MSG, + static_cast(requestCodeSeq)); + event.SetTimeout(timeout); + handler->SendEvent(event, timeout, false); +} + +void SkillExecuteManager::RemoveSkillExecuteTimeoutLocked(uint64_t requestCodeSeq) +{ + auto handler = DelayedSingleton::GetInstance()->GetEventHandler(); + if (handler != nullptr) { + handler->RemoveEvent(AbilityManagerService::SKILL_EXECUTE_TIMEOUT_MSG, + static_cast(requestCodeSeq)); + } + seqToRequestCodeMap_.erase(requestCodeSeq); +} + +void SkillExecuteManager::OnTimeout(int64_t requestCodeSeq) +{ + TAG_LOGI(AAFwkTag::ABILITYMGR, "called, seq:%{public}" PRId64, requestCodeSeq); + std::string requestCode; + { + std::lock_guard lock(mutex_); + auto seqIt = seqToRequestCodeMap_.find(requestCodeSeq); + if (seqIt == seqToRequestCodeMap_.end()) { + TAG_LOGW(AAFwkTag::ABILITYMGR, "seq not found"); + return; + } + requestCode = seqIt->second; + seqToRequestCodeMap_.erase(seqIt); + } + std::lock_guard lock(mutex_); + auto it = records_.find(requestCode); + if (it == records_.end()) { + return; + } + auto record = it->second; + if (record->state != SkillExecuteState::EXECUTING) { + return; + } + TAG_LOGW(AAFwkTag::ABILITYMGR, "skill execute timed out, req:%{public}s", requestCode.c_str()); + record->state = SkillExecuteState::TIMED_OUT; + if (record->callback != nullptr) { + AppExecFwk::SkillExecuteResult emptyResult; + record->callback->OnExecuteDone(requestCode, ERR_TIMED_OUT, emptyResult); + } + RemoveRecord(requestCode); +} + +} // namespace AAFwk +} // namespace OHOS diff --git a/services/abilitymgr/src/skill/skill_execute_param.cpp b/services/abilitymgr/src/skill/skill_execute_param.cpp new file mode 100644 index 0000000000..8d34435837 --- /dev/null +++ b/services/abilitymgr/src/skill/skill_execute_param.cpp @@ -0,0 +1,211 @@ +/* + * 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 "skill_execute_param.h" + +#include + +#include "hilog_tag_wrapper.h" +#include "string_wrapper.h" + +namespace OHOS { +namespace AppExecFwk { + +bool SkillExecuteParam::ReadFromParcel(Parcel &parcel) +{ + bundleName_ = Str16ToStr8(parcel.ReadString16()); + moduleName_ = Str16ToStr8(parcel.ReadString16()); + skillName_ = Str16ToStr8(parcel.ReadString16()); + scriptPath_ = Str16ToStr8(parcel.ReadString16()); + functionName_ = Str16ToStr8(parcel.ReadString16()); + auto *args = parcel.ReadParcelable(); + if (args != nullptr) { + skillArgs_ = std::shared_ptr(args); + } else { + skillArgs_ = std::make_shared(); + } + int32_t srcCount = parcel.ReadInt32(); + for (int32_t i = 0; i < srcCount; i++) { + srcEntries_.push_back(Str16ToStr8(parcel.ReadString16())); + } + requestCode_ = Str16ToStr8(parcel.ReadString16()); + hapPath_ = Str16ToStr8(parcel.ReadString16()); + return true; +} + +SkillExecuteParam *SkillExecuteParam::Unmarshalling(Parcel &parcel) +{ + auto *param = new (std::nothrow) SkillExecuteParam(); + if (param == nullptr) { + return nullptr; + } + if (!param->ReadFromParcel(parcel)) { + delete param; + return nullptr; + } + return param; +} + +bool SkillExecuteParam::Marshalling(Parcel &parcel) const +{ + parcel.WriteString16(Str8ToStr16(bundleName_)); + parcel.WriteString16(Str8ToStr16(moduleName_)); + parcel.WriteString16(Str8ToStr16(skillName_)); + parcel.WriteString16(Str8ToStr16(scriptPath_)); + parcel.WriteString16(Str8ToStr16(functionName_)); + if (skillArgs_ != nullptr) { + parcel.WriteParcelable(skillArgs_.get()); + } else { + auto empty = std::make_shared(); + parcel.WriteParcelable(empty.get()); + } + parcel.WriteInt32(static_cast(srcEntries_.size())); + for (const auto &entry : srcEntries_) { + parcel.WriteString16(Str8ToStr16(entry)); + } + parcel.WriteString16(Str8ToStr16(requestCode_)); + parcel.WriteString16(Str8ToStr16(hapPath_)); + return true; +} + +bool SkillExecuteParam::IsSkillExecute(const AAFwk::Want &want) +{ + return want.HasParameter(SKILL_EXECUTE_PARAM_SKILL_NAME); +} + +bool SkillExecuteParam::GenerateFromWant(const AAFwk::Want &want, SkillExecuteParam ¶m) +{ + const WantParams &wantParams = want.GetParams(); + if (!wantParams.HasParam(SKILL_EXECUTE_PARAM_SKILL_NAME)) { + TAG_LOGE(AAFwkTag::ABILITYMGR, "no skill name in want"); + return false; + } + + param.bundleName_ = wantParams.GetStringParam(SKILL_EXECUTE_PARAM_BUNDLE_NAME); + param.moduleName_ = wantParams.GetStringParam(SKILL_EXECUTE_PARAM_MODULE_NAME); + param.skillName_ = wantParams.GetStringParam(SKILL_EXECUTE_PARAM_SKILL_NAME); + param.scriptPath_ = wantParams.GetStringParam(SKILL_EXECUTE_PARAM_SCRIPT_PATH); + param.functionName_ = wantParams.GetStringParam(SKILL_EXECUTE_PARAM_FUNCTION_NAME); + + // Extract skill args from Want + auto argsKeysStr = wantParams.GetStringParam(SKILL_EXECUTE_PARAM_ARGS_KEYS); + if (!argsKeysStr.empty()) { + param.skillArgs_ = std::make_shared(); + std::istringstream stream(argsKeysStr); + std::string key; + while (std::getline(stream, key, ';')) { + if (key.empty()) { continue; } + auto wantKey = std::string(SKILL_EXECUTE_PARAM_ARGS_PREFIX) + key; + auto it = wantParams.GetParams().find(wantKey); + if (it != wantParams.GetParams().end()) { + param.skillArgs_->SetParam(key, it->second); + } + } + } + + int32_t srcCount = 0; + auto srcCountStr = wantParams.GetStringParam(SKILL_EXECUTE_PARAM_SRC_ENTRIES_COUNT); + if (!srcCountStr.empty()) { + srcCount = std::stoi(srcCountStr); + } + for (int32_t i = 0; i < srcCount; i++) { + auto key = std::string(SKILL_EXECUTE_PARAM_SRC_ENTRY_PREFIX) + std::to_string(i); + param.srcEntries_.push_back(wantParams.GetStringParam(key)); + } + param.requestCode_ = wantParams.GetStringParam(SKILL_EXECUTE_PARAM_REQUEST_CODE); + param.hapPath_ = wantParams.GetStringParam(SKILL_EXECUTE_PARAM_HAP_PATH); + return true; +} + +bool SkillExecuteParam::RemoveSkillParam(AAFwk::Want &want) +{ + auto params = want.GetParams(); + auto argsKeysStr = params.GetStringParam(SKILL_EXECUTE_PARAM_ARGS_KEYS); + auto srcCountStr = params.GetStringParam(SKILL_EXECUTE_PARAM_SRC_ENTRIES_COUNT); + + want.RemoveParam(SKILL_EXECUTE_PARAM_BUNDLE_NAME); + want.RemoveParam(SKILL_EXECUTE_PARAM_MODULE_NAME); + want.RemoveParam(SKILL_EXECUTE_PARAM_SKILL_NAME); + want.RemoveParam(SKILL_EXECUTE_PARAM_SCRIPT_PATH); + want.RemoveParam(SKILL_EXECUTE_PARAM_FUNCTION_NAME); + want.RemoveParam(SKILL_EXECUTE_PARAM_ARGS_KEYS); + want.RemoveParam(SKILL_EXECUTE_PARAM_SRC_ENTRIES_COUNT); + want.RemoveParam(SKILL_EXECUTE_PARAM_HAP_PATH); + want.RemoveParam(SKILL_EXECUTE_PARAM_REQUEST_CODE); + + if (!argsKeysStr.empty()) { + std::istringstream stream(argsKeysStr); + std::string key; + while (std::getline(stream, key, ';')) { + if (key.empty()) { continue; } + auto wantKey = std::string(SKILL_EXECUTE_PARAM_ARGS_PREFIX) + key; + want.RemoveParam(wantKey); + } + } + if (!srcCountStr.empty()) { + int32_t srcCount = std::stoi(srcCountStr); + for (int32_t i = 0; i < srcCount; i++) { + auto key = std::string(SKILL_EXECUTE_PARAM_SRC_ENTRY_PREFIX) + std::to_string(i); + want.RemoveParam(key); + } + } + return true; +} + +void SkillExecuteParam::WriteToWant(AAFwk::Want &want, const std::string &bundleName, + const std::string &moduleName, const std::string &skillName, + const std::string &scriptPath, const std::string &functionName, + const std::shared_ptr &skillArgs, + const std::vector &srcEntries, + const std::string &requestCode, const std::string &hapPath) +{ + want.SetParam(SKILL_EXECUTE_PARAM_BUNDLE_NAME, bundleName); + want.SetParam(SKILL_EXECUTE_PARAM_MODULE_NAME, moduleName); + want.SetParam(SKILL_EXECUTE_PARAM_SKILL_NAME, skillName); + if (!scriptPath.empty()) { + want.SetParam(SKILL_EXECUTE_PARAM_SCRIPT_PATH, scriptPath); + } + if (!functionName.empty()) { + want.SetParam(SKILL_EXECUTE_PARAM_FUNCTION_NAME, functionName); + } + if (skillArgs != nullptr && !skillArgs->GetParams().empty()) { + std::string argsKeys; + auto params = want.GetParams(); + for (auto &[key, value] : skillArgs->GetParams()) { + if (!argsKeys.empty()) { argsKeys += ";"; } + argsKeys += key; + auto wantKey = std::string(SKILL_EXECUTE_PARAM_ARGS_PREFIX) + key; + params.SetParam(wantKey, value); + } + params.SetParam(SKILL_EXECUTE_PARAM_ARGS_KEYS, AAFwk::String::Box(argsKeys)); + want.SetParams(params); + } + if (!srcEntries.empty()) { + want.SetParam(SKILL_EXECUTE_PARAM_SRC_ENTRIES_COUNT, std::to_string(srcEntries.size())); + for (size_t i = 0; i < srcEntries.size(); i++) { + auto key = std::string(SKILL_EXECUTE_PARAM_SRC_ENTRY_PREFIX) + std::to_string(i); + want.SetParam(key, srcEntries[i]); + } + } + if (!requestCode.empty()) { + want.SetParam(SKILL_EXECUTE_PARAM_REQUEST_CODE, requestCode); + } + if (!hapPath.empty()) { + want.SetParam(SKILL_EXECUTE_PARAM_HAP_PATH, hapPath); + } +} + +} // namespace AppExecFwk +} // namespace OHOS diff --git a/services/abilitymgr/src/skill/skill_execute_result.cpp b/services/abilitymgr/src/skill/skill_execute_result.cpp new file mode 100644 index 0000000000..04523f970c --- /dev/null +++ b/services/abilitymgr/src/skill/skill_execute_result.cpp @@ -0,0 +1,63 @@ +/* + * 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 "skill_execute_result.h" + +namespace OHOS { +namespace AppExecFwk { + +bool SkillExecuteResult::ReadFromParcel(Parcel &parcel) +{ + code = parcel.ReadInt32(); + auto wantParams = parcel.ReadParcelable(); + if (wantParams == nullptr) { + return false; + } + result = std::shared_ptr(wantParams); + int32_t uriCount = parcel.ReadInt32(); + for (int32_t i = 0; i < uriCount; i++) { + uris.push_back(Str16ToStr8(parcel.ReadString16())); + } + flags = parcel.ReadUint32(); + return true; +} + +bool SkillExecuteResult::Marshalling(Parcel &parcel) const +{ + parcel.WriteInt32(code); + parcel.WriteParcelable(result.get()); + parcel.WriteInt32(static_cast(uris.size())); + for (const auto &uri : uris) { + parcel.WriteString16(Str8ToStr16(uri)); + } + parcel.WriteUint32(flags); + return true; +} + +SkillExecuteResult *SkillExecuteResult::Unmarshalling(Parcel &parcel) +{ + auto *res = new (std::nothrow) SkillExecuteResult(); + if (res == nullptr) { + return nullptr; + } + if (!res->ReadFromParcel(parcel)) { + delete res; + return nullptr; + } + return res; +} + +} // namespace AppExecFwk +} // namespace OHOS diff --git a/services/abilitymgr/src/skill/skill_query_info.cpp b/services/abilitymgr/src/skill/skill_query_info.cpp new file mode 100644 index 0000000000..6d13bd446d --- /dev/null +++ b/services/abilitymgr/src/skill/skill_query_info.cpp @@ -0,0 +1,75 @@ +/* + * 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 "skill_query_info.h" + +namespace OHOS { +namespace AppExecFwk { + +bool SkillQueryInfo::ReadFromParcel(Parcel &parcel) +{ + bundleName = Str16ToStr8(parcel.ReadString16()); + moduleName = Str16ToStr8(parcel.ReadString16()); + skillName = Str16ToStr8(parcel.ReadString16()); + abilityName = Str16ToStr8(parcel.ReadString16()); + type = parcel.ReadInt32(); + + int32_t srcCount = parcel.ReadInt32(); + for (int32_t i = 0; i < srcCount; i++) { + srcEntries.push_back(Str16ToStr8(parcel.ReadString16())); + } + + int32_t permCount = parcel.ReadInt32(); + for (int32_t i = 0; i < permCount; i++) { + permissions.push_back(Str16ToStr8(parcel.ReadString16())); + } + return true; +} + +bool SkillQueryInfo::Marshalling(Parcel &parcel) const +{ + parcel.WriteString16(Str8ToStr16(bundleName)); + parcel.WriteString16(Str8ToStr16(moduleName)); + parcel.WriteString16(Str8ToStr16(skillName)); + parcel.WriteString16(Str8ToStr16(abilityName)); + parcel.WriteInt32(type); + + parcel.WriteInt32(static_cast(srcEntries.size())); + for (const auto &entry : srcEntries) { + parcel.WriteString16(Str8ToStr16(entry)); + } + + parcel.WriteInt32(static_cast(permissions.size())); + for (const auto &perm : permissions) { + parcel.WriteString16(Str8ToStr16(perm)); + } + return true; +} + +SkillQueryInfo *SkillQueryInfo::Unmarshalling(Parcel &parcel) +{ + auto *info = new (std::nothrow) SkillQueryInfo(); + if (info == nullptr) { + return nullptr; + } + if (!info->ReadFromParcel(parcel)) { + delete info; + return nullptr; + } + return info; +} + +} // namespace AppExecFwk +} // namespace OHOS diff --git a/services/abilitymgr/src/system_dialog_scheduler.cpp b/services/abilitymgr/src/system_dialog_scheduler.cpp index 69c34bc995..20ab001f0d 100644 --- a/services/abilitymgr/src/system_dialog_scheduler.cpp +++ b/services/abilitymgr/src/system_dialog_scheduler.cpp @@ -151,9 +151,10 @@ Want SystemDialogScheduler::GetJumpInterceptorDialogWant(Want &targetWant) nlohmann::json jsonObj; jsonObj[IS_DEFAULT_SELECTOR] = AppUtils::GetInstance().IsSelectorDialogDefaultPossion(); - jsonObj["bundleName"] = targetWant.GetElement().GetBundleName(); - jsonObj["abilityName"] = targetWant.GetElement().GetAbilityName(); - jsonObj["moduleName"] = targetWant.GetElement().GetModuleName(); + auto wantEle = targetWant.GetElement(); + jsonObj["bundleName"] = wantEle.GetBundleName(); + jsonObj["abilityName"] = wantEle.GetAbilityName(); + jsonObj["moduleName"] = wantEle.GetModuleName(); const std::string params = jsonObj.dump(); targetWant.SetElementName(BUNDLE_NAME_DIALOG, ABILITY_NAME_JUMP_INTERCEPTOR_DIALOG); diff --git a/services/abilitymgr/src/ui_extension/ui_extension_ability_manager.cpp b/services/abilitymgr/src/ui_extension/ui_extension_ability_manager.cpp index 512d8ded00..e31461f57f 100644 --- a/services/abilitymgr/src/ui_extension/ui_extension_ability_manager.cpp +++ b/services/abilitymgr/src/ui_extension/ui_extension_ability_manager.cpp @@ -154,9 +154,9 @@ int UIExtensionAbilityManager::UnloadUIExtensionAbility( TAG_LOGD(AAFwkTag::ABILITYMGR, "call"); CHECK_POINTER_AND_RETURN(abilityRecord, ERR_INVALID_VALUE); - auto preLoadUIExtensionInfo = std::make_tuple(abilityRecord->GetWant().GetElement().GetAbilityName(), - abilityRecord->GetWant().GetElement().GetBundleName(), - abilityRecord->GetWant().GetElement().GetModuleName(), hostPid); + auto preLoadUIExtensionInfo = std::make_tuple(abilityRecord->GetAbilityName(), + abilityRecord->GetBundleName(), + abilityRecord->GetModuleName(), hostPid); CHECK_POINTER_AND_RETURN(uiExtensionAbilityRecordMgr_, ERR_NULL_OBJECT); auto extensionRecordId = abilityRecord->GetUIExtensionAbilityId(); @@ -184,9 +184,9 @@ void UIExtensionAbilityManager::ClearPreloadUIExtensionRecord(const std::shared_ return; } - auto extensionRecordMapKey = std::make_tuple(abilityRecord->GetWant().GetElement().GetAbilityName(), - abilityRecord->GetWant().GetElement().GetBundleName(), - abilityRecord->GetWant().GetElement().GetModuleName(), hostPid); + auto extensionRecordMapKey = std::make_tuple(abilityRecord->GetAbilityName(), + abilityRecord->GetBundleName(), + abilityRecord->GetModuleName(), hostPid); uiExtensionAbilityRecordMgr_->RemovePreloadUIExtensionRecordById(extensionRecordMapKey, extensionRecordId); } @@ -198,14 +198,14 @@ int UIExtensionAbilityManager::AttachAbilityThreadInner(const sptrGetElementName().GetBundleName().c_str(), - abilityRecord->GetElementName().GetAbilityName().c_str(), userId_); + abilityRecord->GetInfoBundleName().c_str(), + abilityRecord->GetInfoAbilityName().c_str(), userId_); } auto tmpRecord = Token::GetAbilityRecordByToken(token); if (tmpRecord && tmpRecord != abilityRecord) { TAG_LOGW(AAFwkTag::EXT, "Token:%{public}s/%{public}s, user:%{public}d", - tmpRecord->GetElementName().GetBundleName().c_str(), - tmpRecord->GetElementName().GetAbilityName().c_str(), userId_); + tmpRecord->GetInfoBundleName().c_str(), + tmpRecord->GetInfoAbilityName().c_str(), userId_); } if (!IsUIExtensionAbility(abilityRecord)) { abilityRecord = nullptr; @@ -228,7 +228,7 @@ int UIExtensionAbilityManager::AttachAbilityThreadInner(const sptrRemoveSpecifiedWantParam(UIEXTENSION_NOTIFY_BIND); if (IsUIExtensionAbility(abilityRecord) && !abilityRecord->IsCreateByConnect() - && !abilityRecord->GetWant().GetBoolParam(IS_PRELOAD_UIEXTENSION_ABILITY, false)) { + && !abilityRecord->GetBoolParam(IS_PRELOAD_UIEXTENSION_ABILITY, false)) { abilityRecord->PostUIExtensionAbilityTimeoutTask(AbilityManagerService::FOREGROUND_TIMEOUT_MSG); DelayedSingleton::GetInstance()->MoveToForeground(token); @@ -371,8 +371,8 @@ void UIExtensionAbilityManager::DoBackgroundAbilityWindow(const std::shared_ptr< auto abilitystateStr = abilityRecord->ConvertAbilityState(abilityRecord->GetAbilityState()); TAG_LOGI(AAFwkTag::ABILITYMGR, "%{public}s/%{public}s, persistentId:%{public}d, " "abilityState:%{public}s, pendingState:%{public}d", - abilityRecord->GetElementName().GetBundleName().c_str(), - abilityRecord->GetElementName().GetAbilityName().c_str(), + abilityRecord->GetInfoBundleName().c_str(), + abilityRecord->GetInfoAbilityName().c_str(), sessionInfo->persistentId, abilitystateStr.c_str(), static_cast(abilityRecord->GetPendingState())); abilityRecord->SetPendingState(AbilityState::BACKGROUND); @@ -520,7 +520,7 @@ int32_t UIExtensionAbilityManager::UnRegisterPreloadUIExtensionHostClient(int32_ int32_t UIExtensionAbilityManager::StartAbilityLocked(const AbilityRequest &abilityRequest) { if (AppUtils::GetInstance().IsForbidStart()) { - TAG_LOGW(AAFwkTag::EXT, "forbid start: %{public}s", abilityRequest.want.GetElement().GetBundleName().c_str()); + TAG_LOGW(AAFwkTag::EXT, "forbid start: %{public}s", abilityRequest.want.GetBundle().c_str()); return INNER_ERR; } @@ -558,8 +558,8 @@ int32_t UIExtensionAbilityManager::StartAbilityLocked(const AbilityRequest &abil CHECK_POINTER_AND_RETURN(targetService, ERR_INVALID_VALUE); TAG_LOGI(AAFwkTag::EXT, "%{public}s/%{public}s", - targetService->GetElementName().GetBundleName().c_str(), - targetService->GetElementName().GetAbilityName().c_str()); + targetService->GetInfoBundleName().c_str(), + targetService->GetInfoAbilityName().c_str()); std::string value = abilityRequest.want.GetStringParam(Want::PARM_LAUNCH_REASON_MESSAGE); if (!value.empty()) { @@ -669,8 +669,8 @@ void UIExtensionAbilityManager::DoForegroundUIExtension(std::shared_ptrConvertAbilityState(abilityRecord->GetAbilityState()); TAG_LOGI(AAFwkTag::ABILITYMGR, "foreground ability: %{public}s/%{public}s, persistentId: %{public}d, abilityState: %{public}s", - abilityRecord->GetElementName().GetBundleName().c_str(), - abilityRecord->GetElementName().GetAbilityName().c_str(), + abilityRecord->GetInfoBundleName().c_str(), + abilityRecord->GetInfoAbilityName().c_str(), abilityRequest.sessionInfo->persistentId, abilitystateStr.c_str()); if (abilityRecord->IsReady() && !abilityRecord->IsAbilityState(AbilityState::INACTIVATING) && @@ -812,7 +812,7 @@ void UIExtensionAbilityManager::RemoveUIExtensionAbilityRecord( { CHECK_POINTER(abilityRecord); CHECK_POINTER(uiExtensionAbilityRecordMgr_); - if (abilityRecord->GetWant().GetBoolParam(IS_PRELOAD_UIEXTENSION_ABILITY, false)) { + if (abilityRecord->GetBoolParam(IS_PRELOAD_UIEXTENSION_ABILITY, false)) { ClearPreloadUIExtensionRecord(abilityRecord); } if (UIExtensionWrapper::IsAgentUIExtension(abilityRecord->GetAbilityInfo().extensionAbilityType)) { @@ -894,7 +894,7 @@ void UIExtensionAbilityManager::UpdateUIExtensionInfo(const std::shared_ptrGetWant().GetBoolParam(IS_PRELOAD_UIEXTENSION_ABILITY, false)) { + if (abilityRecord->GetBoolParam(IS_PRELOAD_UIEXTENSION_ABILITY, false)) { auto rootHostPid = (hostPid == AAFwk::DEFAULT_INVAL_VALUE) ? IPCSkeleton::GetCallingPid() : hostPid; wantParams.SetParam(UIEXTENSION_ROOT_HOST_PID, AAFwk::Integer::Box(rootHostPid)); } @@ -1098,8 +1098,8 @@ void UIExtensionAbilityManager::HandleStartTimeoutTaskInner(const std::shared_pt { if (UIExtensionWrapper::IsUIExtension(abilityRecord->GetAbilityInfo().extensionAbilityType)) { TAG_LOGE(AAFwkTag::ABILITYMGR, "consume session timeout, Uri: %{public}s/%{public}s", - abilityRecord->GetElementName().GetBundleName().c_str(), - abilityRecord->GetElementName().GetAbilityName().c_str()); + abilityRecord->GetInfoBundleName().c_str(), + abilityRecord->GetInfoAbilityName().c_str()); LoadTimeout(abilityRecord); } AbilityConnectManager::HandleStartTimeoutTaskInner(abilityRecord); @@ -1263,7 +1263,7 @@ int UIExtensionAbilityManager::DispatchInactive(const std::shared_ptrSetAbilityState(AbilityState::INACTIVE); if (abilityRecord->IsCreateByConnect()) { ConnectAbility(abilityRecord); - } else if (abilityRecord->GetWant().GetBoolParam(IS_PRELOAD_UIEXTENSION_ABILITY, false)) { + } else if (abilityRecord->GetBoolParam(IS_PRELOAD_UIEXTENSION_ABILITY, false)) { TAG_LOGD(AAFwkTag::ABILITYMGR, "IS_PRELOAD_UIEXTENSION_ABILITY"); auto ret = AddPreloadUIExtensionRecord(abilityRecord); if (ret != ERR_OK) { @@ -1353,8 +1353,8 @@ void UIExtensionAbilityManager::CompleteForegroundInner(const std::shared_ptrGetElementName().GetBundleName().c_str(), - abilityRecord->GetElementName().GetAbilityName().c_str(), + abilityRecord->GetInfoBundleName().c_str(), + abilityRecord->GetInfoAbilityName().c_str(), abilityRecord->GetUIExtensionAbilityId(), sessionInfo->persistentId, abilityRecord->GetAbilityState(), static_cast(abilityRecord->GetPendingState())); @@ -1455,8 +1455,8 @@ void UIExtensionAbilityManager::CompleteBackground(const std::shared_ptrGetElementName().GetBundleName().c_str(), - abilityRecord->GetElementName().GetAbilityName().c_str(), + abilityRecord->GetInfoBundleName().c_str(), + abilityRecord->GetInfoAbilityName().c_str(), abilityRecord->GetUIExtensionAbilityId(), sessionInfo->persistentId, abilityRecord->GetAbilityState(), static_cast(abilityRecord->GetPendingState())); diff --git a/services/abilitymgr/src/utils/ability_permission_util.cpp b/services/abilitymgr/src/utils/ability_permission_util.cpp index 844cd2ebe6..bfcf95e4a7 100644 --- a/services/abilitymgr/src/utils/ability_permission_util.cpp +++ b/services/abilitymgr/src/utils/ability_permission_util.cpp @@ -28,6 +28,7 @@ #include "permission_constants.h" #include "permission_verification.h" #include "start_ability_utils.h" +#include "user_controller/user_controller.h" #include "utils/app_mgr_util.h" #ifdef SUPPORT_SCREEN #include "scene_board_judgement.h" @@ -414,5 +415,32 @@ bool AbilityPermissionUtil::NeedCheckStatusBar(std::shared_ptr ab } return true; } + +bool AbilityPermissionUtil::CheckStartUIAbilityByUserLockStatus(const std::string &bundleName) +{ + if (bundleName.empty()) { + TAG_LOGW(AAFwkTag::ABILITYMGR, "empty name"); + return true; + } + auto userId = IPCSkeleton::GetCallingUid() / BASE_USER_RANGE; + auto userLockStatus = AbilityRuntime::UserController::GetInstance().GetUserLockStatus(userId); + if (userLockStatus == AbilityRuntime::UserController::UserLockStatus::USER_UNLOCKED) { + return true; + } + auto bms = AbilityUtil::GetBundleManagerHelper(); + if (bms == nullptr) { + TAG_LOGW(AAFwkTag::ABILITYMGR, "null helper"); + return true; + } + AppExecFwk::BundleInfo bundleInfo; + auto ret = + IN_PROCESS_CALL(bms->GetBundleInfo(bundleName, + static_cast(AppExecFwk::GetBundleInfoFlag::GET_BUNDLE_INFO_WITH_APPLICATION), bundleInfo, userId)); + bool isSystemApp = ret ? bundleInfo.applicationInfo.isSystemApp : false; + if (!isSystemApp) { + TAG_LOGW(AAFwkTag::ABILITYMGR, "not system app"); + } + return isSystemApp; +} } // AAFwk } // OHOS \ No newline at end of file diff --git a/services/abilitymgr/src/utils/oe_extension_utils.cpp b/services/abilitymgr/src/utils/oe_extension_utils.cpp index 8d798330cb..1e087c2b49 100644 --- a/services/abilitymgr/src/utils/oe_extension_utils.cpp +++ b/services/abilitymgr/src/utils/oe_extension_utils.cpp @@ -60,9 +60,9 @@ int32_t OEExtensionUtils::ValidateCaller( return ERR_INVALID_CALLER; } - if (want.GetElement().GetBundleName() != abilityInfo.bundleName) { + if (want.GetBundle() != abilityInfo.bundleName) { TAG_LOGE(AAFwkTag::ABILITYMGR, "want bundleName %{public}s does not match caller bundleName %{public}s", - want.GetElement().GetBundleName().c_str(), abilityInfo.bundleName.c_str()); + want.GetBundle().c_str(), abilityInfo.bundleName.c_str()); return INVALID_PARAMETERS_ERR; } diff --git a/services/abilitymgr/src/utils/start_ability_utils.cpp b/services/abilitymgr/src/utils/start_ability_utils.cpp index 75e830595d..8b91441f8a 100644 --- a/services/abilitymgr/src/utils/start_ability_utils.cpp +++ b/services/abilitymgr/src/utils/start_ability_utils.cpp @@ -47,7 +47,7 @@ thread_local bool StartAbilityUtils::startSpecifiedBySCB = false; bool StartAbilityUtils::GetAppIndex(const Want &want, sptr callerToken, int32_t &appIndex) { auto abilityRecord = Token::GetAbilityRecordByToken(callerToken); - if (abilityRecord && abilityRecord->GetApplicationInfo().bundleName == want.GetElement().GetBundleName() && + if (abilityRecord && abilityRecord->GetApplicationInfo().bundleName == want.GetBundle() && abilityRecord->GetAppIndex() > AbilityRuntime::GlobalConstant::MAX_APP_CLONE_INDEX) { appIndex = abilityRecord->GetAppIndex(); return true; @@ -110,7 +110,7 @@ std::vector StartAbilityUtils::GetCloneAppIndexes(const std::string &bu int32_t StartAbilityUtils::CheckAppProvisionMode(const Want& want, int32_t userId, sptr callerToken) { auto abilityInfo = StartAbilityUtils::startAbilityInfo; - if (!abilityInfo || abilityInfo->GetAppBundleName() != want.GetElement().GetBundleName()) { + if (!abilityInfo || abilityInfo->GetAppBundleName() != want.GetBundle()) { int32_t appIndex = 0; if (!GetAppIndex(want, callerToken, appIndex)) { TAG_LOGE(AAFwkTag::ABILITYMGR, "invalid app clone index"); @@ -420,7 +420,7 @@ void StartAbilityUtils::SetTargetCloneIndexInSameBundle(const Want &want, sptrGetAbilityInfo().bundleName != want.GetElement().GetBundleName()) { + if (callerRecord->GetAbilityInfo().bundleName != want.GetBundle()) { TAG_LOGD(AAFwkTag::ABILITYMGR, "not the same bundle"); return; } diff --git a/services/abilitymgr/src/utils/update_caller_info_util.cpp b/services/abilitymgr/src/utils/update_caller_info_util.cpp index 5f39bf5b82..8812fe864e 100644 --- a/services/abilitymgr/src/utils/update_caller_info_util.cpp +++ b/services/abilitymgr/src/utils/update_caller_info_util.cpp @@ -21,6 +21,7 @@ #include "accesstoken_kit.h" #include "app_scheduler.h" #include "ams_configuration_parameter.h" +#include "event_report.h" #include "dialog_session_manager.h" #include "hilog_tag_wrapper.h" #include "hitrace_meter.h" @@ -48,6 +49,7 @@ constexpr const char* COMPONENT_STARTUP_NEW_RULES = "component.startup.newRules" constexpr const char* SPECIFIED_ABILITY_FLAG = "ohos.ability.params.specifiedAbilityFlag"; constexpr const char* SHELL_ASSISTANT_BUNDLENAME = "com.ohos.shell_assistant"; constexpr const char* UIEXTENSION_TYPE_KEY = "ability.want.params.uiExtensionType"; +const std::string HIDE_SENSITIVE_TYPE = "ohos.media.params.hideSensitiveType"; constexpr int32_t BROKER_UID = 5557; } @@ -90,7 +92,6 @@ void UpdateCallerInfoUtil::UpdateCallerInfo(Want& want, const sptrGetAbilityInfo().bundleName; want.RemoveParam(Want::PARAM_RESV_CALLER_BUNDLE_NAME); @@ -292,7 +291,6 @@ void UpdateCallerInfoUtil::UpdateCallerInfoFromToken(Want& want, const sptr &callerToken, Want &want, int32_t requestCode, bool backFlag) { - want.RemoveParam(AbilityRuntime::GlobalConstant::GAME_PRELAUNCH); if (want.HasParameter(CALLER_REQUEST_CODE)) { want.RemoveParam(CALLER_REQUEST_CODE); } @@ -317,7 +315,6 @@ void UpdateCallerInfoUtil::UpdateDmsCallerInfo(Want& want, const sptr(IPCSkeleton::GetCallingTokenID()); int32_t callerUid = IPCSkeleton::GetCallingUid(); ClearProtectedWantParam(want); - want.RemoveParam(AbilityRuntime::GlobalConstant::GAME_PRELAUNCH); auto abilityRecord = Token::GetAbilityRecordByToken(callerToken); if (!abilityRecord) { @@ -371,20 +368,22 @@ void UpdateCallerInfoUtil::UpdateCallerAppCloneIndex(Want& want, int32_t appInde void UpdateCallerInfoUtil::ClearProtectedWantParam(Want &want) { - if (want.HasParameter(Want::PARAM_RESV_CALLER_NATIVE_NAME)) { + if (want.HasParameter(HIDE_SENSITIVE_TYPE)) { EventInfo eventInfo; - eventInfo.bundleName = want.GetElement().GetBundleName(); - eventInfo.moduleName = want.GetElement().GetModuleName(); - eventInfo.abilityName = want.GetElement().GetAbilityName(); + std::string bundleName = want.GetBundle(); int32_t callerUid = IPCSkeleton::GetCallingUid(); std::string callerBundleName; auto bundleMgr = AbilityUtil::GetBundleManagerHelper(); if (bundleMgr != nullptr) { IN_PROCESS_CALL(bundleMgr->GetNameForUid(callerUid, callerBundleName)); } - eventInfo.callerBundleName = callerBundleName.empty() ? std::to_string(callerUid) : callerBundleName; - AbilityEventUtil::SendStartAbilityErrorEvent(eventInfo, AAFwk::ERR_NOT_EXPECTED_NATIVE_CALLER_NAME, - std::string("no expected caller native name: ") + want.GetStringParam(Want::PARAM_RESV_CALLER_NATIVE_NAME)); + if (callerBundleName.empty()) { + callerBundleName = std::to_string(callerUid); + } + auto hideSensitiveType = want.GetIntParam(HIDE_SENSITIVE_TYPE, 0); + eventInfo.uri = "HideSensitiveType://" + bundleName + "/" + callerBundleName + "/" + + std::to_string(hideSensitiveType); + EventReport::SendGrantUriPermissionEvent(EventName::GRANT_URI_PERMISSION, eventInfo); } want.RemoveParam(Want::PARAM_RESV_CALLER_NATIVE_NAME); want.RemoveParam(COMPONENT_STARTUP_NEW_RULES); diff --git a/services/appdfr/include/appfreeze_manager.h b/services/appdfr/include/appfreeze_manager.h index d271123195..e65c05dadc 100644 --- a/services/appdfr/include/appfreeze_manager.h +++ b/services/appdfr/include/appfreeze_manager.h @@ -130,6 +130,7 @@ public: int GetFreezeExitReason(const std::string& eventName); void UpdateFreezeExcludedPid(bool isAdd, int32_t targetPid, int32_t profilerPid); bool IsFreezeExcludedPid(int32_t targetPid); + bool CheckPreloadUIExtension(const std::string& message, const std::string& bundleName, int32_t pid); private: struct PeerBinderInfo { diff --git a/services/appdfr/src/appfreeze_manager.cpp b/services/appdfr/src/appfreeze_manager.cpp index 066c31b704..6d6973a327 100644 --- a/services/appdfr/src/appfreeze_manager.cpp +++ b/services/appdfr/src/appfreeze_manager.cpp @@ -79,6 +79,7 @@ static constexpr const char *const SPAN_ID = "span_id: "; static constexpr const char *const PARENT_SPAN_ID = "parent_span_id: "; static constexpr const char *const TRACE_FLAG = "trace_flag: "; static constexpr const char *const DEV_SYSLOAD = "/dev/sysload"; +constexpr const char* PRELOAD_UIEXTENSION = "PreloadUIExtension"; // kill resaon constexpr int32_t INVALID_KILL_ID = -2; constexpr const char* INVALID_KILL_REASON = "InvalidKillId"; @@ -1447,5 +1448,16 @@ bool AppfreezeManager::IsFreezeExcludedPid(int32_t targetPid) TAG_LOGW(AAFwkTag::APPDFR, "pid %{public}d is in freeze excluded list", targetPid); return true; } + +bool AppfreezeManager::CheckPreloadUIExtension(const std::string& message, const std::string& bundleName, + int32_t pid) +{ + if (message.find(PRELOAD_UIEXTENSION) != std::string::npos) { + TAG_LOGW(AAFwkTag::APPDFR, "don't report event, msg: PreloadUIExtension, bundleName: %{public}s " + "pid: %{public}d", bundleName.c_str(), pid); + return true; + } + return false; +} } // namespace AppExecFwk } // namespace OHOS \ No newline at end of file diff --git a/services/appmgr/BUILD.gn b/services/appmgr/BUILD.gn index 57babc5c1f..ff8a89fedf 100644 --- a/services/appmgr/BUILD.gn +++ b/services/appmgr/BUILD.gn @@ -146,6 +146,7 @@ ohos_shared_library("libappms") { "init:libbeget_proxy", "init:libbegetutil", "ipc:ipc_core", + "ipc:ipc_single", "json:nlohmann_json_static", "kv_store:distributeddata_inner", "memmgr:memmgrclient", diff --git a/services/appmgr/include/app_mgr_service.h b/services/appmgr/include/app_mgr_service.h index ce70cc6508..9bc4f3135c 100644 --- a/services/appmgr/include/app_mgr_service.h +++ b/services/appmgr/include/app_mgr_service.h @@ -835,6 +835,8 @@ public: int32_t IsProcessCacheSupported(int32_t pid, bool &isSupported) override; + int32_t IsChildProcessSupported(bool isNative, bool &isSupported) override; + int32_t SetProcessCacheEnable(int32_t pid, bool enable) override; int32_t LockProcessCache(int32_t pid, bool isLock) override; @@ -1153,6 +1155,10 @@ private: */ void SetTerminateTimeOutFlag(const sptr token) override; + int32_t EnableDelayedProcessExit(int32_t pid, bool enabled) override; + + void CancelDelayedExitTask(int32_t pid) override; + enum DumpIpcKey { KEY_DUMP_IPC_START = 0, KEY_DUMP_IPC_STOP, diff --git a/services/appmgr/include/app_mgr_service_event_handler.h b/services/appmgr/include/app_mgr_service_event_handler.h index d4e2c0d1a4..751b20118d 100644 --- a/services/appmgr/include/app_mgr_service_event_handler.h +++ b/services/appmgr/include/app_mgr_service_event_handler.h @@ -59,6 +59,7 @@ public: static constexpr int32_t START_PROCESS_SPECIFIED_ABILITY_TIMEOUT = 75000; // ms static constexpr int32_t START_SPECIFIED_PROCESS_TIMEOUT = 45000; // ms static constexpr int32_t KILL_PROCESS_TIMEOUT = 45000; // ms + static constexpr int32_t KILL_PROCESS_TIMEOUT_DELAY = 45000; // ms #else static constexpr int32_t TERMINATE_ABILITY_TIMEOUT = 3000; // ms static constexpr int32_t TERMINATE_APPLICATION_TIMEOUT = 10000; // ms @@ -69,6 +70,7 @@ public: static constexpr int32_t START_PROCESS_SPECIFIED_ABILITY_TIMEOUT = 5000; // ms static constexpr int32_t START_SPECIFIED_PROCESS_TIMEOUT = 2000; // ms static constexpr int32_t KILL_PROCESS_TIMEOUT = 3000; // ms + static constexpr int32_t KILL_PROCESS_TIMEOUT_DELAY = 6000; // ms #endif static constexpr int32_t DELAY_KILL_PROCESS_TIMEOUT = 3000; // ms static constexpr int32_t DELAY_KILL_EXTENSION_PROCESS_TIMEOUT = 500; // ms diff --git a/services/appmgr/include/app_mgr_service_inner.h b/services/appmgr/include/app_mgr_service_inner.h index c7cf8c9a38..13f0c0d8b0 100644 --- a/services/appmgr/include/app_mgr_service_inner.h +++ b/services/appmgr/include/app_mgr_service_inner.h @@ -135,6 +135,7 @@ public: struct MakeImageRequest { std::string bundleName; + std::string abilityName; int32_t userId = -1; int32_t appCloneIndex = -1; PreloadMode preloadMode = PreloadMode::PRELOAD_NONE; @@ -142,6 +143,7 @@ public: bool operator==(const MakeImageRequest& other) const { return bundleName == other.bundleName && + abilityName == other.abilityName && userId == other.userId && appCloneIndex == other.appCloneIndex; } @@ -150,9 +152,10 @@ public: size_t operator()(const MakeImageRequest& req) const { size_t h1 = std::hash{}(req.bundleName); - size_t h2 = std::hash{}(req.userId); - size_t h3 = std::hash{}(req.appCloneIndex); - return h1 ^ (h2 << 1) ^ (h3 << 2); + size_t h2 = std::hash{}(req.abilityName); + size_t h3 = std::hash{}(req.userId); + size_t h4 = std::hash{}(req.appCloneIndex); + return h1 ^ (h2 << 1) ^ (h3 << 2) ^ (h4 << 3); } }; }; @@ -332,15 +335,18 @@ public: ImageError MakeImageInner(const AAFwk::Want &want, int32_t userId, AppExecFwk::PreloadMode preloadMode, int32_t appIndex, sptr errorHandler); void DestroyImage(uint64_t checkpointId, sptr errorHandler); - ImageError DestroyImageInner(uint64_t checkpointId, sptr errorHandler); + ImageError DestroyImageByCheckpointId(uint64_t checkpointId); ImageError DestroyImageForUninstallOrUpgrade(int32_t uid); - ImageError DestroyImageForFault(const std::string& bundleName, int32_t userId, int32_t appIndex); + ImageError DestroyImageByImageInfo(std::shared_ptr imageInfo); + ImageError DestroyImageForFault(std::shared_ptr appRecord); int32_t HandleForkAll(int32_t pid); ImageError HandleForkAllInner(std::shared_ptr appRecord, int32_t pid); - void HandleMakeImageTimeout(const std::string& bundleName, int32_t userId, int32_t appIndex); + void HandleMakeImageTimeout(const std::string& bundleName, const std::string& abilityName, + int32_t userId, int32_t appIndex); void CheckMakeImageState(std::shared_ptr appRecord, ImageError error); - void HandleMakeImageFailed(const PreloadRequest& request, ImageError error); - void HandleMakeImageFailed(const std::string& bundleName, int32_t userId, int32_t appIndex, ImageError err); + void HandleMakeImageFailed(std::shared_ptr appRecord, ImageError error); + void HandleMakeImageFailed(const std::string& bundleName, const std::string& abilityName, + int32_t userId, int32_t appIndex, ImageError err); /** * ApplicationForegrounded, set the application to Foreground State. @@ -1630,6 +1636,8 @@ public: */ int32_t IsProcessCacheSupported(int32_t pid, bool &isSupported); + int32_t IsChildProcessSupported(bool isNative, bool &isSupported); + int32_t SetProcessCacheEnable(int32_t pid, bool enable); int32_t LockProcessCache(int32_t pid, bool isLock); @@ -1786,6 +1794,9 @@ public: */ int32_t GetAllAbilityInfos(const int32_t pid, std::vector &infos); + int32_t EnableDelayedProcessExit(int32_t pid, bool enabled); + + void CancelDelayedExitTask(int32_t pid); private: int32_t ForceKillApplicationInner(const std::string &bundleName, const int userId = -1, const int appIndex = 0); @@ -2346,6 +2357,8 @@ private: int32_t SubmitDfxFaultTask(const FaultData &faultData, const std::string &bundleName, const std::shared_ptr &appRecord, const int32_t pid); void AddAbilityStageForSpecified(std::shared_ptr appRecord); + void ReportAbilityStartInfoForSpecified(std::shared_ptr appRecord, + const AbilityInfo &abilityInfo); void GetKernelPermissions(uint32_t accessTokenId, JITPermissionsMap &permissionsMap); void SendAppSpawnUninstallDebugHapMsg(int32_t userId); std::shared_ptr CreateAppRunningRecord(std::shared_ptr appInfo, @@ -2384,15 +2397,23 @@ private: sptr errorHandler, const PreloadRequest& preloadRequest); void SetTemplatePid(std::shared_ptr appRecord); void UpdateImageInfo(int32_t imagePid, uint64_t checkpointId, std::shared_ptr appRecord); - void RemoveImageInfo(const std::string &bundleName, int32_t userId, int32_t appIndex); + void RemoveImageInfo(std::shared_ptr appRecord); + void RemoveImageInfo(const std::string &bundleName, const std::string &abilityName, + int32_t userId, int32_t appIndex); void RemoveImageInfoByCheckpointId(uint64_t checkpointId); bool IsImageInfoExist(std::shared_ptr appRecord); - bool IsImageInfoExist(const std::string &bundleName, int32_t userId, int32_t appIndex); - std::shared_ptr GetImageInfo(const std::string &bundleName, int32_t userId, int32_t appIndex); + bool IsImageInfoExist(const std::string &bundleName, const std::string &abilityName, + int32_t userId, int32_t appIndex); + std::shared_ptr GetImageInfo(std::shared_ptr appRecord); + std::shared_ptr GetImageInfo(const std::string &bundleName, const std::string &abilityName, + int32_t userId, int32_t appIndex); + std::shared_ptr FindImageInfo( + const std::string &bundleName, const std::string &abilityName, int32_t userId, int32_t appIndex); std::shared_ptr GetImageInfoByCheckPointId(uint64_t checkpointId); - std::shared_ptr GetImageInfoByUid(int32_t uid); + std::list> GetImageInfosByUid(int32_t uid); std::shared_ptr GetImageInfoByRemoteObject(sptr object); - bool IsImageMakeSuccess(const std::string &bundleName, int32_t userId, int32_t appIndex); + bool IsImageMakeSuccess(const std::string &bundleName, const std::string &abilityName, + int32_t userId, int32_t appIndex); bool IsImageInfoMatched(std::shared_ptr imageInfo, int32_t appIndex, const std::string &processName, const std::string &instanceKey, const std::string &specifiedProcessFlag, const std::string &customProcessFlag); void RemoveImageDeathRecipient(std::shared_ptr imageInfo); diff --git a/services/appmgr/include/app_preloader.h b/services/appmgr/include/app_preloader.h index 339da5dddb..980d55917b 100644 --- a/services/appmgr/include/app_preloader.h +++ b/services/appmgr/include/app_preloader.h @@ -34,7 +34,11 @@ struct PreloadRequest { BundleInfo bundleInfo; HapModuleInfo hapModuleInfo; PreloadPhase preloadPhase = PreloadPhase::UNSPECIFIED; + ExtensionProcessMode extensionProcessMode = ExtensionProcessMode::UNDEFINED; + std::string imageName; + std::string abilityName; bool needMakeImage = false; + bool needDestroyTemplate = false; }; class AppPreloader { @@ -44,7 +48,7 @@ public: int32_t GeneratePreloadRequest(const std::string &bundleName, int32_t userId, int32_t appIndex, PreloadRequest &request); - int32_t GeneratePreloadExtensionRequest(const AAFwk::Want &want, const AbilityInfo &abilityInfo, + int32_t GeneratePreloadExtensionRequest(const AAFwk::Want &want, int32_t userId, int32_t appIndex, PreloadRequest &request); bool PreCheck(const std::string &bundleName, PreloadMode mode); @@ -54,6 +58,9 @@ private: bool GetLaunchAbilityInfo(const AAFwk::Want &want, int32_t userId, AbilityInfo &abilityInfo); + bool GetAbilityInfo(const AAFwk::Want &want, int32_t userId, int32_t appIndex, + PreloadRequest &request, AbilityInfo &abilityInfo); + bool GetBundleAndHapInfo(const std::string &bundleName, int32_t userId, const AbilityInfo &abilityInfo, BundleInfo &bundleInfo, HapModuleInfo &hapModuleInfo); diff --git a/services/appmgr/include/app_running_manager.h b/services/appmgr/include/app_running_manager.h index d80a8aa844..e5752d9d97 100644 --- a/services/appmgr/include/app_running_manager.h +++ b/services/appmgr/include/app_running_manager.h @@ -117,6 +117,8 @@ public: std::shared_ptr FindMasterProcessAppRunningRecord(const std::string &appName, const AppExecFwk::AbilityInfo &abilityInfo, const int uid); + std::shared_ptr FindMainProcessAppRunningRecord(const int uid); + bool CheckMasterProcessAppRunningRecordIsExist(const std::string &appName, const AppExecFwk::AbilityInfo &abilityInfo, const int uid); diff --git a/services/appmgr/include/app_running_record.h b/services/appmgr/include/app_running_record.h index a4aa9080ce..698a67a540 100644 --- a/services/appmgr/include/app_running_record.h +++ b/services/appmgr/include/app_running_record.h @@ -434,6 +434,13 @@ public: */ void ScheduleTerminate(); + /** + * ScheduleTerminateByDelayed, Notify application to terminate by delayed. + * + * @return + */ + void ScheduleTerminateByDelayed(bool isLastUIAbility); + /** * ScheduleTerminate, Notify application process exit safely. * @@ -978,6 +985,10 @@ public: std::string GetPreloadModuleName() const; + void SetPreloadAbilityName(const std::string &abilityName); + + std::string GetPreloadAbilityName() const; + /** * @brief Obtains the app record assign tokenId. * @@ -1230,26 +1241,6 @@ public: std::optional IsSupportMultiProcessDeviceFeature() const; void SetSupportMultiProcessDeviceFeature(bool support); - inline void SetArkChildProcessSupported(bool supported) - { - isArkChildProcessSupported_ = supported; - } - - inline bool IsArkChildProcessSupported() const - { - return isArkChildProcessSupported_; - } - - inline void SetNativeChildProcessSupported(bool supported) - { - isNativeChildProcessSupported_ = supported; - } - - inline bool IsNativeChildProcessSupported() const - { - return isNativeChildProcessSupported_; - } - inline void SetMasterProcess(bool isMasterProcess) { isMasterProcess_ = isMasterProcess; @@ -1321,6 +1312,16 @@ public: bool IsLastAgentExtensionAbility(const sptr &token); + inline void EnableDelayedProcessExit(bool enabled) + { + delayedProcessExitEnabled_ = enabled; + } + + inline bool IsDelayedProcessExitEnabled() const + { + return delayedProcessExitEnabled_; + } + private: /** * SearchTheModuleInfoNeedToUpdated, Get an uninitialized abilityStage data. @@ -1443,7 +1444,7 @@ private: ApplicationPendingState pendingState_ = ApplicationPendingState::READY; ApplicationScheduleState scheduleState_ = ApplicationScheduleState::SCHEDULE_READY; WatchdogVisibilityState watchdogVisibilityState_ = WatchdogVisibilityState::WATCHDOG_STATE_READY; - ProcessChangeReason processChangeReason_ = ProcessChangeReason::REASON_NONE; // render record + std::atomic processChangeReason_ = ProcessChangeReason::REASON_NONE; // render record std::chrono::system_clock::time_point preloadAttachTimeoutStartTime_; MakeImageState makeImageState_ = MakeImageState::NONE; @@ -1463,10 +1464,10 @@ private: int32_t callerTokenId_ = -1; int32_t callerUid_ = -1; int32_t exitReason_ = 0; - int32_t pssValue_ = 0; + std::atomic_int32_t pssValue_ = 0; std::atomic isUIExtensionPreload_ = false; int32_t requestProcCode_ = 0; // render record - int32_t rssValue_ = 0; + std::atomic_int32_t rssValue_ = 0; int32_t killId_ = -1; int restartResidentProcCount_ = 0; pid_t gpuPid_ = 0; @@ -1483,6 +1484,7 @@ private: std::string moduleName_; std::string perfCmd_; std::string preloadModuleName_; + std::string preloadAbilityName_; // ability name for preloading specific ability std::string exitMsg_ = ""; std::string instanceKey_; // render record std::string killReason_ = ""; @@ -1498,8 +1500,6 @@ private: bool isContinuousTask_ = false; // Only continuesTask processes can be set to true, please choose carefully bool isDebugApp_ = false; bool isDebugFromLocal_ = false; - bool isArkChildProcessSupported_ = false; - bool isNativeChildProcessSupported_ = false; bool isDependedOnArkWeb_ = false; bool isEmptyKeepAliveApp_ = false; // Only empty resident processes can be set to true, please choose carefully bool isErrorInfoEnhance_ = false; @@ -1511,18 +1511,19 @@ private: bool isKeepAliveDkv_ = false; // Only non-resident keep-alive processes can be set to true, please choose carefully bool isKia_ = false; bool isMainElementRunning_ = false; - bool isMainProcess_ = true; // Only MasterProcess can be keepalive - bool isMasterProcess_ = false; // Only MainProcess can be keepalive + std::atomic_bool isMainProcess_ = true; // Only MainProcess can be keepalive + bool isMasterProcess_ = false; // Only MasterProcess can be keepalive bool isMultiThread_ = false; bool isNativeDebug_ = false; bool isNativeStart_ = false; bool isNeedLimitPrio_ = false; bool isNeedPreloadModule_ = false; + bool delayedProcessExitEnabled_ = false; bool isPrepareExit_ = false; bool isRestartApp_ = false; // Only app calling RestartApp can be set to true bool isSingleton_ = false; bool isStageBasedModel_ = false; - bool isStrictMode_ = false; + std::atomic_bool isStrictMode_ = false; bool isTerminating = false; bool isUnSetPermission_ = false; bool isUserRequestCleaning_ = false; diff --git a/services/appmgr/include/app_spawn_client.h b/services/appmgr/include/app_spawn_client.h index f77659fb00..9f3ddf98bc 100644 --- a/services/appmgr/include/app_spawn_client.h +++ b/services/appmgr/include/app_spawn_client.h @@ -63,6 +63,7 @@ struct AppSpawnStartMsg { std::string processType = ""; std::string extensionTypeName; std::string appSignType; + std::string imageName; HspList hspList; // list of harmony shared package std::set permissions; std::map appEnv; // environment variable to be set to the process @@ -121,7 +122,7 @@ struct StartFlags { static const int DLP_MANAGER_FULL_CONTROL = 37; static const int DLP_MANAGER_READ_ONLY = 38; static const int CLOUD_FILE_SYNC_ENABLED = 39; - static const int SPAWN_IMAGE_PROCESS = 41; + static const int APP_FLAGS_DEBUGSERVER = 44; }; struct CreateStartMsgParam { diff --git a/services/appmgr/include/fork_image_info.h b/services/appmgr/include/fork_image_info.h index 5d4c9542a2..a6c62196d7 100644 --- a/services/appmgr/include/fork_image_info.h +++ b/services/appmgr/include/fork_image_info.h @@ -33,6 +33,9 @@ struct ForkImageInfo { int32_t imagePid = -1; uint64_t checkpointId = 0; int32_t templatePid = -1; + bool needDestroyTemplate = false; // Whether to destroy template process after image creation + std::string imageName; + std::string abilityName; std::shared_ptr abilityInfo = nullptr; BundleInfo bundleInfo; HapModuleInfo hapModuleInfo; diff --git a/services/appmgr/include/utils/appspawn_util.h b/services/appmgr/include/utils/appspawn_util.h index 7c6fa0d914..4aeedf4a1d 100644 --- a/services/appmgr/include/utils/appspawn_util.h +++ b/services/appmgr/include/utils/appspawn_util.h @@ -88,7 +88,9 @@ static uint64_t BuildStartFlags(const AAFwk::Want &want, const ApplicationInfo & if (applicationInfo.appProvisionType == AppExecFwk::Constants::APP_PROVISION_TYPE_DEBUG) { startFlags = startFlags | (START_FLAG_BASE << APP_FLAGS_DEBUG_SIGN); } - startFlags = startFlags | (START_FLAG_BASE << APP_FLAGS_SKILLS); + if (applicationInfo.skillEnabled) { + startFlags = startFlags | (START_FLAG_BASE << APP_FLAGS_SKILLS); + } return startFlags; } diff --git a/services/appmgr/src/app_mgr_service.cpp b/services/appmgr/src/app_mgr_service.cpp index 72c2ffd341..338962b4b8 100644 --- a/services/appmgr/src/app_mgr_service.cpp +++ b/services/appmgr/src/app_mgr_service.cpp @@ -1942,6 +1942,16 @@ int32_t AppMgrService::IsProcessCacheSupported(int32_t pid, bool &isSupported) return appMgrServiceInner_->IsProcessCacheSupported(pid, isSupported); } +int32_t AppMgrService::IsChildProcessSupported(bool isNative, bool &isSupported) +{ + TAG_LOGD(AAFwkTag::APPMGR, "IsChildProcessSupported called"); + if (!IsReady()) { + TAG_LOGE(AAFwkTag::APPMGR, "not ready"); + return ERR_INVALID_OPERATION; + } + return appMgrServiceInner_->IsChildProcessSupported(isNative, isSupported); +} + int32_t AppMgrService::SetProcessCacheEnable(int32_t pid, bool enable) { TAG_LOGD(AAFwkTag::APPMGR, "set enable process cache"); @@ -2314,5 +2324,25 @@ int32_t AppMgrService::GetAllAbilityInfos(const int32_t pid, std::vectorGetAllAbilityInfos(pid, infos); } + +int32_t AppMgrService::EnableDelayedProcessExit(int32_t pid, bool enabled) +{ + HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__); + if (!IsReady()) { + TAG_LOGE(AAFwkTag::APPMGR, "Service not ready"); + return ERR_INVALID_OPERATION; + } + return appMgrServiceInner_->EnableDelayedProcessExit(pid, enabled); +} + +void AppMgrService::CancelDelayedExitTask(int32_t pid) +{ + HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__); + if (!IsReady()) { + TAG_LOGE(AAFwkTag::APPMGR, "Service not ready"); + return; + } + appMgrServiceInner_->CancelDelayedExitTask(pid); +} } // namespace AppExecFwk } // namespace OHOS diff --git a/services/appmgr/src/app_mgr_service_inner.cpp b/services/appmgr/src/app_mgr_service_inner.cpp index 71235c27ba..48035189ba 100644 --- a/services/appmgr/src/app_mgr_service_inner.cpp +++ b/services/appmgr/src/app_mgr_service_inner.cpp @@ -307,7 +307,6 @@ constexpr const char* EVENT_MESSAGE_DEFAULT = "AppMgrServiceInner HandleTimeOut! constexpr const char* SYSTEM_BASIC = "system_basic"; constexpr const char* SYSTEM_CORE = "system_core"; -constexpr const char* ABILITY_OWNER_USERID = "AbilityMS_Owner_UserId"; constexpr const char* PROCESS_EXIT_EVENT_TASK = "Send Process Exit Event Task"; constexpr const char* KILL_PROCESS_REASON_PREFIX = "Kill Reason:"; constexpr const char* PRELOAD_APPLIATION_TASK = "PreloadApplicactionTask"; @@ -316,6 +315,7 @@ constexpr const char* KEY_WATERMARK_BUSINESS_NAME = "com.ohos.param.watermarkBus constexpr const char* KEY_IS_WATERMARK_ENABLED = "com.ohos.param.isWatermarkEnabled"; constexpr const char* KILL_SUB_PROCESS_REASON_PREFIX = "Kill SubProcess Reason:"; constexpr const char* MAKE_IMAGE_TIMEOUT_EVENT = "MakeImageTimeout"; +constexpr const char* NEED_DESTROY_TEMPLATE = "ohos.ability.runtime.needDestroyTemplate"; constexpr const char* PROC_SELF_TASK_PATH = "/proc/self/task/"; constexpr const char* DLP_INDEX = "ohos.dlp.params.index"; @@ -646,6 +646,7 @@ int32_t AppMgrServiceInner::PreloadApplication(const AAFwk::Want &want, int32_t { HITRACE_METER_NAME(HITRACE_TAG_APP, __PRETTY_FUNCTION__); std::string bundleName = want.GetBundle(); + std::string abilityName = want.GetElement().GetAbilityName(); TAG_LOGI(AAFwkTag::APPMGR, "PreloadApplication %{public}s#%{public}d userId:%{public}d \ preloadMode:%{public}d preloadPhase:%{public}d", bundleName.c_str(), appIndex, userId, static_cast(preloadMode), static_cast(preloadPhase)); @@ -665,7 +666,7 @@ int32_t AppMgrServiceInner::PreloadApplication(const AAFwk::Want &want, int32_t return ERR_INVALID_OPERATION; } bool skipPreCheck = (preloadMode == AppExecFwk::PreloadMode::PRESS_DOWN) ? - IsImageMakeSuccess(bundleName, userId, appIndex) : false; + IsImageMakeSuccess(bundleName, abilityName, userId, appIndex) : false; if (!skipPreCheck && !appPreloader_->PreCheck(bundleName, preloadMode)) { TAG_LOGI(AAFwkTag::APPMGR, "bundleName: %{public}s preload preCheck:unallow", bundleName.c_str()); return AAFwk::ERR_NOT_ALLOW_PRELOAD_BY_RSS; @@ -675,33 +676,51 @@ int32_t AppMgrServiceInner::PreloadApplication(const AAFwk::Want &want, int32_t request.preloadMode = preloadMode; request.preloadPhase = preloadPhase; request.needMakeImage = needMakeImage; - auto element = want.GetElement(); + request.abilityName = abilityName; + // Check if need to destroy template process after image creation + if (want.HasParameter(NEED_DESTROY_TEMPLATE)) { + request.needDestroyTemplate = want.GetBoolParam(NEED_DESTROY_TEMPLATE, false); + const_cast(want).RemoveParam(NEED_DESTROY_TEMPLATE); + TAG_LOGI(AAFwkTag::APPMGR, "needDestroyTemplate: %{public}d", request.needDestroyTemplate); + } int32_t ret {0}; - if (element.GetAbilityName().empty()) { + if (abilityName.empty()) { ret = appPreloader_->GeneratePreloadRequest(bundleName, userId, appIndex, request); } else { - AbilityInfo abilityInfo; - if (!CreateAbilityInfo(want, abilityInfo)) { - TAG_LOGE(AAFwkTag::APPMGR, "createAbilityInfo fail"); - return ERR_INVALID_OPERATION; - } - ret = appPreloader_->GeneratePreloadExtensionRequest(want, abilityInfo, userId, appIndex, request); + ret = appPreloader_->GeneratePreloadExtensionRequest(want, userId, appIndex, request); } if (ret != ERR_OK) { TAG_LOGE(AAFwkTag::APPMGR, "generatePreloadRequest fail, ret:%{public}d", ret); return ret; } if (needMakeImage) { - if (request.abilityInfo && request.abilityInfo->isolationProcess) { + if (request.abilityInfo == nullptr) { + return ERR_INVALID_VALUE; + } + if (request.abilityInfo->isolationProcess) { TAG_LOGE(AAFwkTag::APPMGR, "not support isolationProcess"); return ERR_INVALID_VALUE; } + if (AAFwk::UIExtensionWrapper::IsUIExtension(request.abilityInfo->extensionAbilityType)) { + switch (request.extensionProcessMode) { + case ExtensionProcessMode::UNDEFINED: + case ExtensionProcessMode::BUNDLE: + break; + case ExtensionProcessMode::TYPE: + request.abilityInfo->process = bundleName + ":" + abilityName; + break; + default: + TAG_LOGE(AAFwkTag::APPMGR, "UIExtension only supports BUNDLE/TYPE mode for image creation"); + return ERR_INVALID_VALUE; + } + } + request.imageName = abilityName.empty() ? bundleName : (bundleName + ":" + abilityName); auto imageInfoId = PreAddImageInfo(bundleName, userId, appIndex, errorHandler, request); - auto timeoutTask = [innerServiceWeak = weak_from_this(), bundleName, userId, appIndex] () { + auto timeoutTask = [innerServiceWeak = weak_from_this(), bundleName, abilityName, userId, appIndex] () { TAG_LOGE(AAFwkTag::APPMGR, "make image time out"); auto innerService = innerServiceWeak.lock(); CHECK_POINTER_AND_RETURN_LOG(innerService, "get appMgrServiceInner fail"); - innerService->HandleMakeImageTimeout(bundleName, userId, appIndex); + innerService->HandleMakeImageTimeout(bundleName, abilityName, userId, appIndex); }; std::string taskName = std::string(MAKE_IMAGE_TIMEOUT_EVENT) + std::to_string(imageInfoId); TAG_LOGI(AAFwkTag::APPMGR, "submit task:%{public}s", taskName.c_str()); @@ -761,15 +780,17 @@ ImageError AppMgrServiceInner::MakeImageInner(const AAFwk::Want &want, int32_t u AppExecFwk::PreloadMode preloadMode, int32_t appIndex, sptr errorHandler) { std::string bundleName = want.GetBundle(); - TAG_LOGI(AAFwkTag::APPMGR, "make image, bundleName:%{public}s, userId:%{public}d, appIndex:%{public}d", - bundleName.c_str(), userId, appIndex); + std::string abilityName = want.GetElement().GetAbilityName(); + TAG_LOGI(AAFwkTag::APPMGR, "make image, bundleName:%{public}s, abilityName:%{public}s, userId:%{public}d, appIndex:%{public}d", + bundleName.c_str(), abilityName.c_str(), userId, appIndex); if (preloadMode != PreloadMode::PRELOAD_MODULE) { TAG_LOGE(AAFwkTag::APPMGR, "only support preloadModule"); return ImageError::ERR_INVALID_PRELOAD_TYPE; } userId = GetValidUserId(userId); - if (IsImageInfoExist(bundleName, userId, appIndex)) { + // Check image existence: if abilityName is empty, it's a general image + if (IsImageInfoExist(bundleName, abilityName, userId, appIndex)) { TAG_LOGE(AAFwkTag::APPMGR, "image exist"); return ImageError::ERR_IMAGE_INFO_EXIST; } @@ -784,13 +805,13 @@ void AppMgrServiceInner::DestroyImage(uint64_t checkpointId, sptr errorHandler) +ImageError AppMgrServiceInner::DestroyImageByCheckpointId(uint64_t checkpointId) { HITRACE_METER_NAME(HITRACE_TAG_APP, __PRETTY_FUNCTION__); std::lock_guard guard(imageSerialLock_); @@ -810,10 +831,20 @@ ImageError AppMgrServiceInner::DestroyImageInner(uint64_t checkpointId, sptr imageInfo) { HITRACE_METER_NAME(HITRACE_TAG_APP, __PRETTY_FUNCTION__); std::lock_guard guard(imageSerialLock_); - auto imageInfo = GetImageInfoByUid(uid); if (imageInfo == nullptr) { return ImageError::ERR_IMAGE_INFO_NOT_EXIST; } @@ -822,9 +853,8 @@ ImageError AppMgrServiceInner::DestroyImageForUninstallOrUpgrade(int32_t uid) if (imagePid < 0 || appRecord == nullptr) { return ImageError::ERR_IMAGE_INFO_NOT_READY; } - TAG_LOGI(AAFwkTag::APPMGR, "destroy image, uid:%{public}d", uid); RemoveImageDeathRecipient(imageInfo); - RemoveImageInfo(appRecord->GetBundleName(), appRecord->GetUserId(), appRecord->GetAppIndex()); + RemoveImageInfo(appRecord); auto ret = KillImageProcess(imageInfo->checkpointId); if (ret != ERR_OK) { TAG_LOGE(AAFwkTag::APPMGR, "kill process failed"); @@ -835,24 +865,29 @@ ImageError AppMgrServiceInner::DestroyImageForUninstallOrUpgrade(int32_t uid) return ImageError::ERR_OK; } -ImageError AppMgrServiceInner::DestroyImageForFault(const std::string& bundleName, int32_t userId, int32_t appIndex) +ImageError AppMgrServiceInner::DestroyImageForFault(const std::shared_ptr appRecord) { HITRACE_METER_NAME(HITRACE_TAG_APP, __PRETTY_FUNCTION__); std::lock_guard guard(imageSerialLock_); - TAG_LOGD(AAFwkTag::APPMGR, "%{public}s %{public}s_%{public}d_%{public}d", - __func__, bundleName.c_str(), userId, appIndex); - auto imageInfo = GetImageInfo(bundleName, userId, appIndex); + if (appRecord == nullptr) { + TAG_LOGE(AAFwkTag::APPMGR, "null appRecord"); + return ImageError::ERR_INNER; + } + TAG_LOGD(AAFwkTag::APPMGR, "%{public}s %{public}s[%{public}s]_%{public}d_%{public}d", + __func__, appRecord->GetBundleName().c_str(), appRecord->GetPreloadAbilityName().c_str(), + appRecord->GetUserId(), appRecord->GetAppIndex()); + auto imageInfo = GetImageInfo(appRecord); if (imageInfo == nullptr) { return ImageError::ERR_IMAGE_INFO_NOT_EXIST; } auto imagePid = imageInfo->imagePid; - auto appRecord = imageInfo->baseAppRecord; - if (imagePid < 0 || appRecord == nullptr) { + auto baseAppRecord = imageInfo->baseAppRecord; + if (imagePid < 0 || baseAppRecord == nullptr) { return ImageError::ERR_IMAGE_INFO_NOT_READY; } TAG_LOGI(AAFwkTag::APPMGR, "destroy image, bundleName:%{public}s imagePid:%{public}d", - bundleName.c_str(), imagePid); - RemoveImageInfo(bundleName, userId, appIndex); + appRecord->GetBundleName().c_str(), imagePid); + RemoveImageInfo(appRecord); auto ret = KillImageProcess(imageInfo->checkpointId); if (ret != ERR_OK) { TAG_LOGE(AAFwkTag::APPMGR, "kill process failed"); @@ -911,7 +946,7 @@ int32_t AppMgrServiceInner::HandleForkAll(int32_t pid) auto ret = HandleForkAllInner(appRecord, pid); if (ret != ImageError::ERR_OK) { appRecord->SetMakeImageState(MakeImageState::NONE); - HandleMakeImageFailed(appRecord->GetBundleName(), appRecord->GetUserId(), appRecord->GetAppIndex(), ret); + HandleMakeImageFailed(appRecord, ret); return -1; } return ERR_OK; @@ -926,7 +961,7 @@ ImageError AppMgrServiceInner::HandleForkAllInner(std::shared_ptrGetBundleName(), appRecord->GetUserId(), appRecord->GetAppIndex()); + auto imageInfo = GetImageInfo(appRecord); CHECK_POINTER_AND_RETURN_VALUE(imageInfo, ImageError::ERR_INNER); auto appScheduler = appRecord->GetApplicationClient(); CHECK_POINTER_AND_RETURN_VALUE(appScheduler, ImageError::ERR_INNER); @@ -940,7 +975,8 @@ ImageError AppMgrServiceInner::HandleForkAllInner(std::shared_ptrGetStartMsg(); startMsg.code = static_cast(MSG_SPAWN_IMAGE_PROCESS); startMsg.templatePid = pid; - startMsg.flags |= (START_FLAG_BASE << StartFlags::SPAWN_IMAGE_PROCESS); + startMsg.flags |= (START_FLAG_BASE << AppFlagsIndex::APP_FLAGS_SPAWN_IMAGE_PROCESS); + startMsg.imageName = imageInfo->imageName; auto errCode = remoteClientManager_->GetSpawnClient()->StartImageProcess(startMsg, imagePid, checkpointId); TAG_LOGI(AAFwkTag::APPMGR, "forkall after preload, name:%{public}s, errCode:%{public}d," " pid:%{public}d, imagePid:%{public}d", appRecord->GetProcessName().c_str(), errCode, pid, imagePid); @@ -957,19 +993,37 @@ ImageError AppMgrServiceInner::HandleForkAllInner(std::shared_ptrCancelTask(taskName); DelayedSingleton::GetInstance()->OnImageProcessStateChanged( imageInfo, ImageProcessState::IMAGE_PROCESS_CREATE); + + // Check if need to destroy template process after image creation + if (imageInfo->needDestroyTemplate) { + TAG_LOGI(AAFwkTag::APPMGR, "Destroying template process after image creation, templatePid:%{public}d", + imageInfo->templatePid); + auto appRecord = GetAppRunningRecordByPid(pid); + if (appRecord) { + appRecord->SetKilling(); + } + int32_t ret = KillProcessByPid(imageInfo->templatePid, "Destroy template after image creation"); + if (ret != ERR_OK) { + TAG_LOGW(AAFwkTag::APPMGR, "Failed to destroy template process, ret:%{public}d", ret); + } else { + TAG_LOGI(AAFwkTag::APPMGR, "Successfully destroyed template process"); + } + } + return ImageError::ERR_OK; } -void AppMgrServiceInner::HandleMakeImageTimeout(const std::string& bundleName, int32_t userId, int32_t appIndex) +void AppMgrServiceInner::HandleMakeImageTimeout(const std::string& bundleName, const std::string& abilityName, + int32_t userId, int32_t appIndex) { if (taskHandler_ == nullptr) { TAG_LOGE(AAFwkTag::APPMGR, "taskHandler_ null"); return; } - auto task = [weakService = weak_from_this(), bundleName, userId, appIndex]() { + auto task = [weakService = weak_from_this(), bundleName, abilityName, userId, appIndex]() { auto innerService = weakService.lock(); if (innerService) { - innerService->HandleMakeImageFailed(bundleName, userId, appIndex, ImageError::ERR_TIMEOUT); + innerService->HandleMakeImageFailed(bundleName, abilityName, userId, appIndex, ImageError::ERR_TIMEOUT); } }; taskHandler_->SubmitTask(task, AAFwk::TaskAttribute{ @@ -983,28 +1037,26 @@ void AppMgrServiceInner::CheckMakeImageState(std::shared_ptr a CHECK_POINTER_AND_RETURN_LOG(appRecord, "appInfo null"); if (appRecord->GetMakeImageState() > MakeImageState::NONE && appRecord->GetMakeImageState() < MakeImageState::MAKE_IMAGE_FINISH) { - HandleMakeImageFailed(appRecord->GetBundleName(), appRecord->GetUserId(), - appRecord->GetAppIndex(), error); + HandleMakeImageFailed(appRecord, error); } appRecord->SetMakeImageState(MakeImageState::NONE); } -void AppMgrServiceInner::HandleMakeImageFailed(const PreloadRequest& request, ImageError error) +void AppMgrServiceInner::HandleMakeImageFailed(std::shared_ptr appRecord, ImageError error) { - if (!request.needMakeImage) { + if (appRecord == nullptr) { return; } - auto appInfo = request.appInfo; - CHECK_POINTER_AND_RETURN_LOG(appInfo, "appInfo null"); - HandleMakeImageFailed(appInfo->bundleName, appInfo->uid / BASE_USER_RANGE, request.appIndex, error); + HandleMakeImageFailed(appRecord->GetBundleName(), appRecord->GetPreloadAbilityName(), + appRecord->GetUserId(), appRecord->GetAppIndex(), error); } -void AppMgrServiceInner::HandleMakeImageFailed(const std::string& bundleName, int32_t userId, int32_t appIndex, - ImageError err) +void AppMgrServiceInner::HandleMakeImageFailed(const std::string& bundleName, const std::string& abilityName, + int32_t userId, int32_t appIndex, ImageError err) { HITRACE_METER_NAME(HITRACE_TAG_APP, __PRETTY_FUNCTION__); TAG_LOGI(AAFwkTag::APPMGR, "HandleMakeImageFailed, bundleName:%{public}s", bundleName.c_str()); - auto imageInfo = GetImageInfo(bundleName, userId, appIndex); + auto imageInfo = GetImageInfo(bundleName, abilityName, userId, appIndex); if (imageInfo == nullptr) { TAG_LOGE(AAFwkTag::APPMGR, "null imageInfo"); return; @@ -1019,15 +1071,18 @@ void AppMgrServiceInner::HandleMakeImageFailed(const std::string& bundleName, in AAFwk::TaskHandlerWrap::GetFfrtHandler()->CancelTask(taskName); } UnMarkTemplateProcess(imageInfo->templatePid); - RemoveImageInfo(bundleName, userId, appIndex); + RemoveImageInfo(bundleName, abilityName, userId, appIndex); NotifyImageOperationFailed(imageInfo->errorHandler, err); } int32_t AppMgrServiceInner::PreAddImageInfo(const std::string& bundleName, int32_t userId, int32_t appIndex, sptr errorHandler, const PreloadRequest& preloadRequest) { + // For general image, abilityName in request should be empty + // For ability-specific image, use the abilityName from want MakeImageRequest request { .bundleName = bundleName, + .abilityName = preloadRequest.abilityName, .userId = userId, .appCloneIndex = appIndex, .preloadMode = preloadRequest.preloadMode @@ -1038,6 +1093,10 @@ int32_t AppMgrServiceInner::PreAddImageInfo(const std::string& bundleName, int32 imageInfo->hapModuleInfo = preloadRequest.hapModuleInfo; imageInfo->want = preloadRequest.want; imageInfo->errorHandler = errorHandler; + imageInfo->imageName = preloadRequest.imageName; + imageInfo->abilityName = preloadRequest.abilityName; + imageInfo->needDestroyTemplate = preloadRequest.needDestroyTemplate; + sptr appRefreshRecipient = sptr::MakeSptr(); if (appRefreshRecipient) { appRefreshRecipient->SetTaskHandler(taskHandler_); @@ -1045,8 +1104,8 @@ int32_t AppMgrServiceInner::PreAddImageInfo(const std::string& bundleName, int32 } imageInfo->appRefreshRecipient = appRefreshRecipient; std::lock_guard guard(imageInfoLock_); - TAG_LOGI(AAFwkTag::APPMGR, "add image info, b_n:%{public}s, u:%{public}d, a:%{public}d, size:%{public}zu", - bundleName.c_str(), userId, appIndex, imageInfoMap_.size()); + TAG_LOGI(AAFwkTag::APPMGR, "add image info, b_n:%{public}s, a_n:%{public}s, u:%{public}d, a:%{public}d, size:%{public}zu", + bundleName.c_str(), preloadRequest.abilityName.c_str(), userId, appIndex, imageInfoMap_.size()); imageInfoMap_.emplace(request, imageInfo); return imageInfo->imageInfoId; } @@ -1056,6 +1115,7 @@ void AppMgrServiceInner::SetTemplatePid(std::shared_ptr appRec CHECK_POINTER_AND_RETURN_LOG(appRecord, "appRecord null"); MakeImageRequest request { .bundleName = appRecord->GetBundleName(), + .abilityName = appRecord->GetPreloadAbilityName(), .userId = appRecord->GetUserId(), .appCloneIndex = appRecord->GetAppIndex() }; @@ -1073,6 +1133,7 @@ void AppMgrServiceInner::UpdateImageInfo(int32_t imagePid, uint64_t checkpointId CHECK_POINTER_AND_RETURN_LOG(appRecord, "appRecord null"); MakeImageRequest request { .bundleName = appRecord->GetBundleName(), + .abilityName = appRecord->GetPreloadAbilityName(), .userId = appRecord->GetUserId(), .appCloneIndex = appRecord->GetAppIndex() }; @@ -1094,10 +1155,21 @@ void AppMgrServiceInner::UpdateImageInfo(int32_t imagePid, uint64_t checkpointId appRecord->SetNeedRemoveDeathRecipient(false); } -void AppMgrServiceInner::RemoveImageInfo(const std::string &bundleName, int32_t userId, int32_t appIndex) +void AppMgrServiceInner::RemoveImageInfo(std::shared_ptr appRecord) +{ + if (appRecord == nullptr) { + return; + } + RemoveImageInfo(appRecord->GetBundleName(), appRecord->GetPreloadAbilityName(), + appRecord->GetUserId(), appRecord->GetAppIndex()); +} + +void AppMgrServiceInner::RemoveImageInfo(const std::string &bundleName, const std::string &abilityName, + int32_t userId, int32_t appIndex) { MakeImageRequest request { .bundleName = bundleName, + .abilityName = abilityName, .userId = userId, .appCloneIndex = appIndex }; @@ -1129,13 +1201,16 @@ bool AppMgrServiceInner::IsImageInfoExist(std::shared_ptr appR if (appRecord == nullptr) { return false; } - return IsImageInfoExist(appRecord->GetBundleName(), appRecord->GetUserId(), appRecord->GetAppIndex()); + return IsImageInfoExist(appRecord->GetBundleName(), appRecord->GetPreloadAbilityName(), + appRecord->GetUserId(), appRecord->GetAppIndex()); } -bool AppMgrServiceInner::IsImageInfoExist(const std::string &bundleName, int32_t userId, int32_t appIndex) +bool AppMgrServiceInner::IsImageInfoExist(const std::string &bundleName, const std::string &abilityName, + int32_t userId, int32_t appIndex) { MakeImageRequest request { .bundleName = bundleName, + .abilityName = abilityName, .userId = userId, .appCloneIndex = appIndex }; @@ -1147,20 +1222,68 @@ bool AppMgrServiceInner::IsImageInfoExist(const std::string &bundleName, int32_t return true; } -std::shared_ptr AppMgrServiceInner::GetImageInfo( - const std::string &bundleName, int32_t userId, int32_t appIndex) +std::shared_ptr AppMgrServiceInner::GetImageInfo(std::shared_ptr appRecord) { - MakeImageRequest request { + if (appRecord == nullptr) { + return nullptr; + } + return GetImageInfo(appRecord->GetBundleName(), appRecord->GetPreloadAbilityName(), + appRecord->GetUserId(), appRecord->GetAppIndex()); +} + +std::shared_ptr AppMgrServiceInner::GetImageInfo( + const std::string &bundleName, const std::string &abilityName, int32_t userId, int32_t appIndex) +{ + MakeImageRequest abilityRequest { .bundleName = bundleName, + .abilityName = abilityName, .userId = userId, .appCloneIndex = appIndex }; std::lock_guard guard(imageInfoLock_); - auto iter = imageInfoMap_.find(request); - if (iter == imageInfoMap_.end()) { - return nullptr; + auto iter = imageInfoMap_.find(abilityRequest); + if (iter != imageInfoMap_.end()) { + TAG_LOGI(AAFwkTag::APPMGR, "Found image for bundle:%{public}s, ability:%{public}s", + bundleName.c_str(), abilityName.c_str()); + return iter->second; } - return iter->second; + return nullptr; +} + +std::shared_ptr AppMgrServiceInner::FindImageInfo( + const std::string &bundleName, const std::string &abilityName, int32_t userId, int32_t appIndex) +{ + // First try to find ability-specific image (exact match) + MakeImageRequest abilityRequest { + .bundleName = bundleName, + .abilityName = abilityName, + .userId = userId, + .appCloneIndex = appIndex + }; + std::lock_guard guard(imageInfoLock_); + auto iter = imageInfoMap_.find(abilityRequest); + if (iter != imageInfoMap_.end()) { + TAG_LOGI(AAFwkTag::APPMGR, "Found ability-specific image for bundle:%{public}s, ability:%{public}s", + bundleName.c_str(), abilityName.c_str()); + return iter->second; + } + + // If ability-specific image not found, try to find general image (empty abilityName) + if (!abilityName.empty()) { + MakeImageRequest generalRequest { + .bundleName = bundleName, + .abilityName = "", + .userId = userId, + .appCloneIndex = appIndex + }; + iter = imageInfoMap_.find(generalRequest); + if (iter != imageInfoMap_.end()) { + TAG_LOGI(AAFwkTag::APPMGR, "Found general image for bundle:%{public}s", bundleName.c_str()); + return iter->second; + } + } + + return nullptr; } std::shared_ptr AppMgrServiceInner::GetImageInfoByCheckPointId(uint64_t checkpointId) @@ -1177,15 +1300,16 @@ std::shared_ptr AppMgrServiceInner::GetImageInfoByCheckPointId(ui return nullptr; } -std::shared_ptr AppMgrServiceInner::GetImageInfoByUid(int32_t uid) +std::list> AppMgrServiceInner::GetImageInfosByUid(int32_t uid) { std::lock_guard guard(imageInfoLock_); + std::list> imageInfos; for (auto& item: imageInfoMap_) { if (item.second && item.second->baseAppRecord && item.second->baseAppRecord->GetUid() == uid) { - return item.second; + imageInfos.emplace_back(item.second); } } - return nullptr; + return imageInfos; } std::shared_ptr AppMgrServiceInner::GetImageInfoByRemoteObject(sptr object) @@ -1202,10 +1326,12 @@ std::shared_ptr AppMgrServiceInner::GetImageInfoByRemoteObject(sp return nullptr; } -bool AppMgrServiceInner::IsImageMakeSuccess(const std::string &bundleName, int32_t userId, int32_t appIndex) +bool AppMgrServiceInner::IsImageMakeSuccess(const std::string &bundleName, const std::string &abilityName, + int32_t userId, int32_t appIndex) { MakeImageRequest request { .bundleName = bundleName, + .abilityName = abilityName, .userId = userId, .appCloneIndex = appIndex }; @@ -1292,6 +1418,7 @@ std::shared_ptr AppMgrServiceInner::CreateAppRunningRecordFrom appRecord->SetNWebPreload(imageAppRecord->IsNWebPreload()); appRecord->SetState(ApplicationState::APP_STATE_READY); appRecord->SetRestartResidentProcCount(imageAppRecord->GetRestartResidentProcCount()); + appRecord->SetPreloadAbilityName(imageAppRecord->GetPreloadAbilityName()); // mark create form image appRecord->SetIsCreateFromImage(true); @@ -1309,10 +1436,10 @@ int32_t AppMgrServiceInner::TryToUseImageInfo(std::shared_ptr abili std::shared_ptr& appRecord) { HITRACE_METER_NAME(HITRACE_TAG_APP, __PRETTY_FUNCTION__); - if (appRunningManager_ == nullptr || appInfo == nullptr) { + if (appRunningManager_ == nullptr || appInfo == nullptr || abilityInfo == nullptr) { return ERR_OK; } - auto imageInfo = GetImageInfo(appInfo->bundleName, appInfo->uid / BASE_USER_RANGE, appIndex); + auto imageInfo = FindImageInfo(appInfo->bundleName, abilityInfo->name, appInfo->uid / BASE_USER_RANGE, appIndex); if (imageInfo == nullptr) { return ERR_OK; } @@ -1344,6 +1471,7 @@ int32_t AppMgrServiceInner::TryToUseImageInfo(std::shared_ptr abili startMsg.code = static_cast(MSG_SPAWN_WORKER_PROCESS); startMsg.imagePid = imageInfo->imagePid; startMsg.checkpointId = imageInfo->checkpointId; + startMsg.imageName = imageInfo->imageName; TAG_LOGI(AAFwkTag::APPMGR, "StartProcess"); auto errCode = remoteClientManager_->GetSpawnClient()->StartProcess(startMsg, workPid); AAFwk::ResSchedUtil::GetInstance().ReportForkAllEventToRSS(imageInfo->imagePid, appRecord->GetPid(), @@ -1428,7 +1556,7 @@ void AppMgrServiceInner::SnapshotErrorReport(int32_t uid, const std::string &bun AAFwk::EventReport::SendSnapshotEvent(AAFwk::EventName::SNAPSHOT_REPORT, snapshotInfo); } -void AppMgrServiceInner::MarkTemplateProcess(int32_t templatePid, std::string bundleName) +void AppMgrServiceInner::MarkTemplateProcess(int32_t templatePid, std::string imageName) { int fd = open(TEMPLATE_MONITOR_PATH, O_RDWR, 0); if (fd < 0) { @@ -1439,9 +1567,9 @@ void AppMgrServiceInner::MarkTemplateProcess(int32_t templatePid, std::string bu .pid = templatePid, .type = CHECKPOINT_MONITOR_APP_TYPE }; - int32_t beginIndex = bundleName.size() - (CHECKPOINT_NAME_LEN - 1); + int32_t beginIndex = static_cast(imageName.size() - (CHECKPOINT_NAME_LEN - 1)); beginIndex = beginIndex > 0 ? beginIndex : 0; - std::size_t length = bundleName.copy(mark.name, CHECKPOINT_NAME_LEN - 1, beginIndex); + std::size_t length = imageName.copy(mark.name, CHECKPOINT_NAME_LEN - 1, beginIndex); mark.name[length] = '\0'; int ret = ioctl(fd, CHECKPOINT_MONITOR_IOCTL_MARK_TEMPLATE, &mark); if (ret < 0) { @@ -1520,14 +1648,9 @@ int32_t AppMgrServiceInner::PreloadExtension(const AAFwk::Want &want, int32_t ap TAG_LOGE(AAFwkTag::APPMGR, "null appPreloader"); return ERR_INVALID_VALUE; } - AbilityInfo abilityInfo; - if (!CreateAbilityInfo(want, abilityInfo)) { - TAG_LOGE(AAFwkTag::APPMGR, "createAbilityInfo fail"); - return ERR_INVALID_OPERATION; - } PreloadRequest request; request.preloadMode = PreloadMode::PRELOAD_MODULE; - auto ret = appPreloader_->GeneratePreloadExtensionRequest(want, abilityInfo, userId, appIndex, request); + auto ret = appPreloader_->GeneratePreloadExtensionRequest(want, userId, appIndex, request); if (ret != ERR_OK) { TAG_LOGE(AAFwkTag::APPMGR, "generatePreloadRequest fail, ret:%{public}d", ret); return ret; @@ -1581,7 +1704,8 @@ void AppMgrServiceInner::HandlePreloadApplication(const PreloadRequest &request) std::string specifiedProcessFlag; if (CheckAppRecordExistByPreloadRequest(request, processName, specifiedProcessFlag)) { TAG_LOGW(AAFwkTag::APPMGR, "appRecord already exists when preload application"); - HandleMakeImageFailed(request, ImageError::ERR_APP_RECORD_EXIST); + HandleMakeImageFailed(request.appInfo->bundleName, request.abilityName, + request.appInfo->uid / BASE_USER_RANGE, request.appIndex, ImageError::ERR_APP_RECORD_EXIST); return; } @@ -1627,11 +1751,13 @@ void AppMgrServiceInner::HandlePreloadApplication(const PreloadRequest &request) request.preloadPhase == AppExecFwk::PreloadPhase::ABILITY_STAGE_CREATED); appRecord->SetNeedLimitPrio(request.preloadMode != PreloadMode::PRESS_DOWN); appRecord->SetExtensionSandBoxFlag(isExtensionSandBox); + appRecord->SetPreloadAbilityName(request.abilityName); + LoadAbilityNoAppRecord(appRecord, false, appInfo, abilityInfo, processName, specifiedProcessFlag, bundleInfo, hapModuleInfo, want, appExistFlag, true, request.preloadMode); if (request.needMakeImage && appRecord->GetPid() > 0) { SetTemplatePid(appRecord); - MarkTemplateProcess(appRecord->GetPid(), bundleInfo.name); + MarkTemplateProcess(appRecord->GetPid(), request.imageName); } appRecord->SetNeedLimitPrio(false); if (request.preloadMode == AppExecFwk::PreloadMode::PRELOAD_MODULE) { @@ -2069,6 +2195,7 @@ void AppMgrServiceInner::LoadAbility(std::shared_ptr abilityInfo, s if (loadParam->isPreloadUIExtension) { UpdateUIExtensionPreloadState(appRecord, true); } + ReportAbilityStartInfoForSpecified(appRecord, *abilityInfo); LoadAbilityNoAppRecord(appRecord, loadParam->isShellCall, appInfo, abilityInfo, processName, specifiedProcessFlag, bundleInfo, hapModuleInfo, want, appExistFlag, false, AppExecFwk::PreloadMode::PRESS_DOWN, loadParam->token, customProcessFlag, loadParam->isStartupHide); @@ -2078,6 +2205,7 @@ void AppMgrServiceInner::LoadAbility(std::shared_ptr abilityInfo, s } } else { isProcessReuse = true; + ReportAbilityStartInfoForSpecified(appRecord, *abilityInfo); HandleExistingAppRecordAfterFound(appRecord, abilityInfo, hapModuleInfo, want, isProcCache, loadParam); if (AAFwk::UIExtensionWrapper::IsUIExtension(abilityInfo->extensionAbilityType)) { AddUIExtensionBindItem(want, appRecord, loadParam->token); @@ -2140,7 +2268,7 @@ void AppMgrServiceInner::AfterLoadAbility(std::shared_ptr appR auto reportLoadTask = [appRecord, abilityRecordId = loadParam->abilityRecordId, loadTimeout = loadParam->loadTimeout, extensionType = abilityInfo->extensionAbilityType, - isProcessReuse]() { + isProcessReuse, isStartupHide = loadParam->isStartupHide, isStartedByCall = (loadParam->byCallStatus != 0)]() { auto priorityObj = appRecord->GetPriorityObject(); if (priorityObj) { auto timeOut = AppMgrServiceInner::GetLoadTimeout(loadTimeout); @@ -2152,6 +2280,9 @@ void AppMgrServiceInner::AfterLoadAbility(std::shared_ptr appR std::unordered_map eventParams; eventParams["extensionType"] = std::to_string(static_cast(extensionType)); eventParams["isProcessReuse"] = std::to_string(isProcessReuse); + bool isStartVisibleUIAbility = isProcessReuse && !isStartupHide && !isStartedByCall && + (extensionType == ExtensionAbilityType::UNSPECIFIED || static_cast(extensionType) < 0); + eventParams["isStartVisibleUIAbility"] = std::to_string(isStartVisibleUIAbility); AAFwk::ResSchedUtil::GetInstance().ReportLoadingEventToRss(AAFwk::LoadingStage::LOAD_BEGIN, priorityObj->GetPid(), appRecord->GetUid(), timeOut, static_cast(abilityRecordId), eventParams); @@ -2339,7 +2470,8 @@ void AppMgrServiceInner::MakeProcessName(const std::shared_ptr &abi } if (!abilityInfo->process.empty() && (isCallerSetProcess || specifiedProcessFlag.empty())) { TAG_LOGD(AAFwkTag::APPMGR, "Process not null"); - if (AAFwk::UIExtensionWrapper::IsUIExtension(abilityInfo->extensionAbilityType)) { + if (AAFwk::UIExtensionWrapper::IsUIExtension(abilityInfo->extensionAbilityType) || + abilityInfo->extensionAbilityType == AppExecFwk::ExtensionAbilityType::MODULAR_OBJECT) { processName = abilityInfo->process; return; } @@ -2629,13 +2761,6 @@ void AppMgrServiceInner::LaunchApplicationExt(const std::shared_ptrGetProcessName()); appRecord->SetNWebPreload(isPreload); - bool isMultiProcessModel = AAFwk::AppUtils::GetInstance().IsMultiProcessModel(); - bool isArkSupported = isMultiProcessModel || AllowChildProcessInMultiProcessFeatureApp(appRecord); - bool isNativeSupported = isMultiProcessModel || - AllowNativeChildProcess(CHILD_PROCESS_TYPE_NATIVE, appRecord->GetAppIdentifier()) || - AllowChildProcessInMultiProcessFeatureApp(appRecord); - appRecord->SetArkChildProcessSupported(isArkSupported); - appRecord->SetNativeChildProcessSupported(isNativeSupported); LaunchApplication(appRecord); } @@ -2771,6 +2896,22 @@ void AppMgrServiceInner::LaunchApplication(const std::shared_ptr appRecord, + const AbilityInfo &abilityInfo) +{ + if (appRecord == nullptr || abilityInfo.launchMode != LaunchMode::SPECIFIED) { + return; + } + auto pid = appRecord->GetPid(); + auto isSuggestCache = DelayedSingleton::GetInstance()->IsCachedProcess(appRecord); + auto preloadMode = appRecord->GetPreloadMode(); + bool supportWarmSmartGC = (isSuggestCache || + preloadMode == AppExecFwk::PreloadMode::PRE_MAKE || + preloadMode == AppExecFwk::PreloadMode::PRELOAD_MODULE); + AAFwk::ResSchedUtil::GetInstance().ReportAbilityStartInfoToRSS(abilityInfo, pid, + pid > 0, supportWarmSmartGC, static_cast(preloadMode), isSuggestCache); +} + void AppMgrServiceInner::AddAbilityStageForSpecified(std::shared_ptr appRecord) { CHECK_POINTER_AND_RETURN_LOG(appRecord, "appRecord null"); @@ -4496,7 +4637,7 @@ std::shared_ptr AppMgrServiceInner::CreateAppRunningRecord( appIndex = abilityInfo->appIndex; } appRecord->SetAppIndex(appIndex); - if (want->GetBoolParam(AbilityRuntime::GlobalConstant::GAME_PRELAUNCH, false)) { + if (loadParam->isGamePrelaunch) { appRecord->SetPreloadMode(AppExecFwk::PreloadMode::GAME_PRELAUNCH); } #ifdef WITH_DLP @@ -4510,6 +4651,13 @@ std::shared_ptr AppMgrServiceInner::CreateAppRunningRecord( appRecord->SetNativeStart(want->GetBoolParam("native", false)); appRecord->SetDebugFromLocal(want->GetBoolParam(DEBUG_FROM, false)); } + if (abilityInfo != nullptr && abilityInfo->extensionAbilityType == AppExecFwk::ExtensionAbilityType::AGENT && + appInfo != nullptr && appInfo->appProvisionType == AppExecFwk::Constants::APP_PROVISION_TYPE_DEBUG) { + auto mainAppRecord = appRunningManager_->FindMainProcessAppRunningRecord(appInfo->uid); + if (mainAppRecord && mainAppRecord->IsDebug()) { + appRecord->SetDebugApp(true); + } + } return appRecord; } @@ -4577,18 +4725,14 @@ void AppMgrServiceInner::UpdateAbilityState(const sptr &token, co OnAbilityStateChanged(abilityRecord, state); return; } - if (abilityRecord->GetAbilityInfo() == nullptr) { - TAG_LOGE(AAFwkTag::APPMGR, "info null"); - return; - } - auto type = abilityRecord->GetAbilityInfo()->type; - if (type == AppExecFwk::AbilityType::SERVICE && + auto abilityInfo = abilityRecord->GetAbilityInfo(); + CHECK_POINTER_AND_RETURN_LOG(abilityInfo, "abilityInfo null"); + if (abilityInfo->type == AppExecFwk::AbilityType::SERVICE && (state == AbilityState::ABILITY_STATE_CREATE || state == AbilityState::ABILITY_STATE_TERMINATED || state == AbilityState::ABILITY_STATE_CONNECTED || state == AbilityState::ABILITY_STATE_DISCONNECTED)) { - TAG_LOGI( - AAFwkTag::APPMGR, "state:%{public}d", static_cast(state)); + TAG_LOGI(AAFwkTag::APPMGR, "state:%{public}d", static_cast(state)); appRecord->StateChangedNotifyObserver(abilityRecord, static_cast(state), true, false); return; } @@ -4596,7 +4740,9 @@ void AppMgrServiceInner::UpdateAbilityState(const sptr &token, co TAG_LOGE(AAFwkTag::APPMGR, "state is not foreground or background"); return; } - + if (state == AbilityState::ABILITY_STATE_FOREGROUND) { + ReportAbilityStartInfoForSpecified(appRecord, *abilityInfo); + } appRecord->UpdateAbilityState(token, state, isFromScreenOffBackground); CheckCleanAbilityByUserRequest(appRecord, abilityRecord, state); } @@ -5235,6 +5381,7 @@ int32_t AppMgrServiceInner::StartPerfProcessByStartMsg(AppSpawnStartMsg &startMs startMsg.flags |= (START_FLAG_BASE << StartFlags::NO_SANDBOX); } else { TAG_LOGI(AAFwkTag::APPMGR, "debuggablePipe sandbox: true"); + startMsg.flags |= (START_FLAG_BASE << StartFlags::APP_FLAGS_DEBUGSERVER); } if (!perfCmd.empty()) { startMsg.renderParam = perfCmd; @@ -7259,6 +7406,7 @@ void AppMgrServiceInner::StartSpecifiedAbility(const AAFwk::Want &want, const Ap appRecord->AddAbilityStageBySpecifiedAbility(appInfo->bundleName); } else if (!appRecord->AddAbilityStageBySpecifiedAbility(appInfo->bundleName)) { TAG_LOGD(AAFwkTag::APPMGR, "schedule accept want"); + ReportAbilityStartInfoForSpecified(appRecord, abilityInfo); appRecord->ScheduleAcceptWant(hapModuleInfo.moduleName); } } @@ -7856,14 +8004,14 @@ void AppMgrServiceInner::SubmitDestroyImageTask(const std::shared_ptrGetBundleName(); const int32_t userId = appRecord->GetUserId(); const int32_t appIndex = appRecord->GetAppIndex(); - if (!IsImageInfoExist(bundleName, userId, appIndex)) { + if (!IsImageInfoExist(appRecord)) { TAG_LOGD(AAFwkTag::APPMGR, "submit DestroyImageTask image not exist."); return; } TAG_LOGI(AAFwkTag::APPMGR, "submit DestroyImageTask, %{public}s_%{public}d_%{public}d, reason=%{public}d " "exitMsg=%{public}s", bundleName.c_str(), userId, appIndex, reason, exitMsg.c_str()); - auto task = [bundleName, userId, appIndex, innerService = shared_from_this()]() { - innerService->DestroyImageForFault(bundleName, userId, appIndex); + auto task = [appRecord, innerService = shared_from_this()]() { + innerService->DestroyImageForFault(appRecord); }; taskHandler_->SubmitTask(task, AAFwk::TaskQoS::USER_INTERACTIVE); } @@ -8473,7 +8621,7 @@ void AppMgrServiceInner::OnImageProcessRemoteDied(const wptr &rem return; } RemoveImageDeathRecipient(imageInfo); - RemoveImageInfo(baseAppRecord->GetBundleName(), baseAppRecord->GetUserId(), baseAppRecord->GetAppIndex()); + RemoveImageInfo(baseAppRecord); DelayedSingleton::GetInstance()->OnImageProcessStateChanged( imageInfo, ImageProcessState::IMAGE_PROCESS_TERMINATED); @@ -8986,6 +9134,10 @@ int AppMgrServiceInner::GetExceptionTimerId(const FaultData &faultData, const st int32_t AppMgrServiceInner::SubmitDfxFaultTask(const FaultData &faultData, const std::string &bundleName, const std::shared_ptr &appRecord, const int32_t pid) { + if (AppExecFwk::AppfreezeManager::GetInstance()->CheckPreloadUIExtension(faultData.errorObject.message, + bundleName, pid)) { + return ERR_OK; + } int32_t callerUid = IPCSkeleton::GetCallingUid(); std::string processName = appRecord->GetProcessName(); int exceptionId = GetExceptionTimerId(faultData, bundleName, appRecord, pid, callerUid); @@ -9220,7 +9372,9 @@ int32_t AppMgrServiceInner::TransformedNotifyAppFault(const AppFaultDataBySA &fa } auto timeoutNotifyApp = [this, pid, uid, bundleName, processName, transformedFaultData, recordId]() { std::string key = std::to_string(pid) + "_" + std::to_string(uid) + "_" + bundleName; - if (AppExecFwk::AppfreezeManager::GetInstance()->CheckAppfreezeHappend(key, + std::string message = transformedFaultData.errorObject.message; + if (AppExecFwk::AppfreezeManager::GetInstance()->CheckPreloadUIExtension(message, bundleName, pid) || + AppExecFwk::AppfreezeManager::GetInstance()->CheckAppfreezeHappend(key, transformedFaultData.errorObject.name)) { return; } @@ -9493,15 +9647,15 @@ bool AppMgrServiceInner::CreateAbilityInfo(const AAFwk::Want &want, AbilityInfo return false; } } else { - if (!IN_PROCESS_CALL(bundleMgrHelper->GetSandboxExtAbilityInfos(want, appIndex, - abilityInfoFlag, userId, extensionInfos))) { + if (IN_PROCESS_CALL(bundleMgrHelper->GetSandboxExtAbilityInfos(want, appIndex, + abilityInfoFlag, userId, extensionInfos) != ERR_OK)) { TAG_LOGE(AAFwkTag::APPMGR, "getSandboxExtAbilityInfos fail"); return false; } } if (extensionInfos.size() <= 0) { TAG_LOGE(AAFwkTag::APPMGR, "get extension info fail"); - return ERR_INVALID_OPERATION; + return false; } AppExecFwk::ExtensionAbilityInfo extensionInfo = extensionInfos.front(); AbilityRuntime::StartupUtil::InitAbilityInfoFromExtension(extensionInfo, abilityInfo); @@ -11542,6 +11696,26 @@ int32_t AppMgrServiceInner::IsProcessCacheSupported(int32_t pid, bool &isSupport return ERR_OK; } +int32_t AppMgrServiceInner::IsChildProcessSupported(bool isNative, bool &isSupported) +{ + pid_t pid = IPCSkeleton::GetCallingPid(); + TAG_LOGD(AAFwkTag::APPMGR, "IsChildProcessSupported called, pid:%{public}d, isNative:%{public}d", pid, isNative); + auto appRecord = GetAppRunningRecordByPid(pid); + if (!appRecord) { + TAG_LOGE(AAFwkTag::APPMGR, "no such appRecord, pid:%{public}d", pid); + return AAFwk::ERR_NO_APP_RECORD; + } + bool isMultiProcessModel = AAFwk::AppUtils::GetInstance().IsMultiProcessModel(); + if (isNative) { + isSupported = isMultiProcessModel || + AAFwk::AppUtils::GetInstance().IsAllowNativeChildProcess(appRecord->GetAppIdentifier()) || + AllowChildProcessInMultiProcessFeatureApp(appRecord); + } else { + isSupported = isMultiProcessModel || AllowChildProcessInMultiProcessFeatureApp(appRecord); + } + return ERR_OK; +} + int32_t AppMgrServiceInner::SetProcessCacheEnable(int32_t pid, bool enable) { TAG_LOGI(AAFwkTag::APPMGR, "set enable process cache, pid:%{public}d, enable:%{public}d", pid, enable); @@ -12759,5 +12933,55 @@ int32_t AppMgrServiceInner::GetAllAbilityInfos(const int32_t pid, std::vectorGetAllAbilityInfos(pid, infos); } + +int32_t AppMgrServiceInner::EnableDelayedProcessExit(int32_t pid, bool enabled) +{ + if (!AAFwk::AppUtils::GetInstance().IsSupportDelayedProcessExit()) { + TAG_LOGE(AAFwkTag::APPMGR, "device not supported"); + return AAFwk::ERR_CAPABILITY_NOT_SUPPORT; + } + auto appRecord = GetAppRunningRecordByPid(pid); + if (appRecord == nullptr) { + return ERR_INVALID_VALUE; + } + auto hasUIAbility = false; + const auto abilityRecordList = appRecord->GetAbilities(); + for (const auto &item : abilityRecordList) { + if (item.second == nullptr) { + continue; + } + const auto abilityInfo = item.second->GetAbilityInfo(); + if (abilityInfo != nullptr && abilityInfo->type == AppExecFwk::AbilityType::PAGE) { + TAG_LOGD(AAFwkTag::APPMGR, "current process has UIAbility, pid:%{public}d", pid); + hasUIAbility = true; + break; + } + } + if (!hasUIAbility) { + TAG_LOGE(AAFwkTag::APPMGR, "current process has no UIAbility, pid:%{public}d", pid); + return AAFwk::ERR_DELAYED_PROCESS_EXIT_NO_UIABILITY; + } + + appRecord->EnableDelayedProcessExit(enabled); + if (!enabled && taskHandler_ != nullptr) { + taskHandler_->CancelTask("DELAY_EXIT_UI_PROCESS_" + std::to_string(appRecord->GetRecordId())); + } + return ERR_OK; +} + +void AppMgrServiceInner::CancelDelayedExitTask(int32_t pid) +{ + auto appRecord = GetAppRunningRecordByPid(pid); + if (appRecord == nullptr) { + TAG_LOGW(AAFwkTag::APPMGR, "CancelDelayedExitTask appRecord null: %{public}d", pid); + return; + } + TAG_LOGD(AAFwkTag::APPMGR, "CancelDelayedExitTask, pid:%{public}d", pid); + if (appRecord->IsDelayedProcessExitEnabled()) { + if (taskHandler_ != nullptr) { + taskHandler_->CancelTask("DELAY_EXIT_UI_PROCESS_" + std::to_string(appRecord->GetRecordId())); + } + } +} } // namespace AppExecFwk } // namespace OHOS diff --git a/services/appmgr/src/app_preloader.cpp b/services/appmgr/src/app_preloader.cpp index efddcf2601..58dd0ae922 100644 --- a/services/appmgr/src/app_preloader.cpp +++ b/services/appmgr/src/app_preloader.cpp @@ -115,11 +115,53 @@ bool AppPreloader::GetLaunchWant(const std::string &bundleName, int32_t userId, return true; } -int32_t AppPreloader::GeneratePreloadExtensionRequest(const AAFwk::Want &want, const AbilityInfo &abilityInfo, +bool AppPreloader::GetAbilityInfo(const AAFwk::Want &want, int32_t userId, int32_t appIndex, + PreloadRequest &request, AbilityInfo &abilityInfo) +{ + auto bundleMgrHelper = GetBundleManagerHelper(); + if (!bundleMgrHelper) { + TAG_LOGE(AAFwkTag::APPMGR, "get bundle manager helper error"); + return false; + } + auto abilityInfoFlag = AbilityRuntime::StartupUtil::BuildAbilityInfoFlag(); + if (IN_PROCESS_CALL(bundleMgrHelper->QueryAbilityInfo(want, abilityInfoFlag, userId, abilityInfo))) { + TAG_LOGI(AAFwkTag::APPMGR, "queryAbilityInfo ok"); + return true; + } + std::vector extensionInfos; + if (appIndex == 0) { + if (!IN_PROCESS_CALL(bundleMgrHelper->QueryExtensionAbilityInfos(want, abilityInfoFlag, + userId, extensionInfos))) { + TAG_LOGE(AAFwkTag::APPMGR, "queryExtensionAbilityInfos fail"); + return false; + } + } else { + if (IN_PROCESS_CALL(bundleMgrHelper->GetSandboxExtAbilityInfos(want, appIndex, + abilityInfoFlag, userId, extensionInfos) != ERR_OK)) { + TAG_LOGE(AAFwkTag::APPMGR, "getSandboxExtAbilityInfos fail"); + return false; + } + } + if (extensionInfos.size() <= 0) { + TAG_LOGE(AAFwkTag::APPMGR, "get extension info fail"); + return false; + } + AppExecFwk::ExtensionAbilityInfo extensionInfo = extensionInfos.front(); + request.extensionProcessMode = extensionInfo.extensionProcessMode; + AbilityRuntime::StartupUtil::InitAbilityInfoFromExtension(extensionInfo, abilityInfo); + return true; +} + +int32_t AppPreloader::GeneratePreloadExtensionRequest(const AAFwk::Want &want, int32_t userId, int32_t appIndex, PreloadRequest &request) { HITRACE_METER_NAME(HITRACE_TAG_APP, __PRETTY_FUNCTION__); TAG_LOGD(AAFwkTag::APPMGR, "PreloadExtension GeneratePreloadRequest"); + AbilityInfo abilityInfo; + if (!GetAbilityInfo(want, userId, appIndex, request, abilityInfo)) { + TAG_LOGE(AAFwkTag::APPMGR, "createAbilityInfo fail"); + return ERR_INVALID_OPERATION; + } std::string bundleName = want.GetElement().GetBundleName(); BundleInfo bundleInfo; diff --git a/services/appmgr/src/app_running_manager.cpp b/services/appmgr/src/app_running_manager.cpp index 6614e1e8e5..a721379c37 100644 --- a/services/appmgr/src/app_running_manager.cpp +++ b/services/appmgr/src/app_running_manager.cpp @@ -314,6 +314,26 @@ std::shared_ptr AppRunningManager::FindMasterProcessAppRunning return resMasterRecord; } +std::shared_ptr AppRunningManager::FindMainProcessAppRunningRecord(const int uid) +{ + HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__); + TAG_LOGD(AAFwkTag::APPMGR, "uid: %{public}d", uid); + auto appRunningMap = GetAppRunningRecordMap(); + for (const auto &item : appRunningMap) { + const auto &appRecord = item.second; + if (!(appRecord && appRecord->GetUid() == uid)) { + continue; + } + if (!IsAppRunningRecordValid(appRecord)) { + continue; + } + if (appRecord->IsMainProcess()) { + return appRecord; + } + } + return nullptr; +} + bool AppRunningManager::CheckMasterProcessAppRunningRecordIsExist( const std::string &appName, const AppExecFwk::AbilityInfo &abilityInfo, const int uid) { @@ -902,7 +922,7 @@ void AppRunningManager::HandleAbilityAttachTimeOut(const sptr &to } appRecord->TerminateAbility(token, true, true); }; - appRecord->PostTask("DELAY_KILL_ABILITY", AMSEventHandler::KILL_PROCESS_TIMEOUT, timeoutTask); + appRecord->PostTask("DELAY_KILL_ABILITY", AMSEventHandler::KILL_PROCESS_TIMEOUT_DELAY, timeoutTask); } void AppRunningManager::PrepareTerminate(const sptr &token, bool clearMissionFlag) diff --git a/services/appmgr/src/app_running_record.cpp b/services/appmgr/src/app_running_record.cpp index d480bf06e1..f65bb0e754 100644 --- a/services/appmgr/src/app_running_record.cpp +++ b/services/appmgr/src/app_running_record.cpp @@ -34,6 +34,7 @@ #include "app_mgr_service_const.h" #include "app_mgr_service_dump_error_code.h" #include "cache_process_manager.h" +#include "global_constant.h" #include "hisysevent_report.h" #ifdef SUPPORT_SCREEN #include "window_visibility_info.h" @@ -416,11 +417,11 @@ void AppRunningRecord::LaunchApplication(const Configuration &config) launchData.SetAppPreloadMode(preloadMode_); launchData.SetNWebPreload(isAllowedNWebPreload_); launchData.SetPreloadModuleName(preloadModuleName_); + launchData.SetPreloadAbilityName(preloadAbilityName_); launchData.SetDebugFromLocal(isDebugFromLocal_); - launchData.SetArkChildProcessSupported(isArkChildProcessSupported_); - launchData.SetNativeChildProcessSupported(isNativeChildProcessSupported_); launchData.SetStartupTaskData(startupTaskData_); launchData.SetImageProcessType(static_cast(imageProcessType_)); + launchData.SetMainProcess(isMainProcess_); TAG_LOGD(AAFwkTag::APPMGR, "%{public}s called,app is %{public}s.", __func__, GetName().c_str()); AddAppLifecycleEvent("AppRunningRecord::LaunchApplication"); @@ -1177,7 +1178,9 @@ void AppRunningRecord::AbilityTerminated(const sptr &token) auto abilityRecord = moduleRecord->GetAbilityByTerminateLists(token); if (abilityRecord != nullptr && abilityRecord->GetAbilityInfo() != nullptr) { isExtensionDebug = (abilityRecord->GetAbilityInfo()->type == AppExecFwk::AbilityType::EXTENSION) && - (isAttachDebug_ || isDebugApp_); + abilityRecord->GetAbilityInfo()->extensionAbilityType != AppExecFwk::ExtensionAbilityType::AGENT && + abilityRecord->GetAbilityInfo()->extensionAbilityType != AppExecFwk::ExtensionAbilityType::AGENT_UI && + (isAttachDebug_ || isDebugApp_); } TAG_LOGD(AAFwkTag::APPMGR, "Extension debug is [%{public}s]", isExtensionDebug ? "true" : "false"); @@ -1205,16 +1208,35 @@ void AppRunningRecord::AbilityTerminated(const sptr &token) || !ExitResidentProcessManager::GetInstance().IsMemorySizeSufficient()) && !needCache) { RemoveModuleRecord(moduleRecord, isExtensionDebug); } - + bool isLastUIAbility = (abilityRecord != nullptr && abilityRecord->GetAbilityInfo() != nullptr && + abilityRecord->GetAbilityInfo()->type == AppExecFwk::AbilityType::PAGE); auto moduleRecordList = GetAllModuleRecord(); if (moduleRecordList.empty() && (!IsKeepAliveApp() || AAFwk::UIExtensionWrapper::IsUIExtension(GetExtensionType()) || !ExitResidentProcessManager::GetInstance().IsMemorySizeSufficient()) && !isExtensionDebug && !needCache) { - ScheduleTerminate(); + ScheduleTerminateByDelayed(isLastUIAbility); } } +void AppRunningRecord::ScheduleTerminateByDelayed(bool isLastUIAbility) +{ + if (IsDelayedProcessExitEnabled() && isLastUIAbility) { + TAG_LOGD(AAFwkTag::APPMGR, "ScheduleTerminateByDelayed"); + auto delayedExitTime = AbilityRuntime::GlobalConstant::PREPARE_TERMINATE_TIMEOUT_TIME; + std::string taskName = std::string("DELAY_EXIT_UI_PROCESS_") + std::to_string(GetRecordId()); + auto terminateTask = [weakThis = weak_from_this()]() { + auto self = weakThis.lock(); + if (self) { + self->ScheduleTerminate(); + } + }; + PostTask(taskName, delayedExitTime, terminateTask); + return; + } + ScheduleTerminate(); +} + std::list> AppRunningRecord::GetAllModuleRecord() const { std::list> moduleRecordList; @@ -2550,6 +2572,16 @@ std::string AppRunningRecord::GetPreloadModuleName() const return preloadModuleName_; } +void AppRunningRecord::SetPreloadAbilityName(const std::string &abilityName) +{ + preloadAbilityName_ = abilityName; +} + +std::string AppRunningRecord::GetPreloadAbilityName() const +{ + return preloadAbilityName_; +} + void AppRunningRecord::SetPreloadState(PreloadState state) { preloadState_ = state; diff --git a/services/appmgr/src/app_spawn_client.cpp b/services/appmgr/src/app_spawn_client.cpp index 29b0b38bb8..5d54c67d3c 100644 --- a/services/appmgr/src/app_spawn_client.cpp +++ b/services/appmgr/src/app_spawn_client.cpp @@ -568,14 +568,16 @@ int32_t AppSpawnClient::AppspawnCreateDefaultMsg(const AppSpawnStartMsg &startMs break; } if (startMsg.code == MSG_SPAWN_WORKER_PROCESS) { - ret = AppSpawnReqMsgSetCheckpointInfo(reqHandle, startMsg.imagePid, startMsg.checkpointId); + ret = AppSpawnReqMsgSetCheckpointInfo(reqHandle, startMsg.imagePid, startMsg.checkpointId, + startMsg.imageName.c_str()); if (ret != ERR_OK) { TAG_LOGE(AAFwkTag::APPMGR, "fail, ret: %{public}d", ret); break; } } if (startMsg.code == MSG_SPAWN_IMAGE_PROCESS) { - ret = AppSpawnReqMsgSetCheckpointInfo(reqHandle, startMsg.templatePid, 0); + ret = AppSpawnReqMsgSetCheckpointInfo(reqHandle, startMsg.templatePid, 0, + startMsg.imageName.c_str()); if (ret != ERR_OK) { TAG_LOGE(AAFwkTag::APPMGR, "fail, ret: %{public}d", ret); break; diff --git a/services/appmgr/src/app_state_observer_manager.cpp b/services/appmgr/src/app_state_observer_manager.cpp index 36ee2060f1..12ddd6abc8 100644 --- a/services/appmgr/src/app_state_observer_manager.cpp +++ b/services/appmgr/src/app_state_observer_manager.cpp @@ -1738,6 +1738,7 @@ std::shared_ptr AppStateObserverManager::WrapImageProcess data->uid = imageInfo->baseAppRecord->GetUid(); data->state = static_cast(state); data->bundleName = imageInfo->baseAppRecord->GetBundleName(); + data->abilityName = imageInfo->abilityName; return data; } diff --git a/services/common/BUILD.gn b/services/common/BUILD.gn index 16c45a6192..02f88aa445 100644 --- a/services/common/BUILD.gn +++ b/services/common/BUILD.gn @@ -1,5 +1,5 @@ # -# Copyright (c) 2024-2025 Huawei Device Co., Ltd. +# Copyright (c) 2024-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 @@ -33,6 +33,8 @@ config("common_config") { "${ability_runtime_path}/frameworks/c/ability_runtime/*", "${ability_runtime_path}/frameworks/simulator/ability_simulator/*", "${ability_runtime_path}/tools/aa/*", + "${ability_runtime_path}/tools/ohos-aa/*", + "${ability_runtime_path}/tools/cc/*", "${ability_runtime_services_path}/common/*", "${ability_runtime_services_path}/quickfixmgr/*", "${ability_runtime_services_path}/uripermmgr/*", @@ -111,7 +113,10 @@ ohos_shared_library("perm_verification") { sources = [ "src/permission_verification.cpp" ] - deps = [ ":app_util" ] + deps = [ + ":app_util", + ":hisysevent_report", + ] external_deps = [ "ability_base:want", @@ -119,6 +124,7 @@ ohos_shared_library("perm_verification") { "access_token:libtokenid_sdk", "c_utils:utils", "hilog:libhilog", + "hisysevent:libhisysevent", "hitrace:hitrace_meter", "init:libbegetutil", "ipc:ipc_core", @@ -148,7 +154,10 @@ ohos_static_library("perm_verification_static") { sources = [ "src/permission_verification.cpp" ] - deps = [ ":app_util_static" ] + deps = [ + ":app_util_static", + ":hisysevent_report_static", + ] external_deps = [ "ability_base:want", @@ -156,6 +165,7 @@ ohos_static_library("perm_verification_static") { "access_token:libtokenid_sdk", "c_utils:utils", "hilog:libhilog", + "hisysevent:libhisysevent", "hitrace:hitrace_meter", "init:libbegetutil", "ipc:ipc_core", diff --git a/services/common/include/app_utils.h b/services/common/include/app_utils.h index b3420637be..0a55147456 100644 --- a/services/common/include/app_utils.h +++ b/services/common/include/app_utils.h @@ -377,6 +377,8 @@ public: * @return Whether it supports native UI ability. */ bool IsSupportNativeUIAbility(); + + bool IsSupportDelayedProcessExit(); private: /** * LoadResidentProcessInExtremeMemory, load resident process in extreme low memory. @@ -469,6 +471,7 @@ private: volatile DeviceConfiguration isProductAppbootSettingEnabled_ = {false, false}; volatile DeviceConfiguration isHybridSpawnUnified_ = {false, false}; volatile DeviceConfiguration isSupportNativeUIAbility_ = {false, false}; + volatile DeviceConfiguration isSupportDelayedProcessExit_ = {false, false}; DeviceConfiguration>> residentProcessInExtremeMemory_ = {false, {}}; std::mutex residentProcessInExtremeMemoryMutex_; diff --git a/services/common/include/hilog_tag_wrapper.h b/services/common/include/hilog_tag_wrapper.h index 87a8a5a409..3a5a1926af 100644 --- a/services/common/include/hilog_tag_wrapper.h +++ b/services/common/include/hilog_tag_wrapper.h @@ -42,6 +42,7 @@ enum class AAFwkLogTag : uint32_t { ABILITY, TEST, AA_TOOL, + CC_TOOL, ABILITY_SIM, APPDFR = DEFAULT + 0x10, // 0xD001310 @@ -87,6 +88,7 @@ enum class AAFwkLogTag : uint32_t { AUTOFILLMGR, EXTMGR, SER_ROUTER, + CLI_TOOL = SER_ROUTER, AUTO_STARTUP, STARTUP, RECOVERY, @@ -102,7 +104,6 @@ enum class AAFwkLogTag : uint32_t { APP_SERVICE_EXT, VERTICAL_PANEL, USER_CONTROLLER, - CLI_TOOL, END = 256, // N.B. never use it }; @@ -114,7 +115,7 @@ inline uint32_t GetOffset(AAFwkLogTag tag, AAFwkLogTag base) inline const char* GetDomainName0(AAFwkLogTag tag) { - const char* tagNames[] = { "AAFwk", "Ability", "Test", "AATool", "Simulator" }; + const char* tagNames[] = { "AAFwk", "Ability", "Test", "AATool", "CCTool", "Simulator" }; uint32_t offset = GetOffset(tag, AAFwkLogTag::DEFAULT); if (offset >= sizeof(tagNames) / sizeof(const char*)) { return "UN"; diff --git a/services/common/include/permission_constants.h b/services/common/include/permission_constants.h index bec171e736..9b2d1a4f65 100644 --- a/services/common/include/permission_constants.h +++ b/services/common/include/permission_constants.h @@ -88,6 +88,7 @@ constexpr const char* PERMISSION_GET_AGENT_CARD = "ohos.permission.GET_AGENT_CAR constexpr const char* PERMISSION_MODIFY_AGENT_CARD = "ohos.permission.MODIFY_AGENT_CARD"; constexpr const char* PERMISSION_START_ABILITY_TO_PAGE = "ohos.permission.START_ABILITY_TO_PAGE"; constexpr const char* PERMISSION_CONTINUATION_NOTIFY = "ohos.permission.CONTINUATION_NOTIFY"; +constexpr const char* PERMISSION_EXECUTE_DISTRIBUTED_INTENT = "ohos.permission.EXECUTE_DISTRIBUTED_INTENT"; } // namespace PermissionConstants } // namespace AAFwk } // namespace OHOS diff --git a/services/common/include/permission_verification.h b/services/common/include/permission_verification.h index be29363537..dc8d710993 100644 --- a/services/common/include/permission_verification.h +++ b/services/common/include/permission_verification.h @@ -28,6 +28,7 @@ struct VerificationInfo { uint32_t accessTokenId = 0; uint32_t specifyTokenId = 0; int32_t apiTargetVersion = 0; + uint32_t specifiedFullTokenId = 0; bool visible = false; bool isBackgroundCall = true; bool associatedWakeUp = false; diff --git a/services/common/include/support_system_ability_permission.h b/services/common/include/support_system_ability_permission.h index 24e206ad10..c45afa4525 100755 --- a/services/common/include/support_system_ability_permission.h +++ b/services/common/include/support_system_ability_permission.h @@ -43,7 +43,7 @@ constexpr std::array SUPPORTED_UIDS{1002, 1003, 1004, 1007, 1010, 1013, 1014, 7748, 7777, 7778, 7779, 7780, 7789, 7799, 7811, 7812, 7851, 7878, 7886, 7890, 7958, 7992, 7993, 7994, 7995, 7999, 8000, 8002, 8020, 8030, 8050, 8064, 8100, 8666, 8668, 8866, 8879, 8888, 9998, 10000}; -constexpr std::array SUPPORTED_KILL_UIDS{1111, 1096, 1003, 5523, 7345}; +constexpr std::array SUPPORTED_KILL_UIDS{1111, 1096, 1003, 5523, 7345, 7655}; inline bool IsSupportSaCallPermission() { diff --git a/services/common/src/app_utils.cpp b/services/common/src/app_utils.cpp index 352ebab4ce..9b5e2deaef 100644 --- a/services/common/src/app_utils.cpp +++ b/services/common/src/app_utils.cpp @@ -89,6 +89,7 @@ constexpr const char* SUPPORT_MODULAR_OBJECT_EXTENSION = "const.abilityms.suppor constexpr const char* RESTART_APP_WITH_WINDOW = "persist.sys.abilityms.restart_app_with_window"; constexpr const char* SUPPORT_NATIVE_UI_ABILITY = "persist.sys.abilityms.support_native_ui_ability"; constexpr const char* PRODUCT_APPBOOT_SETTING_ENABLED = "const.product.appboot.setting.enabled"; +constexpr const char* SUPPORT_DELAYED_PROCESS_EXIT = "const.abilityms.support_delayed_process_exit"; // Support prepare terminate constexpr int32_t PREPARE_TERMINATE_ENABLE_SIZE = 6; constexpr const char* PREPARE_TERMINATE_ENABLE_PARAMETER = "persist.sys.prepare_terminate"; @@ -896,6 +897,16 @@ bool AppUtils::IsSupportModularObjectExtension() return isSupportModularObjectExtension_.value; } +bool AppUtils::IsSupportDelayedProcessExit() +{ + if (!isSupportDelayedProcessExit_.isLoaded) { + isSupportDelayedProcessExit_.value = system::GetBoolParameter(SUPPORT_DELAYED_PROCESS_EXIT, false); + isSupportDelayedProcessExit_.isLoaded = true; + } + TAG_LOGD(AAFwkTag::DEFAULT, "supportDelayedProcessExit: %{public}d", isSupportDelayedProcessExit_.value); + return isSupportDelayedProcessExit_.value; +} + bool AppUtils::IsProductAppbootSettingEnabled() { if (!isProductAppbootSettingEnabled_.isLoaded) { diff --git a/services/common/src/permission_verification.cpp b/services/common/src/permission_verification.cpp index 2d860096ed..4acdccf5d8 100644 --- a/services/common/src/permission_verification.cpp +++ b/services/common/src/permission_verification.cpp @@ -17,6 +17,7 @@ #include "ability_manager_errors.h" #include "accesstoken_kit.h" +#include "event_report.h" #include "hilog_tag_wrapper.h" #include "permission_constants.h" #include "server_constant.h" @@ -335,7 +336,8 @@ int PermissionVerification::CheckStartByCallPermission(const VerificationInfo &v return CHECK_PERMISSION_FAILED; } // Different APP call, check permissions - if (!VerifyCallingPermission(PermissionConstants::PERMISSION_ABILITY_BACKGROUND_COMMUNICATION)) { + if (!VerifyCallingPermission( + PermissionConstants::PERMISSION_ABILITY_BACKGROUND_COMMUNICATION, verificationInfo.specifiedFullTokenId)) { TAG_LOGE(AAFwkTag::DEFAULT, "Permission denied"); return CHECK_PERMISSION_FAILED; } @@ -432,6 +434,12 @@ int PermissionVerification::JudgeInvisibleAndBackground(const VerificationInfo & if (specifyTokenId == 0 && SupportSystemAbilityPermission::IsSupportSaCallPermission()) { TAG_LOGD(AAFwkTag::DEFAULT, "Support SA call"); + // Only report when: 1) shell call, 2) ability is invisible (exported=false) + if (IsShellCall() && !verificationInfo.visible) { + EventInfo eventInfo; + eventInfo.uri = "ShellCall://" + std::to_string(IPCSkeleton::GetCallingUid()); + EventReport::SendGrantUriPermissionEvent(EventName::GRANT_URI_PERMISSION, eventInfo); + } return ERR_OK; } if (!isCallByShortcut && diff --git a/services/common/src/user_controller/user_controller.cpp b/services/common/src/user_controller/user_controller.cpp index b1a1c599b8..d2e75c6cba 100644 --- a/services/common/src/user_controller/user_controller.cpp +++ b/services/common/src/user_controller/user_controller.cpp @@ -197,7 +197,7 @@ void UserController::SetUserLockStatus(int32_t userId, UserController::UserLockS return; } userLockStatusMap_[userId] = status; - TAG_LOGD(AAFwkTag::USER_CONTROLLER, "SetUserLockStatus successful, userId:%{public}d, status:%{public}d", + TAG_LOGI(AAFwkTag::ABILITYMGR, "SetUserLockStatus successful, userId:%{public}d, status:%{public}d", userId, static_cast(status)); } @@ -205,7 +205,7 @@ void UserController::DeleteUserLockStatus(int32_t userId) { std::lock_guard guard(userLock_); userLockStatusMap_.erase(userId); - TAG_LOGD(AAFwkTag::USER_CONTROLLER, "DeleteUserLockStatus successful, userId:%{public}d", userId); + TAG_LOGD(AAFwkTag::ABILITYMGR, "DeleteUserLockStatus successful, userId:%{public}d", userId); } int32_t UserController::GetUserLockedBundleList(int32_t userId, std::unordered_set &userLockedBundleList) diff --git a/services/uripermmgr/BUILD.gn b/services/uripermmgr/BUILD.gn index e9cbd816b6..e60e373220 100644 --- a/services/uripermmgr/BUILD.gn +++ b/services/uripermmgr/BUILD.gn @@ -112,7 +112,10 @@ ohos_shared_library("libupms") { if (ability_runtime_udmf_enable) { sources += [ "src/upms_udmf_utils.cpp" ] - external_deps += [ "udmf:udmf_client" ] + external_deps += [ + "udmf:udmf_client", + "image_framework:image", + ] defines += [ "ABILITY_RUNTIME_UDMF_ENABLE" ] } @@ -191,7 +194,10 @@ ohos_static_library("libupms_static") { if (ability_runtime_udmf_enable) { sources += [ "src/upms_udmf_utils.cpp" ] - external_deps += [ "udmf:udmf_client" ] + external_deps += [ + "udmf:udmf_client", + "image_framework:image", + ] defines += [ "ABILITY_RUNTIME_UDMF_ENABLE" ] } diff --git a/test/fuzztest/abilityapppreloaderthird_fuzzer/abilityapppreloaderthird_fuzzer.cpp b/test/fuzztest/abilityapppreloaderthird_fuzzer/abilityapppreloaderthird_fuzzer.cpp index 06c442c185..1e3773261e 100644 --- a/test/fuzztest/abilityapppreloaderthird_fuzzer/abilityapppreloaderthird_fuzzer.cpp +++ b/test/fuzztest/abilityapppreloaderthird_fuzzer/abilityapppreloaderthird_fuzzer.cpp @@ -93,8 +93,8 @@ bool DoSomethingInterestingWithMyAPI(const uint8_t* data, size_t size) appPreloader->GeneratePreloadRequest(bundleName, userId, appIndex, requestPhase); appPreloaderNull->GeneratePreloadRequest(bundleName, userId, appIndex, request); - appPreloader->GeneratePreloadExtensionRequest(launchWant, validAbilityInfo, userId, appIndex, request); - appPreloaderNull->GeneratePreloadExtensionRequest(launchWant, validAbilityInfo, userId, appIndex, request); + appPreloader->GeneratePreloadExtensionRequest(launchWant, userId, appIndex, request); + appPreloaderNull->GeneratePreloadExtensionRequest(launchWant, userId, appIndex, request); appPreloader->GetBundleManagerHelper(); appPreloaderNull->GetBundleManagerHelper(); diff --git a/test/fuzztest/abilityfirstframestateobservermanager_fuzzer/BUILD.gn b/test/fuzztest/abilityfirstframestateobservermanager_fuzzer/BUILD.gn index 2f424b56c1..159001380c 100644 --- a/test/fuzztest/abilityfirstframestateobservermanager_fuzzer/BUILD.gn +++ b/test/fuzztest/abilityfirstframestateobservermanager_fuzzer/BUILD.gn @@ -65,6 +65,7 @@ ohos_fuzztest("AbilityFirstFrameStateObserverManagerFuzzTest") { "common_event_service:cesfwk_innerkits", "ffrt:libffrt", "hilog:libhilog", + "hisysevent:libhisysevent", "hitrace:hitrace_meter", "ipc:ipc_core", "napi:ace_napi", diff --git a/test/fuzztest/abilitypermissionutil_fuzzer/BUILD.gn b/test/fuzztest/abilitypermissionutil_fuzzer/BUILD.gn index df06bbaf02..389944569d 100644 --- a/test/fuzztest/abilitypermissionutil_fuzzer/BUILD.gn +++ b/test/fuzztest/abilitypermissionutil_fuzzer/BUILD.gn @@ -62,6 +62,7 @@ ohos_fuzztest("AbilityPermissionUtilFuzzTest") { "${ability_runtime_services_path}/common:app_util", "${ability_runtime_services_path}/common:perm_verification", "${ability_runtime_services_path}/common:task_handler_wrap", + "${ability_runtime_services_path}/common:user_controller", ] external_deps = [ diff --git a/test/fuzztest/insightintentexecutemanagersecond_fuzzer/BUILD.gn b/test/fuzztest/insightintentexecutemanagersecond_fuzzer/BUILD.gn index 5fdd11058d..f91f7aa0e0 100644 --- a/test/fuzztest/insightintentexecutemanagersecond_fuzzer/BUILD.gn +++ b/test/fuzztest/insightintentexecutemanagersecond_fuzzer/BUILD.gn @@ -25,9 +25,12 @@ ohos_fuzztest("InsightIntentExecuteManagerSecondFuzzTest") { fuzz_config_file = "${ability_runtime_test_path}/fuzztest/insightintentexecutemanagersecond_fuzzer" include_dirs = [ + "${ability_runtime_path}/interfaces/kits/native/appkit/ability_bundle_manager_helper", "${ability_runtime_innerkits_path}/ability_manager/include/insight_intent", "${ability_runtime_innerkits_path}/wantagent/include", "${ability_runtime_services_path}/abilitymgr/include/utils", + "${ability_runtime_services_path}/common/include", + "${ability_runtime_test_path}/fuzztest/", ] cflags = [ @@ -38,6 +41,7 @@ ohos_fuzztest("InsightIntentExecuteManagerSecondFuzzTest") { ] sources = [ + "${ability_runtime_path}/frameworks/native/appkit/ability_bundle_manager_helper/bundle_mgr_helper.cpp", "${ability_runtime_services_path}/abilitymgr/src/insight_intent/extract_insight_intent_profile.cpp", "${ability_runtime_services_path}/abilitymgr/src/insight_intent/insight_intent_db_cache.cpp", "${ability_runtime_services_path}/abilitymgr/src/insight_intent/insight_intent_execute_manager.cpp", @@ -45,6 +49,9 @@ ohos_fuzztest("InsightIntentExecuteManagerSecondFuzzTest") { "${ability_runtime_services_path}/abilitymgr/src/insight_intent/insight_intent_rdb_data_mgr.cpp", "${ability_runtime_services_path}/abilitymgr/src/insight_intent/insight_intent_rdb_storage_mgr.cpp", "${ability_runtime_services_path}/abilitymgr/src/utils/hmsf_utils.cpp", + "${ability_runtime_services_path}/common/src/json_utils.cpp", + "${ability_runtime_services_path}/common/src/app_utils.cpp", + "${ability_runtime_services_path}/common/src/record_cost_time_util.cpp", "insightintentexecutemanagersecond_fuzzer.cpp", ] @@ -66,6 +73,8 @@ ohos_fuzztest("InsightIntentExecuteManagerSecondFuzzTest") { external_deps = [ "ability_base:want", "ability_base:zuri", + "bundle_framework:appexecfwk_base", + "bundle_framework:appexecfwk_core", "access_token:libaccesstoken_sdk", "c_utils:utils", "common_event_service:cesfwk_innerkits", diff --git a/test/fuzztest/multiuserconfigmgr_fuzzer/BUILD.gn b/test/fuzztest/multiuserconfigmgr_fuzzer/BUILD.gn index dc0c18eeb1..01bcea2df4 100644 --- a/test/fuzztest/multiuserconfigmgr_fuzzer/BUILD.gn +++ b/test/fuzztest/multiuserconfigmgr_fuzzer/BUILD.gn @@ -45,6 +45,7 @@ ohos_fuzztest("MultiUserConfigMgrFuzzTest") { "hilog:libhilog", "hitrace:hitrace_meter", "i18n:i18n_sa_client", + "ipc:ipc_single", "os_account:libaccountkits", "os_account:os_account_innerkits", ] diff --git a/test/mock/frameworks_kits_ability_ability_runtime_test/AMS/mock_serviceability_manager_service.cpp b/test/mock/frameworks_kits_ability_ability_runtime_test/AMS/mock_serviceability_manager_service.cpp index eb730c2610..4c0f7f3602 100644 --- a/test/mock/frameworks_kits_ability_ability_runtime_test/AMS/mock_serviceability_manager_service.cpp +++ b/test/mock/frameworks_kits_ability_ability_runtime_test/AMS/mock_serviceability_manager_service.cpp @@ -53,7 +53,8 @@ int MockServiceAbilityManagerService::StartAbilityAsCaller( } int MockServiceAbilityManagerService::StartAbilityByCall(const Want& want, const sptr& connect, - const sptr& callerToken, int32_t accountId, bool isSilent, bool promotePriority, bool isVisible) + const sptr& callerToken, int32_t accountId, + bool isSilent, bool promotePriority, bool isVisible, uint64_t specifiedFullTokenId) { GTEST_LOG_(INFO) << "MockServiceAbilityManagerService::StartAbilityByCall begain"; if (!connect) { diff --git a/test/mock/frameworks_kits_ability_ability_runtime_test/AMS/mock_serviceability_manager_service.h b/test/mock/frameworks_kits_ability_ability_runtime_test/AMS/mock_serviceability_manager_service.h index bccaf67438..539417e40c 100644 --- a/test/mock/frameworks_kits_ability_ability_runtime_test/AMS/mock_serviceability_manager_service.h +++ b/test/mock/frameworks_kits_ability_ability_runtime_test/AMS/mock_serviceability_manager_service.h @@ -68,7 +68,7 @@ public: int StartAbilityByCall(const Want& want, const sptr& connect, const sptr& callerToken, int32_t accountId = DEFAULT_INVAL_VALUE, bool isSilent = false, - bool promotePriority = false, bool isVisible = false); + bool promotePriority = false, bool isVisible = false, uint64_t specifiedFullTokenId = 0) override; int StartAbilityForPrelaunch(const Want &want, const int32_t frameNum); int TerminateAbility( const sptr& token, int resultCode = -1, const Want* resultWant = nullptr) override; diff --git a/test/mock/frameworks_kits_ability_native_test/include/mock_ability_manager_service.h b/test/mock/frameworks_kits_ability_native_test/include/mock_ability_manager_service.h index 90b8810259..e908c62696 100644 --- a/test/mock/frameworks_kits_ability_native_test/include/mock_ability_manager_service.h +++ b/test/mock/frameworks_kits_ability_native_test/include/mock_ability_manager_service.h @@ -147,8 +147,8 @@ public: MOCK_METHOD1(GetProcessRunningInfos, int(std::vector& info)); MOCK_METHOD2(GetWantSenderInfo, int(const sptr& target, std::shared_ptr& info)); - MOCK_METHOD7(StartAbilityByCall, - int(const Want&, const sptr&, const sptr&, int32_t, bool, bool, bool)); + MOCK_METHOD8(StartAbilityByCall, int(const Want&, const sptr&, + const sptr&, int32_t, bool, bool, bool, uint64_t)); MOCK_METHOD5(StartAbilityAsCaller, int(const Want& want, const sptr& callerToken, sptr asCallerSourceToken, int32_t userId, int requestCode)); diff --git a/test/mock/frameworks_kits_ability_native_test/include/mock_ui_ability.h b/test/mock/frameworks_kits_ability_native_test/include/mock_ui_ability.h index 2ad33c467a..0566b34193 100644 --- a/test/mock/frameworks_kits_ability_native_test/include/mock_ui_ability.h +++ b/test/mock/frameworks_kits_ability_native_test/include/mock_ui_ability.h @@ -32,64 +32,74 @@ public: enum Event { ON_ACTIVE = 0, ON_BACKGROUND, ON_FOREGROUND, ON_INACTIVE, ON_START, ON_STOP, UNDEFINED }; - void OnAbilityResult(int requestCode, int resultCode, const AAFwk::Want &resultData) + void OnAbilityResult(int requestCode, int resultCode, const AAFwk::Want &resultData) override { GTEST_LOG_(INFO) << "MockUIAbility::OnAbilityResult called"; state_ = ON_ACTIVE; } - void OnNewWant(const Want &want) + void OnNewWant(const Want &want) override { onNewWantCalled_ = true; GTEST_LOG_(INFO) << "MockUIAbility::OnNewWant called"; } - void OnStart(const Want &want, sptr sessionInfo) + void OnStart(const Want &want, sptr sessionInfo) override { GTEST_LOG_(INFO) << "MockUIAbility::OnStart called"; state_ = ON_START; } - void OnStop() + void OnStop() override { GTEST_LOG_(INFO) << "MockUIAbility::OnStop called"; state_ = ON_STOP; } - void OnForeground(const Want &want) + void OnForeground(const Want &want) override { GTEST_LOG_(INFO) << "MockUIAbility::OnForeground called"; state_ = ON_FOREGROUND; } - void OnBackground() + void OnBackground() override { GTEST_LOG_(INFO) << "MockUIAbility::OnBackground called"; state_ = ON_BACKGROUND; } - void OnRestoreAbilityState(const PacMap &inState) + void OnRestoreAbilityState(const PacMap &inState) override { GTEST_LOG_(INFO) << "Mock UIAbility::OnRestoreAbilityState called"; } - void OnConfigurationUpdated(const Configuration &config) + void OnConfigurationUpdated(const Configuration &config) override { GTEST_LOG_(INFO) << "Mock UIAbility::OnConfigurationUpdated called"; OnConfigurationUpdated_++; } - void ContinuationRestore(const Want &want) + void ContinuationRestore(const Want &want) override { GTEST_LOG_(INFO) << "Mock UIAbility::ContinuationRestore called"; continueRestoreCalled_ = true; } + const std::shared_ptr GetAbilityInfo() override + { + if (useMockAbilityInfo_) { + return mockAbilityInfo_; + } + return AbilityRuntime::UIAbility::GetAbilityInfo(); + } + MockUIAbility::Event state_ = UNDEFINED; bool onNewWantCalled_ = false; bool continueRestoreCalled_ = false; int OnConfigurationUpdated_ = 0; std::vector value; + bool useMockAbilityInfo_ = false; + std::shared_ptr mockAbilityInfo_; }; } // namespace AppExecFwk } // namespace OHOS diff --git a/test/mock/frameworks_kits_ability_native_test/include/mock_ui_extension.h b/test/mock/frameworks_kits_ability_native_test/include/mock_ui_extension.h new file mode 100644 index 0000000000..aaf2235988 --- /dev/null +++ b/test/mock/frameworks_kits_ability_native_test/include/mock_ui_extension.h @@ -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. + */ + +#ifndef MOCK_OHOS_ABILITY_RUNTIME_MOCK_UI_EXTENSION_H +#define MOCK_OHOS_ABILITY_RUNTIME_MOCK_UI_EXTENSION_H + +#include +#include +#include "ui_extension.h" +#include "want.h" + +namespace OHOS { +namespace AbilityRuntime { + +class MockUIExtension : public UIExtension { +public: + MockUIExtension() = default; + virtual ~MockUIExtension() = default; + + MOCK_METHOD1(CreateModalUIExtension, int(const AAFwk::Want &want)); +}; + +} // namespace AbilityRuntime +} // namespace OHOS + +#endif // MOCK_OHOS_ABILITY_RUNTIME_MOCK_UI_EXTENSION_H diff --git a/test/mock/frameworks_kits_appkit_native_test/ability_delegator/mock_ability_delegator_stub.cpp b/test/mock/frameworks_kits_appkit_native_test/ability_delegator/mock_ability_delegator_stub.cpp index ec4a7f70be..dd177b397a 100644 --- a/test/mock/frameworks_kits_appkit_native_test/ability_delegator/mock_ability_delegator_stub.cpp +++ b/test/mock/frameworks_kits_appkit_native_test/ability_delegator/mock_ability_delegator_stub.cpp @@ -100,7 +100,8 @@ int MockAbilityDelegatorStub::UnRegisterMissionListener(const std::string& devic } int MockAbilityDelegatorStub::StartAbilityByCall(const Want& want, const sptr& connect, - const sptr& callerToken, int32_t accountId, bool isSilent, bool promotePriority, bool isVisible) + const sptr& callerToken, int32_t accountId, + bool isSilent, bool promotePriority, bool isVisible, uint64_t specifiedFullTokenId) { return 0; } @@ -300,7 +301,8 @@ int MockAbilityDelegatorStub2::UnRegisterMissionListener(const std::string& devi } int MockAbilityDelegatorStub2::StartAbilityByCall(const Want& want, const sptr& connect, - const sptr& callerToken, int32_t accountId, bool isSilent, bool promotePriority, bool isVisible) + const sptr& callerToken, int32_t accountId, + bool isSilent, bool promotePriority, bool isVisible, uint64_t specifiedFullTokenId) { return 0; } diff --git a/test/mock/frameworks_kits_appkit_native_test/ability_delegator/mock_ability_delegator_stub.h b/test/mock/frameworks_kits_appkit_native_test/ability_delegator/mock_ability_delegator_stub.h index 1e171bd8b7..14dafff36f 100644 --- a/test/mock/frameworks_kits_appkit_native_test/ability_delegator/mock_ability_delegator_stub.h +++ b/test/mock/frameworks_kits_appkit_native_test/ability_delegator/mock_ability_delegator_stub.h @@ -138,8 +138,8 @@ public: int UnRegisterMissionListener(const std::string& deviceId, const sptr& listener) override; int StartAbilityByCall(const Want& want, const sptr& connect, - const sptr& callerToken, int32_t accountId = DEFAULT_INVAL_VALUE, - bool isSilent = false, bool promotePriority = false, bool isVisible = false) override; + const sptr& callerToken, int32_t accountId = DEFAULT_INVAL_VALUE, bool isSilent = false, + bool promotePriority = false, bool isVisible = false, uint64_t specifiedFullTokenId = 0) override; int StartAbilityForPrelaunch(const Want &want, const int32_t frameNum) override; void CallRequestDone(const sptr& token, const sptr& callStub) override; int ReleaseCall(const sptr& connect, @@ -324,8 +324,8 @@ public: int UnRegisterMissionListener(const std::string& deviceId, const sptr& listener) override; int StartAbilityByCall(const Want& want, const sptr& connect, - const sptr& callerToken, int32_t accountId = DEFAULT_INVAL_VALUE, - bool isSilent = false, bool promotePriority = false, bool isVisible = false) override; + const sptr& callerToken, int32_t accountId = DEFAULT_INVAL_VALUE, bool isSilent = false, + bool promotePriority = false, bool isVisible = false, uint64_t specifiedFullTokenId = 0) override; int StartAbilityForPrelaunch(const Want &want, const int32_t frameNum) override; void CallRequestDone(const sptr& token, const sptr& callStub) override; virtual int32_t GetForegroundUIAbilities(std::vector &list) diff --git a/test/mock/frameworks_kits_appkit_test/include/mock_app_mgr_service.h b/test/mock/frameworks_kits_appkit_test/include/mock_app_mgr_service.h index b559dcf645..4d90dd6e3c 100644 --- a/test/mock/frameworks_kits_appkit_test/include/mock_app_mgr_service.h +++ b/test/mock/frameworks_kits_appkit_test/include/mock_app_mgr_service.h @@ -99,6 +99,7 @@ public: std::vector &instanceKeys, int32_t userId)); MOCK_METHOD1(SetSupportedProcessCacheSelf, int32_t(bool isSupported)); MOCK_METHOD2(IsProcessCacheSupported, int32_t(int32_t pid, bool &isSupported)); + MOCK_METHOD2(IsChildProcessSupported, int32_t(bool isNative, bool &isSupported)); MOCK_METHOD2(SetProcessCacheEnable, int32_t(int32_t pid, bool enable)); MOCK_METHOD2(SetSupportedProcessCache, int32_t(int32_t pid, bool isSupport)); MOCK_METHOD2(LockProcessCache, int32_t(int32_t pid, bool isLock)); diff --git a/test/mock/frameworks_kits_test/AMS/mock_ability_manager_service.h b/test/mock/frameworks_kits_test/AMS/mock_ability_manager_service.h index aa2299e44e..f2de92b45f 100644 --- a/test/mock/frameworks_kits_test/AMS/mock_ability_manager_service.h +++ b/test/mock/frameworks_kits_test/AMS/mock_ability_manager_service.h @@ -162,8 +162,8 @@ public: MOCK_METHOD1(GetAbilityRunningInfos, int(std::vector& info)); MOCK_METHOD2(GetExtensionRunningInfos, int(int upperLimit, std::vector& info)); MOCK_METHOD1(GetProcessRunningInfos, int(std::vector& info)); - MOCK_METHOD7(StartAbilityByCall, - int(const Want&, const sptr&, const sptr&, int32_t, bool, bool, bool)); + MOCK_METHOD8(StartAbilityByCall, int(const Want&, const sptr&, + const sptr&, int32_t, bool, bool, bool, uint64_t)); MOCK_METHOD1(GetMissionIdByToken, int32_t(const sptr& token)); MOCK_METHOD2(GetAbilityTokenByCalleeObj, void(const sptr &callStub, sptr &token)); MOCK_METHOD2(AcquireShareData, int32_t(const int32_t &missionId, const sptr &shareData)); diff --git a/test/mock/frameworks_kits_test/AMS/mock_serviceability_manager_service.h b/test/mock/frameworks_kits_test/AMS/mock_serviceability_manager_service.h index 76a3429f07..7576bd9641 100644 --- a/test/mock/frameworks_kits_test/AMS/mock_serviceability_manager_service.h +++ b/test/mock/frameworks_kits_test/AMS/mock_serviceability_manager_service.h @@ -166,8 +166,8 @@ public: MOCK_METHOD1(GetAbilityRunningInfos, int(std::vector& info)); MOCK_METHOD2(GetExtensionRunningInfos, int(int upperLimit, std::vector& info)); MOCK_METHOD1(GetProcessRunningInfos, int(std::vector& info)); - MOCK_METHOD7(StartAbilityByCall, - int(const Want&, const sptr&, const sptr&, int32_t, bool, bool, bool)); + MOCK_METHOD8(StartAbilityByCall, int(const Want&, const sptr&, + const sptr&, int32_t, bool, bool, bool, uint64_t)); MOCK_METHOD2(AcquireShareData, int32_t(const int32_t &missionId, const sptr &shareData)); MOCK_METHOD4(ShareDataDone, int32_t(const sptr &token, const int32_t &resultCode, const int32_t &uniqueId, WantParams &wantParam)); diff --git a/test/mock/services_appmgr_test/include/mock_app_mgr_service.h b/test/mock/services_appmgr_test/include/mock_app_mgr_service.h index 85784c38c2..309834b998 100644 --- a/test/mock/services_appmgr_test/include/mock_app_mgr_service.h +++ b/test/mock/services_appmgr_test/include/mock_app_mgr_service.h @@ -128,6 +128,7 @@ public: MOCK_METHOD0(IsFinalAppProcess, bool()); MOCK_METHOD1(SetSupportedProcessCacheSelf, int32_t(bool isSupport)); MOCK_METHOD2(IsProcessCacheSupported, int32_t(int32_t pid, bool &isSupported)); + MOCK_METHOD2(IsChildProcessSupported, int32_t(bool isNative, bool &isSupported)); MOCK_METHOD2(SetProcessCacheEnable, int32_t(int32_t pid, bool enable)); MOCK_METHOD2(SetSupportedProcessCache, int32_t(int32_t pid, bool isSupport)); MOCK_METHOD2(LockProcessCache, int32_t(int32_t pid, bool isLock)); @@ -148,6 +149,8 @@ public: MOCK_METHOD1(RegisterImageProcessStateObserver, int32_t(const sptr &observer)); MOCK_METHOD1(UnregisterImageProcessStateObserver, int32_t(const sptr &observer)); MOCK_METHOD2(GetAllAbilityInfos, int32_t(const int32_t pid, std::vector &infos)); + MOCK_METHOD2(EnableDelayedProcessExit, int32_t(int32_t pid, bool enabled)); + MOCK_METHOD1(CancelDelayedExitTask, void(int32_t pid)); virtual int StartUserTestProcess( const AAFwk::Want &want, const sptr &observer, const BundleInfo &bundleInfo, int32_t userId) { diff --git a/test/moduletest/ability_manager_client_test/ability_manager_client_test.cpp b/test/moduletest/ability_manager_client_test/ability_manager_client_test.cpp index a64313fb10..9a94ab2642 100644 --- a/test/moduletest/ability_manager_client_test/ability_manager_client_test.cpp +++ b/test/moduletest/ability_manager_client_test/ability_manager_client_test.cpp @@ -871,5 +871,23 @@ HWTEST_F(AbilityManagerClientTest, AbilityManagerClient_ConnectAbilityWithIndire TAG_LOGI(AAFwkTag::TEST, "AbilityManagerClient_ConnectAbilityWithIndirectCallerInfo_0100 end"); } + +/** + * @tc.number: AbilityManagerClient_StartSelfUIAbilityByAppContext_0100 + * @tc.name: StartSelfUIAbilityByAppContext + * @tc.desc: Test StartSelfUIAbilityByAppContext with valid parameters + * @tc.type: FUNC + */ +HWTEST_F(AbilityManagerClientTest, AbilityManagerClient_StartSelfUIAbilityByAppContext_0100, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "AbilityManagerClient_StartSelfUIAbilityByAppContext_0100 start"); + + Want want; + want.SetElementName("com.example.bundle", "com.example.ability"); + auto result = AbilityManagerClient::GetInstance()->StartSelfUIAbilityByAppContext(want); + EXPECT_EQ(result, ERR_OK); + + TAG_LOGI(AAFwkTag::TEST, "AbilityManagerClient_StartSelfUIAbilityByAppContext_0100 end"); +} } // namespace AAFwk } // namespace OHOS \ No newline at end of file diff --git a/test/moduletest/ability_manager_client_test/mock_iabilitymanager.h b/test/moduletest/ability_manager_client_test/mock_iabilitymanager.h index 45a46e142e..e2eb6b558f 100644 --- a/test/moduletest/ability_manager_client_test/mock_iabilitymanager.h +++ b/test/moduletest/ability_manager_client_test/mock_iabilitymanager.h @@ -289,7 +289,7 @@ public: } int StartAbilityByCall(const Want &want, const sptr &connect, const sptr &callerToken, int32_t accountId = DEFAULT_INVAL_VALUE, bool isSilent = false, - bool promotePriority = false, bool isVisible = false) override + bool promotePriority = false, bool isVisible = false, uint64_t specifiedFullTokenId = 0) override { return 0; } diff --git a/test/moduletest/mock/include/mock_ability_mgr_service.h b/test/moduletest/mock/include/mock_ability_mgr_service.h index f43bb72ef4..718c3d21fa 100644 --- a/test/moduletest/mock/include/mock_ability_mgr_service.h +++ b/test/moduletest/mock/include/mock_ability_mgr_service.h @@ -117,8 +117,8 @@ public: MOCK_METHOD1(GetAbilityRunningInfos, int(std::vector& info)); MOCK_METHOD2(GetExtensionRunningInfos, int(int upperLimit, std::vector& info)); MOCK_METHOD1(GetProcessRunningInfos, int(std::vector& info)); - MOCK_METHOD7(StartAbilityByCall, - int(const Want&, const sptr&, const sptr&, int32_t, bool, bool, bool)); + MOCK_METHOD8(StartAbilityByCall, int(const Want&, const sptr&, + const sptr&, int32_t, bool, bool, bool, uint64_t)); MOCK_METHOD2(AcquireShareData, int32_t(const int32_t &missionId, const sptr &shareData)); MOCK_METHOD4(ShareDataDone, int32_t(const sptr &token, const int32_t &resultCode, const int32_t &uniqueId, WantParams &wantParam)); diff --git a/test/new_test/unit_test/app_startup/ext_native_startup_manager_test/ext_native_startup_manager_test.cpp b/test/new_test/unit_test/app_startup/ext_native_startup_manager_test/ext_native_startup_manager_test.cpp index 5e3b878e61..e5b4b6adb4 100644 --- a/test/new_test/unit_test/app_startup/ext_native_startup_manager_test/ext_native_startup_manager_test.cpp +++ b/test/new_test/unit_test/app_startup/ext_native_startup_manager_test/ext_native_startup_manager_test.cpp @@ -207,6 +207,7 @@ HWTEST_F(ExtNativeStartupManagerTest, RegisterExtStartupTask_001, TestSize.Level HWTEST_F(ExtNativeStartupManagerTest, RegisterExtStartupTask_002, TestSize.Level1) { ExtNativeStartupManager::GetInstance().extNativeStartupTasks_.clear(); + ExtNativeStartupManager::GetInstance().phaseFlag_ = SchedulerPhase::None; auto &tasks = ExtNativeStartupManager::GetInstance().extNativeStartupTasks_[SchedulerPhase::PostLaunchApplication]; ASSERT_EQ(tasks.size(), 0); auto extNativeStartupTask1 = std::make_shared(TEST_EXT_NATIVE_STARTUP_TASK_NAME); diff --git a/test/unittest/BUILD.gn b/test/unittest/BUILD.gn index bb54035a8c..1c13088035 100644 --- a/test/unittest/BUILD.gn +++ b/test/unittest/BUILD.gn @@ -288,6 +288,7 @@ group("unittest") { "application_context_second_test:unittest", "application_context_test:unittest", "application_state_filter_test:unittest", + "arkts_script_test:unittest", "assert_fault_callback_death_mgr_test:unittest", "atomic_service_status_callback_proxy_test:unittest", "atomic_service_status_callback_stub_test:unittest", @@ -351,6 +352,7 @@ group("unittest") { "modular_object_ability_connection_test:unittest", "modular_object_extension_context_capi_test:unittest", "modular_object_extension_test:unittest", + "modular_object_extension_context_impl_event_handler_test:unittest", "modular_object_extension_context_impl_test:unittest", "data_ability_manager_test:unittest", "data_ability_observer_proxy_test:unittest", @@ -372,6 +374,7 @@ group("unittest") { "dfr_test:unittest", "dialog_session_info_test:unittest", "dialog_session_manager_test:unittest", + "disposed_observer_test:unittest", "disposed_rule_interceptor_test:unittest", "dlp_state_item_test:unittest", "dlp_utils_test:unittest", @@ -398,6 +401,7 @@ group("unittest") { "fault_data:unittest", "file_path_utils_test:unittest", "frameworks_kits_ability_ability_runtime_test:unittest", + "ui_extension_modal_callback_test:unittest", "frameworks_kits_ability_native_test:unittest", "frameworks_kits_appkit_native_test:unittest", "free_install_manager_second_test:unittest", @@ -459,6 +463,7 @@ group("unittest") { "mo_dispatcher_param_codec_test:unittest", "modal_system_app_freeze_uiextension_test:unittest", "modal_system_dialog_util_test:unittest", + "modular_object_connect_test:unittest", "modular_object_extension_manager_test:unittest", "modular_object_rdb_data_mgr_test:unittest", "multi_app_utils_test:unittest", @@ -467,6 +472,7 @@ group("unittest") { "multi_user_config_mgr_test:unittest", "napi_base_context_test:unittest", "napi_common_want_agent_test:unittest", + "native_ability_wrapper_test:unittest", "native_child_process_test:unittest", "native_module_util_test:unittest", "native_runtime_test:unittest", @@ -507,6 +513,10 @@ group("unittest") { "sender_info_test:unittest", "service_extension_context_test:unittest", "service_router_mgr_service_test:unittest", + "skill_execute_callback_proxy_test:unittest", + "skill_execute_callback_stub_test:unittest", + "skill_execute_param_test:unittest", + "skill_execute_result_test:unittest", "services/ability_util_test:unittest", "start_ability_utils_test:unittest", "start_options_impl_test:unittest", @@ -532,6 +542,7 @@ group("unittest") { "ui_extension_ability_test:unittest", "ui_extension_context_second_test:unittest", "ui_extension_context_test:unittest", + "ui_extension_test:unittest", "ui_extension_record_factory_test:unittest", "ui_extension_record_test:unittest", "ui_extension_utils_test:unittest", diff --git a/test/unittest/ability_business_error_test/ability_business_error_test.cpp b/test/unittest/ability_business_error_test/ability_business_error_test.cpp index b9179892b4..99f5448cd5 100644 --- a/test/unittest/ability_business_error_test/ability_business_error_test.cpp +++ b/test/unittest/ability_business_error_test/ability_business_error_test.cpp @@ -68,6 +68,8 @@ HWTEST_F(AbilityBusinessErrorTest, GetErrorMsg_3560000X, TestSize.Level2) { EXPECT_EQ(GetErrorMsg(AbilityErrorCode::ERROR_CODE_AGENT_ID_NOT_EXIST), "The specified agentId does not exist."); + EXPECT_EQ(GetErrorMsg(AbilityErrorCode::ERROR_CODE_AGENT_CARD_LIST_OUT_OF_RANGE), + "The number of AgentCards in the bundle reaches the limit."); EXPECT_EQ(GetErrorMsg(AbilityErrorCode::ERROR_CODE_MAX_CONNECTIONS_REACHED), "Maximum connections from the same caller have been reached. " "Please disconnect at least one agent extension beforehand."); @@ -107,6 +109,9 @@ HWTEST_F(AbilityBusinessErrorTest, GetJsErrorCodeByNativeError_0100, TestSize.Le result = GetJsErrorCodeByNativeError(OHOS::AAFwk::ERR_AGENT_CARD_DUPLICATE_REGISTER); EXPECT_TRUE(result == AbilityErrorCode::ERROR_CODE_AGENT_CARD_DUPLICATE_REGISTER); + result = GetJsErrorCodeByNativeError(OHOS::AAFwk::ERR_AGENT_CARD_LIST_OUT_OF_RANGE); + EXPECT_TRUE(result == AbilityErrorCode::ERROR_CODE_AGENT_CARD_LIST_OUT_OF_RANGE); + result = GetJsErrorCodeByNativeError(OHOS::AAFwk::ERR_LOW_CODE_AGENT_ALREADY_ACTIVE); EXPECT_TRUE(result == AbilityErrorCode::ERROR_CODE_LOW_CODE_AGENT_ALREADY_ACTIVE); } diff --git a/test/unittest/ability_manager_client_branch_second_test/ability_manager_stub_mock_second_test.h b/test/unittest/ability_manager_client_branch_second_test/ability_manager_stub_mock_second_test.h index 0ec4fe4ba2..7818099927 100644 --- a/test/unittest/ability_manager_client_branch_second_test/ability_manager_stub_mock_second_test.h +++ b/test/unittest/ability_manager_client_branch_second_test/ability_manager_stub_mock_second_test.h @@ -355,7 +355,7 @@ public: int StartAbilityByCall(const Want& want, const sptr& connect, const sptr& callerToken, int32_t userId = DEFAULT_INVAL_VALUE, bool isSilent = false, - bool promotePriority = false, bool isVisible = false) override + bool promotePriority = false, bool isVisible = false, uint64_t specifiedFullTokenId = 0) override { return 0; } diff --git a/test/unittest/ability_manager_client_branch_test/ability_manager_client_branch_test.cpp b/test/unittest/ability_manager_client_branch_test/ability_manager_client_branch_test.cpp index 21e736c4a2..52e7b2dcd6 100644 --- a/test/unittest/ability_manager_client_branch_test/ability_manager_client_branch_test.cpp +++ b/test/unittest/ability_manager_client_branch_test/ability_manager_client_branch_test.cpp @@ -2069,6 +2069,38 @@ HWTEST_F(AbilityManagerClientBranchTest, ExecuteIntent_0100, TestSize.Level1) GTEST_LOG_(INFO) << "ExecuteIntent_0100 end"; } +/** + * @tc.name: AbilityManagerClient_QueryEntityInfo_0100 + * @tc.desc: QueryEntityInfo + * @tc.type: FUNC + */ +HWTEST_F(AbilityManagerClientBranchTest, QueryEntityInfo_0100, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "QueryEntityInfo_0100 start"; + uint64_t key = 1; + sptr callerToken = nullptr; + const InsightIntentQueryParam param; + auto result = client_->QueryEntityInfo(key, callerToken, param); + EXPECT_EQ(ERR_OK, result); + GTEST_LOG_(INFO) << "QueryEntityInfo_0100 end"; +} + +/** + * @tc.name: AbilityManagerClient_QueryEntityInfo_0200 + * @tc.desc: QueryEntityInfo default implementation return + * @tc.type: FUNC + */ +HWTEST_F(AbilityManagerClientBranchTest, QueryEntityInfo_0200, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "QueryEntityInfo_0200 start"; + uint64_t key = 1; + sptr callerToken = nullptr; + const InsightIntentQueryParam param; + auto result = client_->QueryEntityInfo(key, callerToken, param); + EXPECT_EQ(ERR_OK, result); + GTEST_LOG_(INFO) << "QueryEntityInfo_0200 end"; +} + /** * @tc.name: AbilityManagerClient_ExecuteInsightIntentDone_0100 * @tc.desc: ExecuteInsightIntentDone @@ -2162,6 +2194,57 @@ HWTEST_F(AbilityManagerClientBranchTest, RequestModalUIExtension_0100, TestSize. GTEST_LOG_(INFO) << "RequestModalUIExtension_0100 end"; } +/** + * @tc.name: AbilityManagerClient_RequestModalUIExtensionWithAccount_0100 + * @tc.desc: RequestModalUIExtensionWithAccount with valid proxy + * @tc.type: FUNC + */ +HWTEST_F(AbilityManagerClientBranchTest, RequestModalUIExtensionWithAccount_0100, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "RequestModalUIExtensionWithAccount_0100 start"; + EXPECT_TRUE(client_ != nullptr); + Want want; + int32_t accountId = 100; + auto result = client_->RequestModalUIExtensionWithAccount(want, accountId); + EXPECT_EQ(result, ERR_OK); + GTEST_LOG_(INFO) << "RequestModalUIExtensionWithAccount_0100 end"; +} + +/** + * @tc.name: AbilityManagerClient_RequestModalUIExtensionWithAccount_0300 + * @tc.desc: RequestModalUIExtensionWithAccount with various accountIds + * @tc.type: FUNC + */ +HWTEST_F(AbilityManagerClientBranchTest, RequestModalUIExtensionWithAccount_0300, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "RequestModalUIExtensionWithAccount_0300 start"; + EXPECT_TRUE(client_ != nullptr); + Want want; + ElementName element("device", "com.test.modal", "ModalUIExtension"); + want.SetElement(element); + + int32_t accountId = -1; + auto result = client_->RequestModalUIExtensionWithAccount(want, accountId); + EXPECT_EQ(result, ERR_OK); + GTEST_LOG_(INFO) << "RequestModalUIExtensionWithAccount_0300 end"; +} + +/** + * @tc.name: AbilityManagerClient_RequestModalUIExtensionWithAccount_0400 + * @tc.desc: RequestModalUIExtensionWithAccount with default accountId + * @tc.type: FUNC + */ +HWTEST_F(AbilityManagerClientBranchTest, RequestModalUIExtensionWithAccount_0400, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "RequestModalUIExtensionWithAccount_0400 start"; + EXPECT_TRUE(client_ != nullptr); + Want want; + int32_t accountId = 0; + auto result = client_->RequestModalUIExtensionWithAccount(want, accountId); + EXPECT_EQ(result, ERR_OK); + GTEST_LOG_(INFO) << "RequestModalUIExtensionWithAccount_0400 end"; +} + /** * @tc.name: AbilityManagerClient_UpdateSessionInfoBySCB_0100 * @tc.desc: UpdateSessionInfoBySCB @@ -2816,6 +2899,54 @@ HWTEST_F(AbilityManagerClientBranchTest, StartSelfUIAbilityInCurrentProcess_0100 EXPECT_EQ(client_->StartSelfUIAbilityInCurrentProcess(want, specifiedFlag, startOptions, false, nullptr), ERR_OK); } +/** + * @tc.name: StartSelfUIAbilityInChildProcess_0100 + * @tc.desc: Test StartSelfUIAbilityInChildProcess with normal parameters + * @tc.type: FUNC + */ +HWTEST_F(AbilityManagerClientBranchTest, StartSelfUIAbilityInChildProcess_0100, TestSize.Level1) +{ + Want want; + want.SetElementName("com.test.bundle", "TestAbility"); + std::string specifiedFlag = "testFlag"; + sptr callerToken = nullptr; + EXPECT_CALL(*mock_, StartSelfUIAbilityInChildProcess(_, specifiedFlag, callerToken)).WillOnce(Return(ERR_OK)); + EXPECT_EQ(client_->StartSelfUIAbilityInChildProcess(want, specifiedFlag, callerToken), ERR_OK); +} + +/** + * @tc.name: StartSelfUIAbilityInChildProcess_0200 + * @tc.desc: Test StartSelfUIAbilityInChildProcess with service return error + * @tc.type: FUNC + */ +HWTEST_F(AbilityManagerClientBranchTest, StartSelfUIAbilityInChildProcess_0200, TestSize.Level1) +{ + Want want; + want.SetElementName("com.test.bundle", "TestAbility"); + std::string specifiedFlag = "testFlag"; + sptr callerToken = nullptr; + EXPECT_CALL(*mock_, StartSelfUIAbilityInChildProcess(_, specifiedFlag, callerToken)) + .WillOnce(Return(ERR_CAPABILITY_NOT_SUPPORT)); + EXPECT_EQ(client_->StartSelfUIAbilityInChildProcess(want, specifiedFlag, callerToken), + ERR_CAPABILITY_NOT_SUPPORT); +} + +/** + * @tc.name: StartSelfUIAbilityInChildProcess_0300 + * @tc.desc: Test StartSelfUIAbilityInChildProcess with empty want + * @tc.type: FUNC + */ +HWTEST_F(AbilityManagerClientBranchTest, StartSelfUIAbilityInChildProcess_0300, TestSize.Level1) +{ + Want want; + std::string specifiedFlag = ""; + sptr callerToken = nullptr; + EXPECT_CALL(*mock_, StartSelfUIAbilityInChildProcess(_, specifiedFlag, callerToken)) + .WillOnce(Return(START_UI_ABILITIES_NOT_SUPPORT_IMPLICIT_START)); + EXPECT_EQ(client_->StartSelfUIAbilityInChildProcess(want, specifiedFlag, callerToken), + START_UI_ABILITIES_NOT_SUPPORT_IMPLICIT_START); +} + /** * @tc.name: QuerySelfModularObjectExtensionInfos_0100 * @tc.desc: QuerySelfModularObjectExtensionInfos @@ -2853,5 +2984,17 @@ HWTEST_F(AbilityManagerClientBranchTest, StartAbilityForPrelaunch_001, TestSize. EXPECT_EQ(client_->StartAbilityForPrelaunch(want, 0), 0); } +/** + * @tc.name: AbilityManagerClient_StartSelf_0100 + * @tc.desc: StartSelf + * @tc.type: FUNC + */ +HWTEST_F(AbilityManagerClientBranchTest, AbilityManagerClient_StartSelf_0100, TestSize.Level1) +{ + sptr token = nullptr; + EXPECT_CALL(*mock_, StartSelf(_)).WillOnce(Return(ERR_OK)); + auto result = client_->StartSelf(token); + EXPECT_EQ(ERR_OK, result); +} } // namespace AAFwk } // namespace OHOS diff --git a/test/unittest/ability_manager_client_branch_test/ability_manager_stub_mock_test.h b/test/unittest/ability_manager_client_branch_test/ability_manager_stub_mock_test.h index 9788f1af5c..8c5e08e9ba 100644 --- a/test/unittest/ability_manager_client_branch_test/ability_manager_stub_mock_test.h +++ b/test/unittest/ability_manager_client_branch_test/ability_manager_stub_mock_test.h @@ -374,7 +374,7 @@ public: int StartAbilityByCall(const Want& want, const sptr& connect, const sptr& callerToken, int32_t userId = DEFAULT_INVAL_VALUE, bool isSilent = false, - bool promotePriority = false, bool isVisible = false) override + bool promotePriority = false, bool isVisible = false, uint64_t specifiedFullTokenId = 0) override { return 0; } @@ -462,6 +462,8 @@ public: const InsightIntentExecuteResult &result)); MOCK_METHOD5(StartAbilityWithSpecifyTokenId, int(const Want& want, const sptr& callerToken, uint32_t specifyTokenId, int32_t userId, int requestCode)); + MOCK_METHOD1(StartSelf, int(sptr token)); + MOCK_METHOD3(StartSelfUIAbilityInChildProcess, ErrCode(const Want &, const std::string &, sptr)); }; } // namespace AAFwk } // namespace OHOS diff --git a/test/unittest/ability_manager_client_branch_third_test/ability_manager_client_branch_third_test.cpp b/test/unittest/ability_manager_client_branch_third_test/ability_manager_client_branch_third_test.cpp index e56d8773f9..b3bd060bb3 100644 --- a/test/unittest/ability_manager_client_branch_third_test/ability_manager_client_branch_third_test.cpp +++ b/test/unittest/ability_manager_client_branch_third_test/ability_manager_client_branch_third_test.cpp @@ -36,6 +36,7 @@ #include "mock_scene_session_manager_lite.h" #include "session/host/include/session.h" #include "status_bar_delegate_interface.h" +#include "skill_execute_result.h" using namespace testing::ext; using namespace testing; @@ -1421,5 +1422,111 @@ HWTEST_F(AbilityManagerClientBranchThirdTest, DisplayInfoTest_0100, TestSize.Lev EXPECT_EQ(displayInfo.displayName, newDisplayInfo->displayName); delete newDisplayInfo; } + +/** + * @tc.name: ExecuteInAppSkill_0100 + * @tc.desc: Test ExecuteInAppSkill with proxy not connected + * @tc.type: FUNC + */ +HWTEST_F(AbilityManagerClientBranchThirdTest, ExecuteInAppSkill_0100, TestSize.Level1) +{ + client_->proxy_ = nullptr; + EXPECT_CALL(Rosen::SceneBoardJudgement::GetInstance(), MockIsSceneBoardEnabled()) + .WillRepeatedly(testing::Return(false)); + EXPECT_CALL(*mockSystemAbility_, GetSystemAbility(testing::_)).WillRepeatedly(Return(nullptr)); + SystemAbilityManagerClient::GetInstance().systemAbilityManager_ = mockSystemAbility_; + + auto skillArgs = std::make_shared(); + sptr callback = nullptr; + auto ret = client_->ExecuteInAppSkill("bundle", "module", "skill", "path", "func", skillArgs, callback); + EXPECT_EQ(ret, ABILITY_SERVICE_NOT_CONNECTED); +} + +/** + * @tc.name: ExecuteInAppSkill_0200 + * @tc.desc: Test ExecuteInAppSkill with proxy connected + * @tc.type: FUNC + */ +HWTEST_F(AbilityManagerClientBranchThirdTest, ExecuteInAppSkill_0200, TestSize.Level1) +{ + client_->proxy_ = mock_; + auto skillArgs = std::make_shared(); + sptr callback = nullptr; + EXPECT_CALL(*mock_, ExecuteInAppSkill(_, _, _, _, _, _, _)) + .Times(1) + .WillOnce(Return(ERR_OK)); + auto ret = client_->ExecuteInAppSkill("bundle", "module", "skill", "path", "func", skillArgs, callback); + EXPECT_EQ(ret, ERR_OK); +} + +/** + * @tc.name: ExecuteSkillDone_0100 + * @tc.desc: Test ExecuteSkillDone with proxy not connected + * @tc.type: FUNC + */ +HWTEST_F(AbilityManagerClientBranchThirdTest, ExecuteSkillDone_0100, TestSize.Level1) +{ + client_->proxy_ = nullptr; + EXPECT_CALL(Rosen::SceneBoardJudgement::GetInstance(), MockIsSceneBoardEnabled()) + .WillRepeatedly(testing::Return(false)); + EXPECT_CALL(*mockSystemAbility_, GetSystemAbility(testing::_)).WillRepeatedly(Return(nullptr)); + SystemAbilityManagerClient::GetInstance().systemAbilityManager_ = mockSystemAbility_; + + sptr token = sptr::MakeSptr(); + AppExecFwk::SkillExecuteResult result; + auto ret = client_->ExecuteSkillDone(token, "requestCode", 0, result); + EXPECT_EQ(ret, ABILITY_SERVICE_NOT_CONNECTED); +} + +/** + * @tc.name: ExecuteSkillDone_0200 + * @tc.desc: Test ExecuteSkillDone with proxy connected + * @tc.type: FUNC + */ +HWTEST_F(AbilityManagerClientBranchThirdTest, ExecuteSkillDone_0200, TestSize.Level1) +{ + client_->proxy_ = mock_; + sptr token = sptr::MakeSptr(); + AppExecFwk::SkillExecuteResult result; + EXPECT_CALL(*mock_, ExecuteSkillDone(_, _, _, _)) + .Times(1) + .WillOnce(Return(ERR_OK)); + auto ret = client_->ExecuteSkillDone(token, "requestCode", 0, result); + EXPECT_EQ(ret, ERR_OK); +} + +/** + * @tc.name: QuerySkillType_0100 + * @tc.desc: Test QuerySkillType with proxy not connected + * @tc.type: FUNC + */ +HWTEST_F(AbilityManagerClientBranchThirdTest, QuerySkillType_0100, TestSize.Level1) +{ + client_->proxy_ = nullptr; + EXPECT_CALL(Rosen::SceneBoardJudgement::GetInstance(), MockIsSceneBoardEnabled()) + .WillRepeatedly(testing::Return(false)); + EXPECT_CALL(*mockSystemAbility_, GetSystemAbility(testing::_)).WillRepeatedly(Return(nullptr)); + SystemAbilityManagerClient::GetInstance().systemAbilityManager_ = mockSystemAbility_; + + int32_t skillType = 0; + auto ret = client_->QuerySkillType("bundle", "module", "skill", skillType); + EXPECT_EQ(ret, ABILITY_SERVICE_NOT_CONNECTED); +} + +/** + * @tc.name: QuerySkillType_0200 + * @tc.desc: Test QuerySkillType with proxy connected + * @tc.type: FUNC + */ +HWTEST_F(AbilityManagerClientBranchThirdTest, QuerySkillType_0200, TestSize.Level1) +{ + client_->proxy_ = mock_; + int32_t skillType = 0; + EXPECT_CALL(*mock_, QuerySkillType(_, _, _, _)) + .Times(1) + .WillOnce(Return(ERR_OK)); + auto ret = client_->QuerySkillType("bundle", "module", "skill", skillType); + EXPECT_EQ(ret, ERR_OK); +} } // namespace AAFwk } // namespace OHOS \ No newline at end of file diff --git a/test/unittest/ability_manager_client_branch_third_test/mock/include/ability_manager_stub_mock_test.h b/test/unittest/ability_manager_client_branch_third_test/mock/include/ability_manager_stub_mock_test.h index a29afdeb2d..4c509dcaa4 100644 --- a/test/unittest/ability_manager_client_branch_third_test/mock/include/ability_manager_stub_mock_test.h +++ b/test/unittest/ability_manager_client_branch_third_test/mock/include/ability_manager_stub_mock_test.h @@ -326,7 +326,7 @@ public: int StartAbilityByCall(const Want& want, const sptr& connect, const sptr& callerToken, int32_t userId = DEFAULT_INVAL_VALUE, bool isSilent = false, - bool promotePriority = false, bool isVisible = false) override + bool promotePriority = false, bool isVisible = false, uint64_t specifiedFullTokenId = 0) override { return 0; } @@ -433,6 +433,13 @@ public: MOCK_METHOD(void, CompleteFirstFrameDrawing, (int32_t sessionId), (override)); #endif MOCK_METHOD(int32_t, GetUserLockedBundleList, (int32_t, std::unordered_set &), (override)); + MOCK_METHOD7(ExecuteInAppSkill, int32_t(const std::string &, const std::string &, + const std::string &, const std::string &, const std::string &, + const std::shared_ptr &, const sptr &)); + MOCK_METHOD4(ExecuteSkillDone, int32_t(const sptr &, const std::string &, + int32_t, const AppExecFwk::SkillExecuteResult &)); + MOCK_METHOD4(QuerySkillType, int32_t(const std::string &, const std::string &, + const std::string &, int32_t &)); }; // namespace AAFwk } // namespace OHOS } diff --git a/test/unittest/ability_manager_event_subscriber_test/BUILD.gn b/test/unittest/ability_manager_event_subscriber_test/BUILD.gn index 159ab868a2..a3a5fe1859 100644 --- a/test/unittest/ability_manager_event_subscriber_test/BUILD.gn +++ b/test/unittest/ability_manager_event_subscriber_test/BUILD.gn @@ -48,6 +48,7 @@ ohos_unittest("ability_manager_event_subscriber_test") { "${ability_runtime_services_path}/common:app_util", "${ability_runtime_services_path}/common:perm_verification", "${ability_runtime_services_path}/common:task_handler_wrap", + "${ability_runtime_services_path}/common:user_controller", "${ability_runtime_test_path}/mock/services_abilitymgr_test/libs/aakit:aakit_mock", ] diff --git a/test/unittest/ability_manager_proxy_fifth_test/ability_manager_proxy_fifth_test.cpp b/test/unittest/ability_manager_proxy_fifth_test/ability_manager_proxy_fifth_test.cpp index 54d1511865..98efe8fe3f 100644 --- a/test/unittest/ability_manager_proxy_fifth_test/ability_manager_proxy_fifth_test.cpp +++ b/test/unittest/ability_manager_proxy_fifth_test/ability_manager_proxy_fifth_test.cpp @@ -908,5 +908,69 @@ HWTEST_F(AbilityManagerProxyFifthTest, OpenFile_0100, TestSize.Level1) auto res1 = proxy_->OpenFile(uri, flag); EXPECT_EQ(res1, INVALID_PARAMETERS_ERR); } + +/** + * @tc.name: StartSelfUIAbilityInChildProcess_0100 + * @tc.desc: Test StartSelfUIAbilityInChildProcess with normal parameters + * @tc.type: FUNC + */ +HWTEST_F(AbilityManagerProxyFifthTest, StartSelfUIAbilityInChildProcess_0100, TestSize.Level1) +{ + Want want; + want.SetElementName("com.test.bundle", "TestAbility"); + std::string specifiedFlag = "testFlag"; + sptr callerToken = nullptr; + EXPECT_CALL(*mock_, SendRequest(_, _, _, _)).WillOnce(Return(NO_ERROR)); + auto res = proxy_->StartSelfUIAbilityInChildProcess(want, specifiedFlag, callerToken); + EXPECT_EQ(res, ZERO); +} + +/** + * @tc.name: StartSelfUIAbilityInChildProcess_0200 + * @tc.desc: Test StartSelfUIAbilityInChildProcess with error return + * @tc.type: FUNC + */ +HWTEST_F(AbilityManagerProxyFifthTest, StartSelfUIAbilityInChildProcess_0200, TestSize.Level1) +{ + Want want; + want.SetElementName("com.test.bundle", "TestAbility"); + std::string specifiedFlag = "testFlag"; + sptr callerToken = nullptr; + EXPECT_CALL(*mock_, SendRequest(_, _, _, _)).WillOnce(Return(INVALID_PARAMETERS_ERR)); + auto res = proxy_->StartSelfUIAbilityInChildProcess(want, specifiedFlag, callerToken); + EXPECT_EQ(res, INVALID_PARAMETERS_ERR); +} + +/** + * @tc.name: StartSelfUIAbilityInChildProcess_0300 + * @tc.desc: Test StartSelfUIAbilityInChildProcess with callerToken + * @tc.type: FUNC + */ +HWTEST_F(AbilityManagerProxyFifthTest, StartSelfUIAbilityInChildProcess_0300, TestSize.Level1) +{ + Want want; + want.SetElementName("com.test.bundle", "TestAbility"); + std::string specifiedFlag = "testFlag"; + sptr callerToken = new IRemoteObjectMocker(); + EXPECT_CALL(*mock_, SendRequest(_, _, _, _)).WillOnce(Return(NO_ERROR)); + auto res = proxy_->StartSelfUIAbilityInChildProcess(want, specifiedFlag, callerToken); + EXPECT_EQ(res, ZERO); +} + +/** + * @tc.name: StartSelfUIAbilityInChildProcess_0400 + * @tc.desc: Test StartSelfUIAbilityInChildProcess with empty specifiedFlag + * @tc.type: FUNC + */ +HWTEST_F(AbilityManagerProxyFifthTest, StartSelfUIAbilityInChildProcess_0400, TestSize.Level1) +{ + Want want; + want.SetElementName("com.test.bundle", "TestAbility"); + std::string specifiedFlag = ""; + sptr callerToken = nullptr; + EXPECT_CALL(*mock_, SendRequest(_, _, _, _)).WillOnce(Return(NO_ERROR)); + auto res = proxy_->StartSelfUIAbilityInChildProcess(want, specifiedFlag, callerToken); + EXPECT_EQ(res, ZERO); +} } // namespace AAFwk } // namespace OHOS diff --git a/test/unittest/ability_manager_proxy_fifth_test/ability_manager_stub_mock.h b/test/unittest/ability_manager_proxy_fifth_test/ability_manager_stub_mock.h index 367f62a7ce..69154cf688 100644 --- a/test/unittest/ability_manager_proxy_fifth_test/ability_manager_stub_mock.h +++ b/test/unittest/ability_manager_proxy_fifth_test/ability_manager_stub_mock.h @@ -359,8 +359,8 @@ public: MOCK_METHOD1(GetAbilityRunningInfos, int(std::vector& info)); MOCK_METHOD2(GetExtensionRunningInfos, int(int upperLimit, std::vector& info)); MOCK_METHOD1(GetProcessRunningInfos, int(std::vector& info)); - MOCK_METHOD7(StartAbilityByCall, - int(const Want&, const sptr&, const sptr&, int32_t, bool, bool, bool)); + MOCK_METHOD8(StartAbilityByCall, int(const Want&, const sptr&, + const sptr&, int32_t, bool, bool, bool, uint64_t)); MOCK_METHOD2(AcquireShareData, int32_t(const int32_t &missionId, const sptr &shareData)); MOCK_METHOD4(ShareDataDone, int32_t(const sptr &token, const int32_t &resultCode, const int32_t &uniqueId, WantParams &wantParam)); diff --git a/test/unittest/ability_manager_proxy_fourth_test/ability_manager_stub_mock.h b/test/unittest/ability_manager_proxy_fourth_test/ability_manager_stub_mock.h index 57bcd80350..c1b023956d 100644 --- a/test/unittest/ability_manager_proxy_fourth_test/ability_manager_stub_mock.h +++ b/test/unittest/ability_manager_proxy_fourth_test/ability_manager_stub_mock.h @@ -359,8 +359,8 @@ public: MOCK_METHOD1(GetAbilityRunningInfos, int(std::vector& info)); MOCK_METHOD2(GetExtensionRunningInfos, int(int upperLimit, std::vector& info)); MOCK_METHOD1(GetProcessRunningInfos, int(std::vector& info)); - MOCK_METHOD7(StartAbilityByCall, - int(const Want&, const sptr&, const sptr&, int32_t, bool, bool, bool)); + MOCK_METHOD8(StartAbilityByCall, int(const Want&, const sptr&, + const sptr&, int32_t, bool, bool, bool, uint64_t)); MOCK_METHOD2(AcquireShareData, int32_t(const int32_t &missionId, const sptr &shareData)); MOCK_METHOD4(ShareDataDone, int32_t(const sptr &token, const int32_t &resultCode, const int32_t &uniqueId, WantParams &wantParam)); diff --git a/test/unittest/ability_manager_proxy_second_test/ability_manager_stub_mock.h b/test/unittest/ability_manager_proxy_second_test/ability_manager_stub_mock.h index f4e46129ca..98010ea59c 100644 --- a/test/unittest/ability_manager_proxy_second_test/ability_manager_stub_mock.h +++ b/test/unittest/ability_manager_proxy_second_test/ability_manager_stub_mock.h @@ -359,8 +359,8 @@ public: MOCK_METHOD1(GetAbilityRunningInfos, int(std::vector& info)); MOCK_METHOD2(GetExtensionRunningInfos, int(int upperLimit, std::vector& info)); MOCK_METHOD1(GetProcessRunningInfos, int(std::vector& info)); - MOCK_METHOD7(StartAbilityByCall, - int(const Want&, const sptr&, const sptr&, int32_t, bool, bool, bool)); + MOCK_METHOD8(StartAbilityByCall, int(const Want&, const sptr&, + const sptr&, int32_t, bool, bool, bool, uint64_t)); MOCK_METHOD2(AcquireShareData, int32_t(const int32_t &missionId, const sptr &shareData)); MOCK_METHOD4(ShareDataDone, int32_t(const sptr &token, const int32_t &resultCode, const int32_t &uniqueId, WantParams &wantParam)); diff --git a/test/unittest/ability_manager_proxy_sixth_test/ability_manager_proxy_sixth_test.cpp b/test/unittest/ability_manager_proxy_sixth_test/ability_manager_proxy_sixth_test.cpp index cc59c1a242..011f2253c0 100644 --- a/test/unittest/ability_manager_proxy_sixth_test/ability_manager_proxy_sixth_test.cpp +++ b/test/unittest/ability_manager_proxy_sixth_test/ability_manager_proxy_sixth_test.cpp @@ -27,6 +27,7 @@ #include "hilog_tag_wrapper.h" #include "mission_snapshot.h" #include "want_sender_info.h" +#include "skill_execute_result.h" using namespace testing::ext; using namespace testing; @@ -1148,5 +1149,99 @@ HWTEST_F(AbilityManagerProxySixthTest, AbilityManagerProxy_QueryCallerTokenIdFor auto res = proxy_->QueryCallerTokenIdForAnco(userId, asCallerForAncoSessionId, callerTokenId); EXPECT_EQ(res, 0); } + +/** + * @tc.name: AbilityManagerProxy_ExecuteInAppSkill_001 + * @tc.desc: ExecuteInAppSkill with SendRequest success + * @tc.type: FUNC + */ +HWTEST_F(AbilityManagerProxySixthTest, AbilityManagerProxy_ExecuteInAppSkill_001, TestSize.Level1) +{ + EXPECT_CALL(*mock_, SendRequest(_, _, _, _)) + .Times(1) + .WillOnce(Invoke(mock_.GetRefPtr(), &AbilityManagerStubMock::InvokeSendRequest)); + auto skillArgs = std::make_shared(); + sptr callback = nullptr; + auto res = proxy_->ExecuteInAppSkill("bundle", "module", "skill", "path", "func", skillArgs, callback); + EXPECT_EQ(res, 0); +} + +/** + * @tc.name: AbilityManagerProxy_ExecuteInAppSkill_002 + * @tc.desc: ExecuteInAppSkill with SendRequest failure + * @tc.type: FUNC + */ +HWTEST_F(AbilityManagerProxySixthTest, AbilityManagerProxy_ExecuteInAppSkill_002, TestSize.Level1) +{ + EXPECT_CALL(*mock_, SendRequest(_, _, _, _)) + .Times(1) + .WillOnce(Return(-1)); + auto skillArgs = std::make_shared(); + sptr callback = nullptr; + auto res = proxy_->ExecuteInAppSkill("bundle", "module", "skill", "path", "func", skillArgs, callback); + EXPECT_EQ(res, -1); +} + +/** + * @tc.name: AbilityManagerProxy_ExecuteSkillDone_001 + * @tc.desc: ExecuteSkillDone with SendRequest success + * @tc.type: FUNC + */ +HWTEST_F(AbilityManagerProxySixthTest, AbilityManagerProxy_ExecuteSkillDone_001, TestSize.Level1) +{ + EXPECT_CALL(*mock_, SendRequest(_, _, _, _)) + .Times(1) + .WillOnce(Invoke(mock_.GetRefPtr(), &AbilityManagerStubMock::InvokeSendRequest)); + sptr token = sptr::MakeSptr(); + AppExecFwk::SkillExecuteResult result; + auto res = proxy_->ExecuteSkillDone(token, "requestCode", 0, result); + EXPECT_EQ(res, 0); +} + +/** + * @tc.name: AbilityManagerProxy_ExecuteSkillDone_002 + * @tc.desc: ExecuteSkillDone with SendRequest failure + * @tc.type: FUNC + */ +HWTEST_F(AbilityManagerProxySixthTest, AbilityManagerProxy_ExecuteSkillDone_002, TestSize.Level1) +{ + EXPECT_CALL(*mock_, SendRequest(_, _, _, _)) + .Times(1) + .WillOnce(Return(-1)); + sptr token = sptr::MakeSptr(); + AppExecFwk::SkillExecuteResult result; + auto res = proxy_->ExecuteSkillDone(token, "requestCode", 0, result); + EXPECT_EQ(res, -1); +} + +/** + * @tc.name: AbilityManagerProxy_QuerySkillType_001 + * @tc.desc: QuerySkillType with SendRequest success + * @tc.type: FUNC + */ +HWTEST_F(AbilityManagerProxySixthTest, AbilityManagerProxy_QuerySkillType_001, TestSize.Level1) +{ + EXPECT_CALL(*mock_, SendRequest(_, _, _, _)) + .Times(1) + .WillOnce(Invoke(mock_.GetRefPtr(), &AbilityManagerStubMock::InvokeSendRequest)); + int32_t skillType = 0; + auto res = proxy_->QuerySkillType("bundle", "module", "skill", skillType); + EXPECT_EQ(res, 0); +} + +/** + * @tc.name: AbilityManagerProxy_QuerySkillType_002 + * @tc.desc: QuerySkillType with SendRequest failure + * @tc.type: FUNC + */ +HWTEST_F(AbilityManagerProxySixthTest, AbilityManagerProxy_QuerySkillType_002, TestSize.Level1) +{ + EXPECT_CALL(*mock_, SendRequest(_, _, _, _)) + .Times(1) + .WillOnce(Return(-1)); + int32_t skillType = 0; + auto res = proxy_->QuerySkillType("bundle", "module", "skill", skillType); + EXPECT_EQ(res, -1); +} } // namespace AAFwk } // namespace OHOS diff --git a/test/unittest/ability_manager_proxy_sixth_test/ability_manager_stub_mock.h b/test/unittest/ability_manager_proxy_sixth_test/ability_manager_stub_mock.h index f4e46129ca..0873edadb8 100644 --- a/test/unittest/ability_manager_proxy_sixth_test/ability_manager_stub_mock.h +++ b/test/unittest/ability_manager_proxy_sixth_test/ability_manager_stub_mock.h @@ -359,8 +359,8 @@ public: MOCK_METHOD1(GetAbilityRunningInfos, int(std::vector& info)); MOCK_METHOD2(GetExtensionRunningInfos, int(int upperLimit, std::vector& info)); MOCK_METHOD1(GetProcessRunningInfos, int(std::vector& info)); - MOCK_METHOD7(StartAbilityByCall, - int(const Want&, const sptr&, const sptr&, int32_t, bool, bool, bool)); + MOCK_METHOD8(StartAbilityByCall, int(const Want&, const sptr&, + const sptr&, int32_t, bool, bool, bool, uint64_t)); MOCK_METHOD2(AcquireShareData, int32_t(const int32_t &missionId, const sptr &shareData)); MOCK_METHOD4(ShareDataDone, int32_t(const sptr &token, const int32_t &resultCode, const int32_t &uniqueId, WantParams &wantParam)); @@ -433,6 +433,13 @@ public: const InsightIntentExecuteParam ¶m)); MOCK_METHOD3(ExecuteInsightIntentDone, int32_t(const sptr &token, uint64_t intentId, const InsightIntentExecuteResult &result)); + MOCK_METHOD7(ExecuteInAppSkill, int32_t(const std::string &, const std::string &, + const std::string &, const std::string &, const std::string &, + const std::shared_ptr &, const sptr &)); + MOCK_METHOD4(ExecuteSkillDone, int32_t(const sptr &, const std::string &, + int32_t, const AppExecFwk::SkillExecuteResult &)); + MOCK_METHOD4(QuerySkillType, int32_t(const std::string &, const std::string &, + const std::string &, int32_t &)); }; } // namespace AAFwk } // namespace OHOS diff --git a/test/unittest/ability_manager_proxy_test/ability_manager_proxy_test.cpp b/test/unittest/ability_manager_proxy_test/ability_manager_proxy_test.cpp index 93881be379..b67e4ad249 100644 --- a/test/unittest/ability_manager_proxy_test/ability_manager_proxy_test.cpp +++ b/test/unittest/ability_manager_proxy_test/ability_manager_proxy_test.cpp @@ -3905,5 +3905,181 @@ HWTEST_F(AbilityManagerProxyTest, AbilityManagerProxy_RequestModalUIExtensionWit TAG_LOGI(AAFwkTag::TEST, "AbilityManagerProxy_RequestModalUIExtensionWithAccount_001 end"); } + +/* + * Feature: AbilityManagerService + * Function: RequestModalUIExtensionWithAccount + * SubFunction: NA + * FunctionPoints: AbilityManagerProxy RequestModalUIExtensionWithAccount + * EnvConditions: NA + * CaseDescription: Verify RequestModalUIExtensionWithAccount with SendRequest error + */ +HWTEST_F(AbilityManagerProxyTest, AbilityManagerProxy_RequestModalUIExtensionWithAccount_002, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "AbilityManagerProxy_RequestModalUIExtensionWithAccount_002 start"); + + EXPECT_CALL(*mock_, SendRequest(_, _, _, _)) + .Times(1) + .WillOnce(Invoke(mock_.GetRefPtr(), &AbilityManagerStubMock::InvokeErrorSendRequest)); + + Want want; + int32_t accountId = 100; + auto res = proxy_->RequestModalUIExtensionWithAccount(want, accountId); + EXPECT_NE(res, NO_ERROR); + + TAG_LOGI(AAFwkTag::TEST, "AbilityManagerProxy_RequestModalUIExtensionWithAccount_002 end"); +} + +/* + * Feature: AbilityManagerService + * Function: RequestModalUIExtensionWithAccount + * SubFunction: NA + * FunctionPoints: AbilityManagerProxy RequestModalUIExtensionWithAccount + * EnvConditions: NA + * CaseDescription: Verify RequestModalUIExtensionWithAccount with Want parameters + */ +HWTEST_F(AbilityManagerProxyTest, AbilityManagerProxy_RequestModalUIExtensionWithAccount_003, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "AbilityManagerProxy_RequestModalUIExtensionWithAccount_003 start"); + + EXPECT_CALL(*mock_, SendRequest(_, _, _, _)) + .Times(1) + .WillOnce(Invoke(mock_.GetRefPtr(), &AbilityManagerStubMock::InvokeSendRequest)); + + Want want; + ElementName element("device", "com.test.modal", "ModalUIExtensionAbility"); + want.SetElement(element); + want.SetParam("key_test", 123); + int32_t accountId = 200; + + auto res = proxy_->RequestModalUIExtensionWithAccount(want, accountId); + + EXPECT_EQ(static_cast( + AbilityManagerInterfaceCode::REQUEST_MODAL_UI_EXTENSION_WITH_ACCOUNT), mock_->code_); + EXPECT_EQ(res, NO_ERROR); + + TAG_LOGI(AAFwkTag::TEST, "AbilityManagerProxy_RequestModalUIExtensionWithAccount_003 end"); +} + +/* + * Feature: AbilityManagerService + * Function: RequestModalUIExtensionWithAccount + * SubFunction: NA + * FunctionPoints: AbilityManagerProxy RequestModalUIExtensionWithAccount + * EnvConditions: NA + * CaseDescription: Verify RequestModalUIExtensionWithAccount with negative accountId + */ +HWTEST_F(AbilityManagerProxyTest, AbilityManagerProxy_RequestModalUIExtensionWithAccount_004, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "AbilityManagerProxy_RequestModalUIExtensionWithAccount_004 start"); + + EXPECT_CALL(*mock_, SendRequest(_, _, _, _)) + .Times(1) + .WillOnce(Invoke(mock_.GetRefPtr(), &AbilityManagerStubMock::InvokeSendRequest)); + + Want want; + int32_t accountId = -1; + + auto res = proxy_->RequestModalUIExtensionWithAccount(want, accountId); + + EXPECT_EQ(static_cast( + AbilityManagerInterfaceCode::REQUEST_MODAL_UI_EXTENSION_WITH_ACCOUNT), mock_->code_); + EXPECT_EQ(res, NO_ERROR); + + TAG_LOGI(AAFwkTag::TEST, "AbilityManagerProxy_RequestModalUIExtensionWithAccount_004 end"); +} + +/* + * Feature: AbilityManagerService + * Function: StartSelfUIAbilityByAppContext + * SubFunction: NA + * FunctionPoints: AbilityManagerService StartSelfUIAbilityByAppContext + * EnvConditions: NA + * CaseDescription: Verify the normal process of StartSelfUIAbilityByAppContext + */ +HWTEST_F(AbilityManagerProxyTest, AbilityManagerProxy_StartSelfUIAbilityByAppContext_001, TestSize.Level1) +{ + EXPECT_CALL(*mock_, SendRequest(_, _, _, _)) + .Times(1) + .WillOnce(Invoke(mock_.GetRefPtr(), &AbilityManagerStubMock::InvokeSendRequest)); + Want want; + auto res = proxy_->StartSelfUIAbilityByAppContext(want); + EXPECT_EQ(static_cast(AbilityManagerInterfaceCode::START_SELF_UI_ABILITY_BY_APP_CONTEXT), + mock_->code_); + EXPECT_EQ(res, NO_ERROR); +} + +/* + * Feature: AbilityManagerService + * Function: StartSelfUIAbilityByAppContext + * SubFunction: NA + * FunctionPoints: AbilityManagerService StartSelfUIAbilityByAppContext + * EnvConditions: NA + * CaseDescription: Verify the abnormal process of StartSelfUIAbilityByAppContext (SendRequest failure) + */ +HWTEST_F(AbilityManagerProxyTest, AbilityManagerProxy_StartSelfUIAbilityByAppContext_002, TestSize.Level1) +{ + EXPECT_CALL(*mock_, SendRequest(_, _, _, _)) + .Times(1) + .WillOnce(Invoke(mock_.GetRefPtr(), &AbilityManagerStubMock::InvokeErrorSendRequest)); + Want want; + auto res = proxy_->StartSelfUIAbilityByAppContext(want); + EXPECT_EQ(static_cast(AbilityManagerInterfaceCode::START_SELF_UI_ABILITY_BY_APP_CONTEXT), + mock_->code_); + EXPECT_NE(res, NO_ERROR); +} + +/* + * Feature: AbilityManagerService + * Function: StartSelf + * SubFunction: NA + * FunctionPoints: AbilityManagerService StartSelf + * EnvConditions: NA + * CaseDescription: Verify the normal process of StartSelf + */ +HWTEST_F(AbilityManagerProxyTest, AbilityManagerProxy_StartSelf_001, TestSize.Level1) +{ + EXPECT_CALL(*mock_, SendRequest(_, _, _, _)) + .Times(1) + .WillOnce(Invoke(mock_.GetRefPtr(), &AbilityManagerStubMock::InvokeSendRequest)); + auto token = sptr::MakeSptr(); + auto res = proxy_->StartSelf(token); + EXPECT_EQ(static_cast(AbilityManagerInterfaceCode::START_SELF), mock_->code_); + EXPECT_EQ(res, NO_ERROR); +} + +/* + * Feature: AbilityManagerService + * Function: StartSelf + * SubFunction: NA + * FunctionPoints: AbilityManagerService StartSelf + * EnvConditions: NA + * CaseDescription: Verify StartSelf with null token returns ERR_INVALID_VALUE + */ +HWTEST_F(AbilityManagerProxyTest, AbilityManagerProxy_StartSelf_002, TestSize.Level1) +{ + OHOS::sptr token = nullptr; + auto res = proxy_->StartSelf(token); + EXPECT_EQ(res, ERR_INVALID_VALUE); +} + +/* + * Feature: AbilityManagerService + * Function: StartSelf + * SubFunction: NA + * FunctionPoints: AbilityManagerService StartSelf + * EnvConditions: NA + * CaseDescription: Verify the abnormal process of StartSelf (SendRequest failure) + */ +HWTEST_F(AbilityManagerProxyTest, AbilityManagerProxy_StartSelf_003, TestSize.Level1) +{ + EXPECT_CALL(*mock_, SendRequest(_, _, _, _)) + .Times(1) + .WillOnce(Invoke(mock_.GetRefPtr(), &AbilityManagerStubMock::InvokeErrorSendRequest)); + auto token = sptr::MakeSptr(); + auto res = proxy_->StartSelf(token); + EXPECT_EQ(static_cast(AbilityManagerInterfaceCode::START_SELF), mock_->code_); + EXPECT_NE(res, NO_ERROR); +} } // namespace AAFwk } // namespace OHOS diff --git a/test/unittest/ability_manager_proxy_test/ability_manager_stub_mock.h b/test/unittest/ability_manager_proxy_test/ability_manager_stub_mock.h index e8621e5a21..d95025962d 100644 --- a/test/unittest/ability_manager_proxy_test/ability_manager_stub_mock.h +++ b/test/unittest/ability_manager_proxy_test/ability_manager_stub_mock.h @@ -378,8 +378,8 @@ public: MOCK_METHOD1(GetAbilityRunningInfos, int(std::vector& info)); MOCK_METHOD2(GetExtensionRunningInfos, int(int upperLimit, std::vector& info)); MOCK_METHOD1(GetProcessRunningInfos, int(std::vector& info)); - MOCK_METHOD7(StartAbilityByCall, - int(const Want&, const sptr&, const sptr&, int32_t, bool, bool, bool)); + MOCK_METHOD8(StartAbilityByCall, int(const Want&, const sptr&, + const sptr&, int32_t, bool, bool, bool, uint64_t)); MOCK_METHOD2(AcquireShareData, int32_t(const int32_t &missionId, const sptr &shareData)); MOCK_METHOD4(ShareDataDone, int32_t(const sptr &token, const int32_t &resultCode, const int32_t &uniqueId, WantParams &wantParam)); diff --git a/test/unittest/ability_manager_proxy_third_test/ability_manager_stub_mock.h b/test/unittest/ability_manager_proxy_third_test/ability_manager_stub_mock.h index 2073831935..74041b680a 100644 --- a/test/unittest/ability_manager_proxy_third_test/ability_manager_stub_mock.h +++ b/test/unittest/ability_manager_proxy_third_test/ability_manager_stub_mock.h @@ -327,8 +327,8 @@ public: MOCK_METHOD1(GetAbilityRunningInfos, int(std::vector& info)); MOCK_METHOD2(GetExtensionRunningInfos, int(int upperLimit, std::vector& info)); MOCK_METHOD1(GetProcessRunningInfos, int(std::vector& info)); - MOCK_METHOD7(StartAbilityByCall, - int(const Want&, const sptr&, const sptr&, int32_t, bool, bool, bool)); + MOCK_METHOD8(StartAbilityByCall, int(const Want&, const sptr&, + const sptr&, int32_t, bool, bool, bool, uint64_t)); MOCK_METHOD2(AcquireShareData, int32_t(const int32_t& missionId, const sptr& shareData)); MOCK_METHOD4(ShareDataDone, int32_t(const sptr& token, const int32_t& resultCode, const int32_t& uniqueId, WantParams& wantParam)); diff --git a/test/unittest/ability_manager_service_first_test/ability_manager_service_first_test.cpp b/test/unittest/ability_manager_service_first_test/ability_manager_service_first_test.cpp index 5d7b046923..8bfda475cd 100644 --- a/test/unittest/ability_manager_service_first_test/ability_manager_service_first_test.cpp +++ b/test/unittest/ability_manager_service_first_test/ability_manager_service_first_test.cpp @@ -2909,6 +2909,89 @@ HWTEST_F(AbilityManagerServiceFirstTest, CheckCallAbilityPermission_004, TestSiz TAG_LOGI(AAFwkTag::TEST, "AbilityManagerServiceFirstTest CheckCallAbilityPermission_004 end"); } +/** + * @tc.name: StartSelfUIAbilityInChildProcess_0100 + * @tc.desc: Test StartSelfUIAbilityInChildProcess with device not supported + * @tc.type: FUNC + */ +HWTEST_F(AbilityManagerServiceFirstTest, StartSelfUIAbilityInChildProcess_0100, TestSize.Level1) +{ + auto abilityMs_ = std::make_shared(); + Want want; + want.SetElementName("com.test.bundle", "TestAbility"); + std::string specifiedFlag = "testFlag"; + sptr callerToken = nullptr; + AppUtils::GetInstance().isSupportNativeUIAbility_.isLoaded = true; + AppUtils::GetInstance().isSupportNativeUIAbility_.value = false; + auto res = abilityMs_->StartSelfUIAbilityInChildProcess(want, specifiedFlag, callerToken); + EXPECT_EQ(res, ERR_CAPABILITY_NOT_SUPPORT); +} +/** + * @tc.name: StartSelfUIAbilityInChildProcess_0200 + * @tc.desc: Test StartSelfUIAbilityInChildProcess with null callerToken + * @tc.type: FUNC + */ +HWTEST_F(AbilityManagerServiceFirstTest, StartSelfUIAbilityInChildProcess_0200, TestSize.Level1) +{ + auto abilityMs_ = std::make_shared(); + ASSERT_NE(abilityMs_, nullptr); + Want want; + want.SetElementName("com.test.bundle", "TestAbility"); + std::string specifiedFlag = "testFlag"; + sptr callerToken = nullptr; + AppUtils::GetInstance().isSupportNativeUIAbility_.isLoaded = true; + AppUtils::GetInstance().isSupportNativeUIAbility_.value = true; + auto res = abilityMs_->StartSelfUIAbilityInChildProcess(want, specifiedFlag, callerToken); + EXPECT_EQ(res, ERR_INVALID_VALUE); +} + +/** + * @tc.name: StartSelfUIAbilityInChildProcess_0300 + * @tc.desc: Test StartSelfUIAbilityInChildProcess with empty specifiedFlag + * @tc.type: FUNC + */ +HWTEST_F(AbilityManagerServiceFirstTest, StartSelfUIAbilityInChildProcess_0300, TestSize.Level1) +{ + auto abilityMs_ = std::make_shared(); + ASSERT_NE(abilityMs_, nullptr); + Want want; + std::string specifiedFlag = ""; + AppExecFwk::AbilityInfo abilityInfo; + AppExecFwk::ApplicationInfo applicationInfo; + applicationInfo.accessTokenId = IPCSkeleton::GetCallingTokenID(); + auto abilityRecord = std::make_shared(want, abilityInfo, applicationInfo); + abilityRecord->Init(AbilityRequest()); + sptr callerToken = abilityRecord->token_; + AppUtils::GetInstance().isSupportNativeUIAbility_.isLoaded = true; + AppUtils::GetInstance().isSupportNativeUIAbility_.value = true; + auto res = abilityMs_->StartSelfUIAbilityInChildProcess(want, specifiedFlag, callerToken); + EXPECT_EQ(res, START_UI_ABILITIES_NOT_SUPPORT_IMPLICIT_START); +} + +/** + * @tc.name: StartSelfUIAbilityInChildProcess_0400 + * @tc.desc: Test StartSelfUIAbilityInChildProcess with valid want + * @tc.type: FUNC + */ +HWTEST_F(AbilityManagerServiceFirstTest, StartSelfUIAbilityInChildProcess_0400, TestSize.Level1) +{ + auto abilityMs_ = std::make_shared(); + ASSERT_NE(abilityMs_, nullptr); + Want want; + want.SetElementName("com.test.bundle", "TestAbility"); + want.SetParam("testParam", std::string("testValue")); + std::string specifiedFlag = "testFlag"; + AppExecFwk::AbilityInfo abilityInfo; + AppExecFwk::ApplicationInfo applicationInfo; + applicationInfo.accessTokenId = IPCSkeleton::GetCallingTokenID(); + auto abilityRecord = std::make_shared(want, abilityInfo, applicationInfo); + abilityRecord->Init(AbilityRequest()); + sptr callerToken = abilityRecord->token_; + AppUtils::GetInstance().isSupportNativeUIAbility_.isLoaded = true; + AppUtils::GetInstance().isSupportNativeUIAbility_.value = true; + auto res = abilityMs_->StartSelfUIAbilityInChildProcess(want, specifiedFlag, callerToken); + EXPECT_EQ(res, ERROR_UIABILITY_NOT_BELONG_TO_CALLER); +} } // namespace AAFwk } // namespace OHOS diff --git a/test/unittest/ability_manager_service_first_test/abilitymgr.gni b/test/unittest/ability_manager_service_first_test/abilitymgr.gni index d307f448fc..8ee676d900 100644 --- a/test/unittest/ability_manager_service_first_test/abilitymgr.gni +++ b/test/unittest/ability_manager_service_first_test/abilitymgr.gni @@ -137,6 +137,13 @@ abilityms_files = [ "${ability_runtime_services_path}/abilitymgr/src/insight_intent/insight_intent_event_mgr.cpp", "${ability_runtime_services_path}/abilitymgr/src/insight_intent/insight_intent_sys_event_receiver.cpp", + #skill execution + "${ability_runtime_services_path}/abilitymgr/src/skill/skill_execute_callback_proxy.cpp", + "${ability_runtime_services_path}/abilitymgr/src/skill/skill_execute_callback_stub.cpp", + "${ability_runtime_services_path}/abilitymgr/src/skill/skill_execute_param.cpp", + "${ability_runtime_services_path}/abilitymgr/src/skill/skill_execute_result.cpp", + "${ability_runtime_services_path}/abilitymgr/src/skill/skill_execute_manager.cpp", + #sa interceptor "${ability_runtime_services_path}/abilitymgr/src/sa_interceptor_manager.cpp", diff --git a/test/unittest/ability_manager_service_fourteenth_test/BUILD.gn b/test/unittest/ability_manager_service_fourteenth_test/BUILD.gn index d6c2c61861..080360d145 100644 --- a/test/unittest/ability_manager_service_fourteenth_test/BUILD.gn +++ b/test/unittest/ability_manager_service_fourteenth_test/BUILD.gn @@ -43,6 +43,7 @@ ohos_unittest("ability_manager_service_fourteenth_test") { "${ability_runtime_abilitymgr_path}/include/resident_process/", "${ability_runtime_abilitymgr_path}/include/screen_lock/", "${ability_runtime_abilitymgr_path}/include/ui_extension", + "${ability_runtime_abilitymgr_path}/include/skill", "${ability_runtime_abilitymgr_path}/include/ui_extension_record", "${ability_runtime_abilitymgr_path}/include/utils/", "${ability_runtime_innerkits_path}/ability_manager/include", @@ -169,10 +170,17 @@ ohos_unittest("ability_manager_service_fourteenth_test") { "${ability_runtime_services_path}/abilitymgr/src/rdb/parser_util.cpp", "${ability_runtime_services_path}/abilitymgr/src/rdb/rdb_data_manager.cpp", "${ability_runtime_services_path}/abilitymgr/src/recovery_info_timer.cpp", + "${ability_runtime_services_path}/abilitymgr/src/remote_intent_result_callback_stub.cpp", + "${ability_runtime_services_path}/abilitymgr/src/remote_intent_result_callback.cpp", "${ability_runtime_services_path}/abilitymgr/src/remote_mission_listener_proxy.cpp", "${ability_runtime_services_path}/abilitymgr/src/report_data_partition_usage_manager.cpp", "${ability_runtime_services_path}/abilitymgr/src/resident_process/resident_process_manager.cpp", "${ability_runtime_services_path}/abilitymgr/src/restart_app_manager.cpp", + "${ability_runtime_services_path}/abilitymgr/src/skill/skill_execute_callback_proxy.cpp", + "${ability_runtime_services_path}/abilitymgr/src/skill/skill_execute_callback_stub.cpp", + "${ability_runtime_services_path}/abilitymgr/src/skill/skill_execute_param.cpp", + "${ability_runtime_services_path}/abilitymgr/src/skill/skill_execute_result.cpp", + "${ability_runtime_services_path}/abilitymgr/src/skill/skill_execute_manager.cpp", "${ability_runtime_services_path}/abilitymgr/src/sa_interceptor_manager.cpp", "${ability_runtime_services_path}/abilitymgr/src/scene_board/status_bar_delegate_manager.cpp", "${ability_runtime_services_path}/abilitymgr/src/screen_lock/unlock_screen_callback.cpp", @@ -199,7 +207,7 @@ ohos_unittest("ability_manager_service_fourteenth_test") { "${ability_runtime_services_path}/abilitymgr/src/utils/modal_system_dialog_util.cpp", "${ability_runtime_services_path}/abilitymgr/src/utils/multi_app_utils.cpp", "${ability_runtime_services_path}/abilitymgr/src/utils/multi_instance_utils.cpp", - "${ability_runtime_services_path}/abilitymgr/src/utils/oe_extension_utils.cpp", + "mock/src/mock_oe_extension_utils.cpp", "${ability_runtime_services_path}/abilitymgr/src/utils/start_ability_utils.cpp", "${ability_runtime_services_path}/abilitymgr/src/utils/start_options_utils.cpp", "${ability_runtime_services_path}/abilitymgr/src/utils/state_utils.cpp", diff --git a/test/unittest/ability_manager_service_fourteenth_test/ability_manager_service_fourteenth_test.cpp b/test/unittest/ability_manager_service_fourteenth_test/ability_manager_service_fourteenth_test.cpp index 26c4a2c9eb..aa08109e1e 100644 --- a/test/unittest/ability_manager_service_fourteenth_test/ability_manager_service_fourteenth_test.cpp +++ b/test/unittest/ability_manager_service_fourteenth_test/ability_manager_service_fourteenth_test.cpp @@ -23,6 +23,7 @@ #include "mission_list_manager.h" #include "scene_board_judgement.h" #include "call_record.h" +#include "utils/start_ability_utils.h" using namespace testing; using namespace testing::ext; @@ -1747,5 +1748,131 @@ HWTEST_F(AbilityManagerServiceFourteenthTest, NotifyCompleteGamePreLaunch_001, T } TAG_LOGI(AAFwkTag::TEST, "AbilityManagerServiceFourteenthTest NotifyCompleteGamePreLaunch_001 end"); } + +/** + * @tc.number: StartSelfUIAbilityByAppContext_001 + * @tc.name: StartSelfUIAbilityByAppContext + * @tc.desc: Test StartSelfUIAbilityByAppContext when device not supported + */ +HWTEST_F(AbilityManagerServiceFourteenthTest, StartSelfUIAbilityByAppContext_001, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "StartSelfUIAbilityByAppContext_001 start"); + auto abilityMs = std::make_shared(); + ASSERT_NE(abilityMs, nullptr); + MyStatus::GetInstance().auIsSupportDelayedProcessExit_ = false; + Want want; + want.SetElementName("com.example.bundle", "MainAbility"); + auto result = abilityMs->StartSelfUIAbilityByAppContext(want); + EXPECT_EQ(result, ERR_CAPABILITY_NOT_SUPPORT); + TAG_LOGI(AAFwkTag::TEST, "StartSelfUIAbilityByAppContext_001 end"); +} + +/** + * @tc.number: StartSelfUIAbilityByAppContext_002 + * @tc.name: StartSelfUIAbilityByAppContext + * @tc.desc: Test StartSelfUIAbilityByAppContext when device supported, empty bundle (implicit start) + */ +HWTEST_F(AbilityManagerServiceFourteenthTest, StartSelfUIAbilityByAppContext_002, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "StartSelfUIAbilityByAppContext_002 start"); + auto abilityMs = std::make_shared(); + ASSERT_NE(abilityMs, nullptr); + MyStatus::GetInstance().auIsSupportDelayedProcessExit_ = true; + Want want; + auto result = abilityMs->StartSelfUIAbilityByAppContext(want); + EXPECT_EQ(result, START_UI_ABILITIES_NOT_SUPPORT_IMPLICIT_START); + MyStatus::GetInstance().auIsSupportDelayedProcessExit_ = false; + TAG_LOGI(AAFwkTag::TEST, "StartSelfUIAbilityByAppContext_002 end"); +} + +/** + * @tc.number: StartSelfUIAbilityByAppContext_003 + * @tc.name: StartSelfUIAbilityByAppContext + * @tc.desc: Test StartSelfUIAbilityByAppContext with non-empty bundle, falls through to StartAbilityDelayed + */ +HWTEST_F(AbilityManagerServiceFourteenthTest, StartSelfUIAbilityByAppContext_003, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "StartSelfUIAbilityByAppContext_003 start"); + auto abilityMs = std::make_shared(); + ASSERT_NE(abilityMs, nullptr); + MyStatus::GetInstance().auIsSupportDelayedProcessExit_ = true; + Want want; + want.SetElementName("com.example.bundle", "MainAbility"); + auto result = abilityMs->StartSelfUIAbilityByAppContext(want); + EXPECT_NE(result, ERR_OK); + MyStatus::GetInstance().auIsSupportDelayedProcessExit_ = false; + TAG_LOGI(AAFwkTag::TEST, "StartSelfUIAbilityByAppContext_003 end"); +} + +/** + * @tc.number: StartAbilityDelayed_001 + * @tc.name: StartAbilityDelayed + * @tc.desc: Test StartAbilityDelayed with empty bundle name + */ +HWTEST_F(AbilityManagerServiceFourteenthTest, StartAbilityDelayed_001, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "StartAbilityDelayed_001 start"); + auto abilityMs = std::make_shared(); + ASSERT_NE(abilityMs, nullptr); + StartAbilityWrapParam param; + auto result = abilityMs->StartAbilityDelayed(param); + EXPECT_EQ(result, TARGET_BUNDLE_NOT_EXIST); + TAG_LOGI(AAFwkTag::TEST, "StartAbilityDelayed_001 end"); +} + +/** + * @tc.number: StartAbilityDelayed_002 + * @tc.name: StartAbilityDelayed + * @tc.desc: Test StartAbilityDelayed with bundle not belonging to caller + */ +HWTEST_F(AbilityManagerServiceFourteenthTest, StartAbilityDelayed_002, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "StartAbilityDelayed_002 start"); + auto abilityMs = std::make_shared(); + ASSERT_NE(abilityMs, nullptr); + StartAbilityWrapParam param; + param.want.SetBundle("com.example.nonexistent"); + auto result = abilityMs->StartAbilityDelayed(param); + EXPECT_NE(result, ERR_OK); + TAG_LOGI(AAFwkTag::TEST, "StartAbilityDelayed_002 end"); +} +/** + * @tc.number: StartAbilityByOEExt_001 + * @tc.name: StartAbilityByOEExt + * @tc.desc: Test StartAbilityByOEExt when ValidateCaller returns CHECK_PERMISSION_FAILED + */ +HWTEST_F(AbilityManagerServiceFourteenthTest, StartAbilityByOEExt_001, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "AbilityManagerServiceFourteenthTest StartAbilityByOEExt_001 start"); + auto abilityMs = std::make_shared(); + MyStatus::GetInstance().oeuValidateCallerResult_ = CHECK_PERMISSION_FAILED; + Want want; + want.SetElementName("com.test.bundle", "TestAbility"); + int32_t result = abilityMs->StartAbilityByOEExt(want, nullptr, 0, ""); + EXPECT_EQ(result, CHECK_PERMISSION_FAILED); + MyStatus::GetInstance().oeuValidateCallerResult_ = ERR_OK; + TAG_LOGI(AAFwkTag::TEST, "AbilityManagerServiceFourteenthTest StartAbilityByOEExt_001 end"); +} + +/** + * @tc.number: StartAbilityByOEExt_005 + * @tc.name: StartAbilityByOEExt + * @tc.desc: Test StartAbilityByOEExt when ValidateCaller returns ERR_OK, falls through to StartAbilityWrap + */ +HWTEST_F(AbilityManagerServiceFourteenthTest, StartAbilityByOEExt_005, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "AbilityManagerServiceFourteenthTest StartAbilityByOEExt_005 start"); + auto abilityMs = std::make_shared(); + ASSERT_NE(abilityMs, nullptr); + MyStatus::GetInstance().oeuValidateCallerResult_ = ERR_OK; + MyStatus::GetInstance().oeuValidateCallerUserId_ = 100; + MyStatus::GetInstance().oeuValidateCallerHostBundleName_ = "com.test.host"; + Want want; + want.SetElementName("com.test.bundle", "TestAbility"); + int32_t result = abilityMs->StartAbilityByOEExt(want, nullptr, 1000, "testFlag"); + EXPECT_NE(result, ERR_OK); + TAG_LOGI(AAFwkTag::TEST, "AbilityManagerServiceFourteenthTest StartAbilityByOEExt_005 end"); +} + } // namespace AAFwk } // namespace OHOS diff --git a/test/unittest/ability_manager_service_fourteenth_test/mock/src/mock_ability_record.cpp b/test/unittest/ability_manager_service_fourteenth_test/mock/src/mock_ability_record.cpp index 8e634ea528..9d3ec48de4 100644 --- a/test/unittest/ability_manager_service_fourteenth_test/mock/src/mock_ability_record.cpp +++ b/test/unittest/ability_manager_service_fourteenth_test/mock/src/mock_ability_record.cpp @@ -362,7 +362,7 @@ bool AbilityRecord::CanRestartResident() } // only for UIAbility -void AbilityRecord::ForegroundAbility(uint32_t sceneFlag, bool hasLastWant) +void AbilityRecord::ForegroundAbility(uint32_t sceneFlag) { } diff --git a/test/unittest/ability_manager_service_fourteenth_test/mock/src/mock_oe_extension_utils.cpp b/test/unittest/ability_manager_service_fourteenth_test/mock/src/mock_oe_extension_utils.cpp new file mode 100644 index 0000000000..659dff8a49 --- /dev/null +++ b/test/unittest/ability_manager_service_fourteenth_test/mock/src/mock_oe_extension_utils.cpp @@ -0,0 +1,56 @@ +/* + * 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 "utils/oe_extension_utils.h" + +#include "mock_my_status.h" + +namespace OHOS { +namespace AAFwk { + +OEExtensionUtils &OEExtensionUtils::GetInstance() +{ + static OEExtensionUtils instance; + return instance; +} + +int32_t OEExtensionUtils::ValidateCaller( + int32_t callingUid, + const Want &want, + const sptr &callerToken, + int32_t hostPid, + std::string &hostBundleName, + int32_t &userId) +{ + auto &status = MyStatus::GetInstance(); + if (status.oeuValidateCallerResult_ != ERR_OK) { + return status.oeuValidateCallerResult_; + } + hostBundleName = status.oeuValidateCallerHostBundleName_; + userId = status.oeuValidateCallerUserId_; + return ERR_OK; +} + +void OEExtensionUtils::AddOEExtRequest(int32_t requestId) {} + +bool OEExtensionUtils::RemoveOEExtRequest(int32_t requestId) +{ + return false; +} + +void OEExtensionUtils::ScheduleDelayedCleanup(int32_t requestId) {} + +} // namespace AAFwk +} // namespace OHOS diff --git a/test/unittest/ability_manager_service_fourth_test/ability_manager_service_fourth_test.cpp b/test/unittest/ability_manager_service_fourth_test/ability_manager_service_fourth_test.cpp index e464122843..6cc37b9656 100644 --- a/test/unittest/ability_manager_service_fourth_test/ability_manager_service_fourth_test.cpp +++ b/test/unittest/ability_manager_service_fourth_test/ability_manager_service_fourth_test.cpp @@ -27,6 +27,7 @@ #include "modal_system_dialog/modal_system_dialog_ui_extension.h" #include "user_controller/user_controller.h" #include "utils/modal_system_dialog_util.h" +#include "app_utils.h" #undef private #undef protected #include "hilog_tag_wrapper.h" @@ -48,6 +49,7 @@ using OHOS::AppExecFwk::ExtensionAbilityType; constexpr char DEVELOPER_MODE_STATE[] = "const.security.developermode.state"; constexpr const char* DEBUG_APP = "debugApp"; constexpr const char* START_ABILITY_TYPE = "ABILITY_INNER_START_WITH_ACCOUNT"; +constexpr const char* SUPPORT_NATIVE_UI_ABILITY = "persist.sys.abilityms.support_native_ui_ability"; constexpr int32_t FOUNDATION_UID = 5523; constexpr int32_t TEST_VALID_USER_ID = 100; @@ -1687,6 +1689,57 @@ HWTEST_F(AbilityManagerServiceFourthTest, StartUIExtensionAbilityTset_001, TestS TAG_LOGI(AAFwkTag::TEST, "AbilityManagerServiceFourthTest StartUIExtensionAbilityTset_001 end"); } +/* + * Feature: AbilityManagerService + * Function: StartAbilityAsCallerDetails + * SubFunction: NA + * FunctionPoints: StartAbilityAsCallerDetails with non-null callback on failure + */ +HWTEST_F(AbilityManagerServiceFourthTest, StartAbilityAsCallerDetails_002, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "AbilityManagerServiceFourthTest StartAbilityAsCallerDetails_002 start"); + Want want; + auto callerToken = MockToken(AbilityType::PAGE); + auto asCallerSourceToken = MockToken(AbilityType::PAGE); + int32_t userId = 0; + int requestCode = 0; + bool isImplicit = true; + auto abilityMs_ = std::make_shared(); + ASSERT_NE(abilityMs_, nullptr); + + sptr callback = new RequestAbilityImplCallback(); + auto ret = abilityMs_->StartAbilityAsCallerDetails( + want, callerToken, asCallerSourceToken, userId, requestCode, isImplicit, false, 0, callback); + EXPECT_NE(ret, ERR_OK); + EXPECT_EQ(callback->result_, false); + TAG_LOGI(AAFwkTag::TEST, "AbilityManagerServiceFourthTest StartAbilityAsCallerDetails_002 end"); +} + +/* + * Feature: AbilityManagerService + * Function: StartAbilityAsCallerDetails + * SubFunction: NA + * FunctionPoints: StartAbilityAsCallerDetails with null callback on failure + */ +HWTEST_F(AbilityManagerServiceFourthTest, StartAbilityAsCallerDetails_003, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "AbilityManagerServiceFourthTest StartAbilityAsCallerDetails_003 start"); + Want want; + auto callerToken = MockToken(AbilityType::PAGE); + auto asCallerSourceToken = MockToken(AbilityType::PAGE); + int32_t userId = 0; + int requestCode = 0; + bool isImplicit = true; + auto abilityMs_ = std::make_shared(); + ASSERT_NE(abilityMs_, nullptr); + + sptr callback = nullptr; + auto ret = abilityMs_->StartAbilityAsCallerDetails( + want, callerToken, asCallerSourceToken, userId, requestCode, isImplicit, false, 0, callback); + EXPECT_NE(ret, ERR_OK); + TAG_LOGI(AAFwkTag::TEST, "AbilityManagerServiceFourthTest StartAbilityAsCallerDetails_003 end"); +} + /* * Feature: AbilityManagerService * Function: StartUIAbilityWithCallback @@ -1888,5 +1941,30 @@ HWTEST_F(AbilityManagerServiceFourthTest, StartSelf_005, TestSize.Level1) EXPECT_EQ(ret, ERR_INVALID_VALUE); TAG_LOGI(AAFwkTag::TEST, "AbilityManagerServiceFourthTest StartSelf_005 end"); } + +/* + * Feature: AbilityManagerService + * Function: StartSelf + * FunctionPoints: AbilityManagerService StartSelf when device does not support Native UI Ability + */ +HWTEST_F(AbilityManagerServiceFourthTest, StartSelf_006, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "AbilityManagerServiceFourthTest StartSelf_006 start"); + auto abilityMs_ = std::make_shared(); + auto &appUtils = AppUtils::GetInstance(); + bool srcCacheLoaded = appUtils.isSupportNativeUIAbility_.isLoaded; + bool srcCacheValue = appUtils.isSupportNativeUIAbility_.value; + bool srcSupportNativeUiAbility = OHOS::system::GetBoolParameter(SUPPORT_NATIVE_UI_ABILITY, false); + OHOS::system::SetBoolParameter(SUPPORT_NATIVE_UI_ABILITY, false); + appUtils.isSupportNativeUIAbility_.isLoaded = false; + auto callerToken = MockToken(AbilityType::PAGE); + ASSERT_NE(callerToken, nullptr); + auto ret = abilityMs_->StartSelf(callerToken); + EXPECT_EQ(ret, ERR_CAPABILITY_NOT_SUPPORT); + OHOS::system::SetBoolParameter(SUPPORT_NATIVE_UI_ABILITY, srcSupportNativeUiAbility); + appUtils.isSupportNativeUIAbility_.isLoaded = srcCacheLoaded; + appUtils.isSupportNativeUIAbility_.value = srcCacheValue; + TAG_LOGI(AAFwkTag::TEST, "AbilityManagerServiceFourthTest StartSelf_006 end"); +} } // namespace AAFwk } // namespace OHOS diff --git a/test/unittest/ability_manager_service_second_test/BUILD.gn b/test/unittest/ability_manager_service_second_test/BUILD.gn index 1d3b4a65bd..da2c55b764 100644 --- a/test/unittest/ability_manager_service_second_test/BUILD.gn +++ b/test/unittest/ability_manager_service_second_test/BUILD.gn @@ -43,6 +43,7 @@ ohos_unittest("ability_manager_service_second_test") { "${ability_runtime_abilitymgr_path}/include/resident_process/", "${ability_runtime_abilitymgr_path}/include/screen_lock/", "${ability_runtime_abilitymgr_path}/include/ui_extension", + "${ability_runtime_abilitymgr_path}/include/skill", "${ability_runtime_abilitymgr_path}/include/ui_extension_record", "${ability_runtime_abilitymgr_path}/include/utils/", "${ability_runtime_innerkits_path}/ability_manager/include", @@ -177,10 +178,17 @@ ohos_unittest("ability_manager_service_second_test") { "${ability_runtime_services_path}/abilitymgr/src/rdb/parser_util.cpp", "${ability_runtime_services_path}/abilitymgr/src/rdb/rdb_data_manager.cpp", "${ability_runtime_services_path}/abilitymgr/src/recovery_info_timer.cpp", + "${ability_runtime_services_path}/abilitymgr/src/remote_intent_result_callback_stub.cpp", + "${ability_runtime_services_path}/abilitymgr/src/remote_intent_result_callback.cpp", "${ability_runtime_services_path}/abilitymgr/src/remote_mission_listener_proxy.cpp", "${ability_runtime_services_path}/abilitymgr/src/report_data_partition_usage_manager.cpp", "${ability_runtime_services_path}/abilitymgr/src/resident_process/resident_process_manager.cpp", "${ability_runtime_services_path}/abilitymgr/src/restart_app_manager.cpp", + "${ability_runtime_services_path}/abilitymgr/src/skill/skill_execute_callback_proxy.cpp", + "${ability_runtime_services_path}/abilitymgr/src/skill/skill_execute_callback_stub.cpp", + "${ability_runtime_services_path}/abilitymgr/src/skill/skill_execute_param.cpp", + "${ability_runtime_services_path}/abilitymgr/src/skill/skill_execute_result.cpp", + "${ability_runtime_services_path}/abilitymgr/src/skill/skill_execute_manager.cpp", "${ability_runtime_services_path}/abilitymgr/src/sa_interceptor_manager.cpp", "${ability_runtime_services_path}/abilitymgr/src/scene_board/status_bar_delegate_manager.cpp", "${ability_runtime_services_path}/abilitymgr/src/scene_board/ui_ability_record.cpp", diff --git a/test/unittest/ability_manager_service_sixth_test/BUILD.gn b/test/unittest/ability_manager_service_sixth_test/BUILD.gn index 84060e5ee0..336f720135 100644 --- a/test/unittest/ability_manager_service_sixth_test/BUILD.gn +++ b/test/unittest/ability_manager_service_sixth_test/BUILD.gn @@ -46,6 +46,8 @@ ohos_unittest("ability_manager_service_sixth_test") { "${ability_runtime_test_path}/mock/services_abilitymgr_test/libs/aakit/include", "${ability_runtime_test_path}/unittest/app_debug_listener_stub_test", "${agent_runtime_framework_path}/interfaces/kits/native/agent_extension/connection/include", + "${ability_runtime_services_path}/common/include/user_controller", + "${ability_runtime_path}/utils/server/constant", "mock/include", ] diff --git a/test/unittest/ability_manager_service_sixth_test/ability_manager_service_sixth_test.cpp b/test/unittest/ability_manager_service_sixth_test/ability_manager_service_sixth_test.cpp index cad4b44a6e..4d2d9e9ac2 100644 --- a/test/unittest/ability_manager_service_sixth_test/ability_manager_service_sixth_test.cpp +++ b/test/unittest/ability_manager_service_sixth_test/ability_manager_service_sixth_test.cpp @@ -44,6 +44,8 @@ #include "start_ability_utils.h" #include "start_params_by_SCB.h" #include "ui_service_extension_connection_constants.h" +#include "user_controller.h" +#include "server_constant.h" using namespace testing; using namespace testing::ext; @@ -2187,6 +2189,197 @@ HWTEST_F(AbilityManagerServiceSixthTest, IsUIAbilityAlreadyExist_001, TestSize.L TAG_LOGI(AAFwkTag::TEST, "AbilityManagerServiceSixthTest IsUIAbilityAlreadyExist_001 end"); } +/* + * Feature: AbilityManagerService + * Function: IsSpecifiedUIAbilityAlreadyExist + * FunctionPoints: AbilityManagerService IsSpecifiedUIAbilityAlreadyExist + */ +HWTEST_F(AbilityManagerServiceSixthTest, IsSpecifiedUIAbilityAlreadyExist_001, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "AbilityManagerServiceSixthTest IsSpecifiedUIAbilityAlreadyExist_001 start"); + auto abilityMs = std::make_shared(); + Want want; + std::string specifiedFlag; + int32_t appIndex = 0; + std::string instanceKey; + auto ret = abilityMs->IsSpecifiedUIAbilityAlreadyExist(want, specifiedFlag, appIndex, instanceKey); + EXPECT_EQ(ret, ERR_INVALID_VALUE); + TAG_LOGI(AAFwkTag::TEST, "AbilityManagerServiceSixthTest IsSpecifiedUIAbilityAlreadyExist_001 end"); +} + +/* + * Feature: AbilityManagerService + * Function: IsSpecifiedUIAbilityAlreadyExist + * FunctionPoints: AbilityManagerService IsSpecifiedUIAbilityAlreadyExist + */ +HWTEST_F(AbilityManagerServiceSixthTest, IsSpecifiedUIAbilityAlreadyExist_002, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "AbilityManagerServiceSixthTest IsSpecifiedUIAbilityAlreadyExist_002 start"); + auto abilityMs = MockAbilityManagerService(); + ASSERT_NE(abilityMs, nullptr); + auto foregroundUserId = AbilityRuntime::UserController::GetInstance().GetForegroundUserId( + AbilityRuntime::ServerConstant::DEFAULT_DISPLAY_ID); + abilityMs->subManagersHelper_->uiAbilityManagers_[0] = + std::make_shared(0); + abilityMs->subManagersHelper_->uiAbilityManagers_[USER_ID_U100] = + std::make_shared(USER_ID_U100); + abilityMs->subManagersHelper_->uiAbilityManagers_[foregroundUserId] = + std::make_shared(foregroundUserId); + + Want want; + want.SetElementName("target.bundle", "TargetAbility"); + std::string specifiedFlag = "specifiedFlag"; + int32_t appIndex = 0; + std::string instanceKey; + auto ret = abilityMs->IsSpecifiedUIAbilityAlreadyExist(want, specifiedFlag, appIndex, instanceKey); + EXPECT_EQ(ret, ERR_OK); + TAG_LOGI(AAFwkTag::TEST, "AbilityManagerServiceSixthTest IsSpecifiedUIAbilityAlreadyExist_002 end"); +} + +/* + * Feature: AbilityManagerService + * Function: CheckStartSelfUIAbilityInChildProcess + * FunctionPoints: AbilityManagerService CheckStartSelfUIAbilityInChildProcess + */ +HWTEST_F(AbilityManagerServiceSixthTest, CheckStartSelfUIAbilityInChildProcess_001, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "AbilityManagerServiceSixthTest CheckStartSelfUIAbilityInChildProcess_001 start"); + auto abilityMs = std::make_shared(); + Want want; + std::string specifiedFlag = "flag"; + AppExecFwk::AbilityInfo outAbilityInfo; + auto ret = abilityMs->CheckStartSelfUIAbilityInChildProcess(want, specifiedFlag, nullptr, outAbilityInfo); + EXPECT_EQ(ret, START_UI_ABILITIES_NOT_SUPPORT_IMPLICIT_START); + TAG_LOGI(AAFwkTag::TEST, "AbilityManagerServiceSixthTest CheckStartSelfUIAbilityInChildProcess_001 end"); +} + +/* + * Feature: AbilityManagerService + * Function: CheckStartSelfUIAbilityInChildProcess + * FunctionPoints: AbilityManagerService CheckStartSelfUIAbilityInChildProcess + */ +HWTEST_F(AbilityManagerServiceSixthTest, CheckStartSelfUIAbilityInChildProcess_002, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "AbilityManagerServiceSixthTest CheckStartSelfUIAbilityInChildProcess_002 start"); + auto abilityMs = std::make_shared(); + + auto scheduler = DelayedSingleton::GetInstance(); + ASSERT_NE(scheduler, nullptr); + auto mockClient = std::make_unique(); + scheduler->appMgrClient_ = std::move(mockClient); + + Want want; + want.SetElementName("target.bundle", "TargetAbility"); + AppExecFwk::AbilityInfo abilityInfo; + AppExecFwk::ApplicationInfo applicationInfo; + applicationInfo.bundleName = "target.bundle"; + applicationInfo.accessTokenId = IPCSkeleton::GetCallingTokenID(); + auto callerRecord = std::make_shared(want, abilityInfo, applicationInfo); + callerRecord->Init(AbilityRequest()); + + std::string specifiedFlag = "flag"; + AppExecFwk::AbilityInfo outAbilityInfo; + auto ret = abilityMs->CheckStartSelfUIAbilityInChildProcess(want, specifiedFlag, callerRecord, outAbilityInfo); + EXPECT_EQ(ret, TARGET_BUNDLE_NOT_EXIST); + TAG_LOGI(AAFwkTag::TEST, "AbilityManagerServiceSixthTest CheckStartSelfUIAbilityInChildProcess_002 end"); +} + +/* + * Feature: AbilityManagerService + * Function: CheckStartSelfUIAbilityInChildProcess + * FunctionPoints: AbilityManagerService CheckStartSelfUIAbilityInChildProcess + */ +HWTEST_F(AbilityManagerServiceSixthTest, CheckStartSelfUIAbilityInChildProcess_003, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "AbilityManagerServiceSixthTest CheckStartSelfUIAbilityInChildProcess_003 start"); + auto abilityMs = std::make_shared(); + + auto scheduler = DelayedSingleton::GetInstance(); + ASSERT_NE(scheduler, nullptr); + auto mockClient = std::make_unique(); + scheduler->appMgrClient_ = std::move(mockClient); + + Want want; + want.SetElementName("target.bundle", "TargetAbility"); + AppExecFwk::AbilityInfo abilityInfo; + AppExecFwk::ApplicationInfo applicationInfo; + applicationInfo.bundleName = "other.bundle"; + applicationInfo.accessTokenId = IPCSkeleton::GetCallingTokenID(); + auto callerRecord = std::make_shared(want, abilityInfo, applicationInfo); + callerRecord->Init(AbilityRequest()); + + std::string specifiedFlag = "flag"; + AppExecFwk::AbilityInfo outAbilityInfo; + auto ret = abilityMs->CheckStartSelfUIAbilityInChildProcess(want, specifiedFlag, callerRecord, outAbilityInfo); + EXPECT_EQ(ret, ERROR_UIABILITY_NOT_BELONG_TO_CALLER); + TAG_LOGI(AAFwkTag::TEST, "AbilityManagerServiceSixthTest CheckStartSelfUIAbilityInChildProcess_003 end"); +} + +/* + * Feature: AbilityManagerService + * Function: CheckStartSelfUIAbilityInChildProcess + * FunctionPoints: AbilityManagerService CheckStartSelfUIAbilityInChildProcess + */ +HWTEST_F(AbilityManagerServiceSixthTest, CheckStartSelfUIAbilityInChildProcess_004, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "AbilityManagerServiceSixthTest CheckStartSelfUIAbilityInChildProcess_004 start"); + auto abilityMs = std::make_shared(); + + auto scheduler = DelayedSingleton::GetInstance(); + ASSERT_NE(scheduler, nullptr); + auto mockClient = std::make_unique(); + mockClient->configuredInstanceKey = "instance_origin"; + scheduler->appMgrClient_ = std::move(mockClient); + + Want want; + want.SetElementName("target.bundle", "TargetAbility"); + want.SetParam(Want::APP_INSTANCE_KEY, std::string("instance_other")); + AppExecFwk::AbilityInfo abilityInfo; + AppExecFwk::ApplicationInfo applicationInfo; + applicationInfo.bundleName = "target.bundle"; + applicationInfo.accessTokenId = IPCSkeleton::GetCallingTokenID(); + auto callerRecord = std::make_shared(want, abilityInfo, applicationInfo); + callerRecord->Init(AbilityRequest()); + + std::string specifiedFlag = "flag"; + AppExecFwk::AbilityInfo outAbilityInfo; + auto ret = abilityMs->CheckStartSelfUIAbilityInChildProcess(want, specifiedFlag, callerRecord, outAbilityInfo); + EXPECT_EQ(ret, ERROR_UIABILITY_NOT_BELONG_TO_CALLER); + TAG_LOGI(AAFwkTag::TEST, "AbilityManagerServiceSixthTest CheckStartSelfUIAbilityInChildProcess_004 end"); +} + +/* + * Feature: AbilityManagerService + * Function: CheckStartSelfUIAbilityInChildProcess + * FunctionPoints: AbilityManagerService CheckStartSelfUIAbilityInChildProcess + */ +HWTEST_F(AbilityManagerServiceSixthTest, CheckStartSelfUIAbilityInChildProcess_005, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "AbilityManagerServiceSixthTest CheckStartSelfUIAbilityInChildProcess_005 start"); + auto abilityMs = std::make_shared(); + + auto scheduler = DelayedSingleton::GetInstance(); + ASSERT_NE(scheduler, nullptr); + auto mockClient = std::make_unique(); + mockClient->configuredInstanceKey = "instance_origin"; + scheduler->appMgrClient_ = std::move(mockClient); + + Want want; + want.SetElementName("target.bundle", "TargetAbility"); + AppExecFwk::AbilityInfo abilityInfo; + AppExecFwk::ApplicationInfo applicationInfo; + applicationInfo.bundleName = "target.bundle"; + applicationInfo.accessTokenId = IPCSkeleton::GetCallingTokenID(); + auto callerRecord = std::make_shared(want, abilityInfo, applicationInfo); + callerRecord->Init(AbilityRequest()); + + std::string specifiedFlag = "flag"; + AppExecFwk::AbilityInfo outAbilityInfo; + auto ret = abilityMs->CheckStartSelfUIAbilityInChildProcess(want, specifiedFlag, callerRecord, outAbilityInfo); + EXPECT_EQ(ret, TARGET_BUNDLE_NOT_EXIST); + TAG_LOGI(AAFwkTag::TEST, "AbilityManagerServiceSixthTest CheckStartSelfUIAbilityInChildProcess_005 end"); +} + /* * Feature: AbilityManagerService * Function: StartSelfUIAbilityInCurrentProcess @@ -2503,5 +2696,60 @@ HWTEST_F(AbilityManagerServiceSixthTest, UnRegisterPreloadUIExtensionHostClient_ auto ret = abilityMs->UnRegisterPreloadUIExtensionHostClient(DEFAULT_INVALID_USER_ID); EXPECT_EQ(ret, ERR_INVALID_VALUE); } + +/* + * Feature: AbilityManagerService + * Function: AtomicServicePreprocess + * FunctionPoints: AbilityManagerService AtomicServicePreprocess + */ +HWTEST_F(AbilityManagerServiceSixthTest, AtomicServicePreprocess_001, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "AbilityManagerServiceSixthTest AtomicServicePreprocess_001 start"); + auto abilityMs = std::make_shared(); + ASSERT_NE(abilityMs, nullptr); + Want want; + EXPECT_EQ(abilityMs->AtomicServicePreprocess(want), 0); + TAG_LOGI(AAFwkTag::TEST, "AbilityManagerServiceSixthTest AtomicServicePreprocess_001 end"); +} + +/* + * Feature: AbilityManagerService + * Function: IsFloodAttackByCallerUid + * FunctionPoints: AbilityManagerService IsFloodAttackByCallerUid + */ +HWTEST_F(AbilityManagerServiceSixthTest, IsFloodAttackByCallerUid_001, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "AbilityManagerServiceSixthTest IsFloodAttackByCallerUid_001 start"); + auto abilityMs = std::make_shared(); + ASSERT_NE(abilityMs, nullptr); + int32_t callerUid = 1000; + EXPECT_FALSE(abilityMs->IsFloodAttackByCallerUid(callerUid)); + bool isFloodAttack = false; + for (int i = 0; i < 11; ++i) { + isFloodAttack = abilityMs->IsFloodAttackByCallerUid(callerUid); + } + EXPECT_TRUE(isFloodAttack); + TAG_LOGI(AAFwkTag::TEST, "AbilityManagerServiceSixthTest IsFloodAttackByCallerUid_001 end"); +} + +/* + * Feature: AbilityManagerService + * Function: GetCallerUidAndToken + * FunctionPoints: AbilityManagerService GetCallerUidAndToken + */ +HWTEST_F(AbilityManagerServiceSixthTest, GetCallerUidAndToken_001, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "AbilityManagerServiceSixthTest GetCallerUidAndToken_001 start"); + auto abilityMs = std::make_shared(); + ASSERT_NE(abilityMs, nullptr); + std::string bundleName = "com.example.bundle"; + int32_t userId = 1000; + int32_t callerUid = 0; + uint32_t accessToken = 0; + abilityMs->GetCallerUidAndToken(bundleName, userId, callerUid, accessToken); + EXPECT_EQ(callerUid, 0); + EXPECT_EQ(accessToken, 0); + TAG_LOGI(AAFwkTag::TEST, "AbilityManagerServiceSixthTest GetCallerUidAndToken_001 end"); +} } // namespace AAFwk } // namespace OHOS \ No newline at end of file diff --git a/test/unittest/ability_manager_service_sixth_test/abilitymgr.gni b/test/unittest/ability_manager_service_sixth_test/abilitymgr.gni index 1bbc12d2bb..0466ea098d 100644 --- a/test/unittest/ability_manager_service_sixth_test/abilitymgr.gni +++ b/test/unittest/ability_manager_service_sixth_test/abilitymgr.gni @@ -135,6 +135,13 @@ abilityms_files = [ "${ability_runtime_services_path}/abilitymgr/src/insight_intent/insight_intent_event_mgr.cpp", "${ability_runtime_services_path}/abilitymgr/src/insight_intent/insight_intent_sys_event_receiver.cpp", + #skill execution + "${ability_runtime_services_path}/abilitymgr/src/skill/skill_execute_callback_proxy.cpp", + "${ability_runtime_services_path}/abilitymgr/src/skill/skill_execute_callback_stub.cpp", + "${ability_runtime_services_path}/abilitymgr/src/skill/skill_execute_param.cpp", + "${ability_runtime_services_path}/abilitymgr/src/skill/skill_execute_result.cpp", + "${ability_runtime_services_path}/abilitymgr/src/skill/skill_execute_manager.cpp", + #sa interceptor "${ability_runtime_services_path}/abilitymgr/src/sa_interceptor_manager.cpp", diff --git a/test/unittest/ability_manager_service_third_test/ability_manager_service_third_test.cpp b/test/unittest/ability_manager_service_third_test/ability_manager_service_third_test.cpp index d103080c24..d74e1fae41 100644 --- a/test/unittest/ability_manager_service_third_test/ability_manager_service_third_test.cpp +++ b/test/unittest/ability_manager_service_third_test/ability_manager_service_third_test.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023-2025 Huawei Device Co., Ltd. + * Copyright (c) 2023-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 @@ -1197,7 +1197,7 @@ HWTEST_F(AbilityManagerServiceThirdTest, CheckRestartAppWant_001, TestSize.Level EXPECT_NE(abilityMs, nullptr); AAFwk::Want want; int32_t userId = 100; - int32_t res = abilityMs->CheckRestartAppWant(want, 0, userId); + int32_t res = abilityMs->CheckRestartAppWant(want.GetElement(), 0, userId); EXPECT_EQ(res, AAFwk::ERR_RESTART_APP_INCORRECT_ABILITY); } @@ -3370,5 +3370,6 @@ HWTEST_F(AbilityManagerServiceThirdTest, CheckSupportVpn_002, TestSize.Level1) auto result = abilityMs_->CheckSupportVpn(abilityInfo); EXPECT_TRUE(result); } + } // namespace AAFwk } // namespace OHOS diff --git a/test/unittest/ability_manager_service_third_test/abilitymgr.gni b/test/unittest/ability_manager_service_third_test/abilitymgr.gni index f09029d34f..7469beabbf 100644 --- a/test/unittest/ability_manager_service_third_test/abilitymgr.gni +++ b/test/unittest/ability_manager_service_third_test/abilitymgr.gni @@ -135,6 +135,13 @@ abilityms_files = [ "${ability_runtime_services_path}/abilitymgr/src/insight_intent/insight_intent_event_mgr.cpp", "${ability_runtime_services_path}/abilitymgr/src/insight_intent/insight_intent_sys_event_receiver.cpp", + #skill execution + "${ability_runtime_services_path}/abilitymgr/src/skill/skill_execute_callback_proxy.cpp", + "${ability_runtime_services_path}/abilitymgr/src/skill/skill_execute_callback_stub.cpp", + "${ability_runtime_services_path}/abilitymgr/src/skill/skill_execute_param.cpp", + "${ability_runtime_services_path}/abilitymgr/src/skill/skill_execute_result.cpp", + "${ability_runtime_services_path}/abilitymgr/src/skill/skill_execute_manager.cpp", + #sa interceptor "${ability_runtime_services_path}/abilitymgr/src/sa_interceptor_manager.cpp", diff --git a/test/unittest/ability_manager_service_thirteenth_test/BUILD.gn b/test/unittest/ability_manager_service_thirteenth_test/BUILD.gn index f2ba7edc97..453a887be0 100644 --- a/test/unittest/ability_manager_service_thirteenth_test/BUILD.gn +++ b/test/unittest/ability_manager_service_thirteenth_test/BUILD.gn @@ -43,6 +43,7 @@ ohos_unittest("ability_manager_service_thirteenth_test") { "${ability_runtime_abilitymgr_path}/include/resident_process/", "${ability_runtime_abilitymgr_path}/include/screen_lock/", "${ability_runtime_abilitymgr_path}/include/ui_extension", + "${ability_runtime_abilitymgr_path}/include/skill", "${ability_runtime_abilitymgr_path}/include/ui_extension_record", "${ability_runtime_abilitymgr_path}/include/utils/", "${ability_runtime_innerkits_path}/ability_manager/include", @@ -170,10 +171,17 @@ ohos_unittest("ability_manager_service_thirteenth_test") { "${ability_runtime_services_path}/abilitymgr/src/rdb/parser_util.cpp", "${ability_runtime_services_path}/abilitymgr/src/rdb/rdb_data_manager.cpp", "${ability_runtime_services_path}/abilitymgr/src/recovery_info_timer.cpp", + "${ability_runtime_services_path}/abilitymgr/src/remote_intent_result_callback_stub.cpp", + "${ability_runtime_services_path}/abilitymgr/src/remote_intent_result_callback.cpp", "${ability_runtime_services_path}/abilitymgr/src/remote_mission_listener_proxy.cpp", "${ability_runtime_services_path}/abilitymgr/src/report_data_partition_usage_manager.cpp", "${ability_runtime_services_path}/abilitymgr/src/resident_process/resident_process_manager.cpp", "${ability_runtime_services_path}/abilitymgr/src/restart_app_manager.cpp", + "${ability_runtime_services_path}/abilitymgr/src/skill/skill_execute_callback_proxy.cpp", + "${ability_runtime_services_path}/abilitymgr/src/skill/skill_execute_callback_stub.cpp", + "${ability_runtime_services_path}/abilitymgr/src/skill/skill_execute_param.cpp", + "${ability_runtime_services_path}/abilitymgr/src/skill/skill_execute_result.cpp", + "${ability_runtime_services_path}/abilitymgr/src/skill/skill_execute_manager.cpp", "${ability_runtime_services_path}/abilitymgr/src/sa_interceptor_manager.cpp", "${ability_runtime_services_path}/abilitymgr/src/scene_board/status_bar_delegate_manager.cpp", "${ability_runtime_services_path}/abilitymgr/src/scene_board/ui_ability_record.cpp", diff --git a/test/unittest/ability_manager_service_thirteenth_test/mock/include/mock_app_utils.h b/test/unittest/ability_manager_service_thirteenth_test/mock/include/mock_app_utils.h index 8b83eae625..fd065b063e 100644 --- a/test/unittest/ability_manager_service_thirteenth_test/mock/include/mock_app_utils.h +++ b/test/unittest/ability_manager_service_thirteenth_test/mock/include/mock_app_utils.h @@ -321,6 +321,10 @@ public: bool IsPreloadApplicationEnabled(); + bool IsForbidStart(); + + bool IsSupportDelayedProcessExit(); + private: /** * LoadResidentProcessInExtremeMemory, load resident process in extreme low memory. diff --git a/test/unittest/ability_manager_service_thirteenth_test/mock/include/mock_my_status.h b/test/unittest/ability_manager_service_thirteenth_test/mock/include/mock_my_status.h index e4920d521e..18b9229205 100644 --- a/test/unittest/ability_manager_service_thirteenth_test/mock/include/mock_my_status.h +++ b/test/unittest/ability_manager_service_thirteenth_test/mock/include/mock_my_status.h @@ -63,6 +63,10 @@ public: int32_t fimConnectExtensionType_ = -1; std::string fimConnectLocalDeviceId_; int32_t softbusGetLocalNodeDeviceInfo_ = ERR_OK; + bool auIsSupportDelayedProcessExit_ = false; + int32_t oeuValidateCallerResult_ = ERR_OK; + int32_t oeuValidateCallerUserId_ = 0; + std::string oeuValidateCallerHostBundleName_; }; } // namespace AAFwk } // namespace OHOS diff --git a/test/unittest/ability_manager_service_thirteenth_test/mock/include/mock_ui_ability_lifecycle_manager.h b/test/unittest/ability_manager_service_thirteenth_test/mock/include/mock_ui_ability_lifecycle_manager.h index c85093882e..f46b684060 100644 --- a/test/unittest/ability_manager_service_thirteenth_test/mock/include/mock_ui_ability_lifecycle_manager.h +++ b/test/unittest/ability_manager_service_thirteenth_test/mock/include/mock_ui_ability_lifecycle_manager.h @@ -425,6 +425,8 @@ public: ErrCode QueryCallerTokenIdForAnco(const std::string &asCallerForAncoSessionId, uint32_t &callerTokenId); ErrCode IsUIAbilityAlreadyExist(const Want &want, const std::string &specifiedFlag, int32_t appIndex, const std::string &instanceKey, AppExecFwk::LaunchMode launchMode); + ErrCode IsSpecifiedUIAbilityAlreadyExist(const Want &want, const std::string &specifiedFlag, + int32_t appIndex, const std::string &instanceKey); void HandleUIAbilityDiedByPid(pid_t pid); int32_t StartSelf(const UIAbilityRecordPtr &abilityRecord); diff --git a/test/unittest/ability_manager_service_thirteenth_test/mock/src/mock_ability_record.cpp b/test/unittest/ability_manager_service_thirteenth_test/mock/src/mock_ability_record.cpp index df894c2b30..c3901367d7 100644 --- a/test/unittest/ability_manager_service_thirteenth_test/mock/src/mock_ability_record.cpp +++ b/test/unittest/ability_manager_service_thirteenth_test/mock/src/mock_ability_record.cpp @@ -366,7 +366,7 @@ bool AbilityRecord::CanRestartResident() } // only for UIAbility -void AbilityRecord::ForegroundAbility(uint32_t sceneFlag, bool hasLastWant) +void AbilityRecord::ForegroundAbility(uint32_t sceneFlag) { } @@ -1672,6 +1672,48 @@ bool AbilityRecord::GetKillForPermissionUpdateFlag() const return isKillForPermissionUpdate_; } +std::string AbilityRecord::GetAbilityName() const +{ + std::lock_guard guard(wantLock_); + return want_.GetElement().GetAbilityName(); +} + +std::string AbilityRecord::GetBundleName() const +{ + std::lock_guard guard(wantLock_); + return want_.GetBundle(); +} + +std::string AbilityRecord::GetModuleName() const +{ + std::lock_guard guard(wantLock_); + return want_.GetModuleName(); +} + +std::string AbilityRecord::GetStringParam(const std::string &key) const +{ + std::lock_guard guard(wantLock_); + return want_.GetStringParam(key); +} + +int AbilityRecord::GetIntParam(const std::string &key, int defaultValue) const +{ + std::lock_guard guard(wantLock_); + return want_.GetIntParam(key, defaultValue); +} + +bool AbilityRecord::GetBoolParam(const std::string &key, bool defaultValue) const +{ + std::lock_guard guard(wantLock_); + return want_.GetBoolParam(key, defaultValue); +} + +bool AbilityRecord::HasParameter(const std::string &key) const +{ + std::lock_guard guard(wantLock_); + return want_.HasParameter(key); +} + void AbilityRecord::UpdateUIExtensionInfo(const WantParams &wantParams) { } diff --git a/test/unittest/ability_manager_service_thirteenth_test/mock/src/mock_app_utils.cpp b/test/unittest/ability_manager_service_thirteenth_test/mock/src/mock_app_utils.cpp index 9eb41be014..03e8407c55 100644 --- a/test/unittest/ability_manager_service_thirteenth_test/mock/src/mock_app_utils.cpp +++ b/test/unittest/ability_manager_service_thirteenth_test/mock/src/mock_app_utils.cpp @@ -257,5 +257,15 @@ bool AppUtils::IsPreloadApplicationEnabled() { return false; } + +bool AppUtils::IsForbidStart() +{ + return false; +} + +bool AppUtils::IsSupportDelayedProcessExit() +{ + return MyStatus::GetInstance().auIsSupportDelayedProcessExit_; +} } // namespace AAFwk } // namespace OHOS diff --git a/test/unittest/ability_manager_service_thirteenth_test/mock/src/mock_ui_ability_lifecycle_manager.cpp b/test/unittest/ability_manager_service_thirteenth_test/mock/src/mock_ui_ability_lifecycle_manager.cpp index 9c032c36fb..178ffc81ce 100644 --- a/test/unittest/ability_manager_service_thirteenth_test/mock/src/mock_ui_ability_lifecycle_manager.cpp +++ b/test/unittest/ability_manager_service_thirteenth_test/mock/src/mock_ui_ability_lifecycle_manager.cpp @@ -787,6 +787,12 @@ ErrCode UIAbilityLifecycleManager::IsUIAbilityAlreadyExist(const Want &want, return ERR_OK; } +ErrCode UIAbilityLifecycleManager::IsSpecifiedUIAbilityAlreadyExist(const Want &want, + const std::string &specifiedFlag, int32_t appIndex, const std::string &instanceKey) +{ + return ERR_OK; +} + void UIAbilityLifecycleManager::HandleUIAbilityDiedByPid(pid_t pid) { return; diff --git a/test/unittest/ability_manager_service_twelfth_test/ability_manager_service_twelfth_test.cpp b/test/unittest/ability_manager_service_twelfth_test/ability_manager_service_twelfth_test.cpp index eda0ae20db..55b46299bd 100644 --- a/test/unittest/ability_manager_service_twelfth_test/ability_manager_service_twelfth_test.cpp +++ b/test/unittest/ability_manager_service_twelfth_test/ability_manager_service_twelfth_test.cpp @@ -2115,5 +2115,446 @@ HWTEST_F(AbilityManagerServiceTwelfthTest, RequestModalUIExtensionWithAccount_00 MyFlag::retCreateModalUIExtension_ = true; // 恢复默认值 TAG_LOGI(AAFwkTag::TEST, "AbilityManagerServiceTwelfthTest RequestModalUIExtensionWithAccount_008 end"); } + +/* + * Feature: AbilityManagerService + * Function: RequestModalUIExtensionWithAccount + * SubFunction: NA + * FunctionPoints: Request modal UI extension with DEFAULT_INVAL_VALUE accountId + */ +HWTEST_F(AbilityManagerServiceTwelfthTest, RequestModalUIExtensionWithAccount_009, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "AbilityManagerServiceTwelfthTest RequestModalUIExtensionWithAccount_009 start"); + auto abilityMs_ = std::make_shared(); + EXPECT_TRUE(abilityMs_ != nullptr); + + MyFlag::retCreateModalUIExtension_ = true; + + // Test: accountId == DEFAULT_INVAL_VALUE (-1), triggers GetValidUserId path + Want want; + std::string bundleName = "com.test.demo"; + want.SetParam("bundleName", bundleName); + int32_t accountId = DEFAULT_INVAL_VALUE; // -1 + MyFlag::flag_ = 0; // Mock permission fail for default userId + + auto result = abilityMs_->RequestModalUIExtensionWithAccount(want, accountId); + // GetValidUserId converts -1 to current userId, then GetDisplayIdByAccount may fail + EXPECT_EQ(result, ERR_OK); + + MyFlag::retCreateModalUIExtension_ = true; + TAG_LOGI(AAFwkTag::TEST, "AbilityManagerServiceTwelfthTest RequestModalUIExtensionWithAccount_009 end"); +} + +/* + * Feature: AbilityManagerService + * Function: RequestModalUIExtensionWithAccount + * SubFunction: NA + * FunctionPoints: Request modal UI extension with accountId=0 + */ +HWTEST_F(AbilityManagerServiceTwelfthTest, RequestModalUIExtensionWithAccount_010, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "AbilityManagerServiceTwelfthTest RequestModalUIExtensionWithAccount_010 start"); + auto abilityMs_ = std::make_shared(); + EXPECT_TRUE(abilityMs_ != nullptr); + + MyFlag::retCreateModalUIExtension_ = true; + + // Test: accountId = 0 (not DEFAULT_INVAL_VALUE, so no GetValidUserId call) + Want want; + std::string bundleName = "com.test.demo"; + want.SetParam("bundleName", bundleName); + int32_t accountId = 0; + MyFlag::flag_ = 0; // Mock permission fail + + auto result = abilityMs_->RequestModalUIExtensionWithAccount(want, accountId); + EXPECT_NE(result, ERR_OK); + + MyFlag::retCreateModalUIExtension_ = true; + TAG_LOGI(AAFwkTag::TEST, "AbilityManagerServiceTwelfthTest RequestModalUIExtensionWithAccount_010 end"); +} + +/* + * Feature: AbilityManagerService + * Function: RequestModalUIExtensionWithAccount + * SubFunction: NA + * FunctionPoints: Request modal UI extension with retCreateModalUIExtension_ = false + */ +HWTEST_F(AbilityManagerServiceTwelfthTest, RequestModalUIExtensionWithAccount_011, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "AbilityManagerServiceTwelfthTest RequestModalUIExtensionWithAccount_011 start"); + auto abilityMs_ = std::make_shared(); + EXPECT_TRUE(abilityMs_ != nullptr); + + // Test: ModalSystemUiExtension::CreateModalUIExtension returns false + MyFlag::retCreateModalUIExtension_ = false; + + Want want; + std::string bundleName = "com.test.demo"; + want.SetParam("bundleName", bundleName); + int32_t accountId = 100; + uint64_t displayId = 1; + MyFlag::flag_ = 1; // Mock permission pass but no GetTopAbility success + + AbilityRuntime::UserController::GetInstance().SetForegroundUserId(accountId, displayId); + + // Falls through to ModalSystemUiExtension which returns false → INNER_ERR + auto result = abilityMs_->RequestModalUIExtensionWithAccount(want, accountId); + EXPECT_EQ(result, INNER_ERR); + + AbilityRuntime::UserController::GetInstance().ClearUserId(accountId); + MyFlag::retCreateModalUIExtension_ = true; + TAG_LOGI(AAFwkTag::TEST, "AbilityManagerServiceTwelfthTest RequestModalUIExtensionWithAccount_011 end"); +} + +/* + * Feature: AbilityManagerService + * Function: RequestModalUIExtensionWithAccount + * SubFunction: NA + * FunctionPoints: Request modal UI extension without bundleName param + */ +HWTEST_F(AbilityManagerServiceTwelfthTest, RequestModalUIExtensionWithAccount_012, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "AbilityManagerServiceTwelfthTest RequestModalUIExtensionWithAccount_012 start"); + auto abilityMs_ = std::make_shared(); + EXPECT_TRUE(abilityMs_ != nullptr); + + MyFlag::retCreateModalUIExtension_ = true; + + // Test: no bundleName param in want, callerName will be empty + Want want; + // intentionally NOT setting bundleName + int32_t accountId = 100; + uint64_t displayId = 1; + MyFlag::flag_ = 1; + + AbilityRuntime::UserController::GetInstance().SetForegroundUserId(accountId, displayId); + + auto result = abilityMs_->RequestModalUIExtensionWithAccount(want, accountId); + // Falls through to ModalSystemUiExtension because bundleName mismatch + EXPECT_EQ(result, ERR_OK); + + AbilityRuntime::UserController::GetInstance().ClearUserId(accountId); + MyFlag::retCreateModalUIExtension_ = true; + TAG_LOGI(AAFwkTag::TEST, "AbilityManagerServiceTwelfthTest RequestModalUIExtensionWithAccount_012 end"); +} + +/* + * Feature: AbilityManagerService + * Function: GetDisplayIdByAccount + * SubFunction: NA + * FunctionPoints: Positive accountId, callerUser matches userId via IPCSkeleton, no display found + */ +HWTEST_F(AbilityManagerServiceTwelfthTest, GetDisplayIdByAccount_005, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "AbilityManagerServiceTwelfthTest GetDisplayIdByAccount_005 start"); + auto abilityMs_ = std::make_shared(); + EXPECT_TRUE(abilityMs_ != nullptr); + + // 分支: callerUser == userId (通过 IPCSkeleton uid 匹配),权限检查通过, + // 但 GetDisplayIdByForegroundUserId 返回 false → ERR_INVALID_VALUE + MyFlag::flag_ = 0; + int32_t accountId = 100; + uint64_t displayId = 0; + IPCSkeleton::SetCallingUid(accountId * BASE_USER_RANGE); // callerUser = 100 = accountId + + EXPECT_EQ(abilityMs_->GetDisplayIdByAccount(accountId, displayId), ERR_OK); + + IPCSkeleton::SetCallingUid(0); // cleanup + TAG_LOGI(AAFwkTag::TEST, "AbilityManagerServiceTwelfthTest GetDisplayIdByAccount_005 end"); +} + +/* + * Feature: AbilityManagerService + * Function: GetDisplayIdByAccount + * SubFunction: NA + * FunctionPoints: accountId=0, callerUser=0=userId, displayId found successfully + */ +HWTEST_F(AbilityManagerServiceTwelfthTest, GetDisplayIdByAccount_006, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "AbilityManagerServiceTwelfthTest GetDisplayIdByAccount_006 start"); + auto abilityMs_ = std::make_shared(); + EXPECT_TRUE(abilityMs_ != nullptr); + + // 分支: accountId=0, callerUser=0=userId → VerifyAccountPermission returns ERR_OK + // SetForegroundUserId(0, 5) → GetDisplayIdByForegroundUserId returns true + MyFlag::flag_ = 0; + int32_t accountId = 0; + uint64_t displayId = 0; + uint64_t expectedDisplayId = 5; + + AbilityRuntime::UserController::GetInstance().SetForegroundUserId(accountId, expectedDisplayId); + + EXPECT_EQ(abilityMs_->GetDisplayIdByAccount(accountId, displayId), ERR_OK); + EXPECT_EQ(displayId, expectedDisplayId); + + AbilityRuntime::UserController::GetInstance().ClearUserId(accountId); + TAG_LOGI(AAFwkTag::TEST, "AbilityManagerServiceTwelfthTest GetDisplayIdByAccount_006 end"); +} + +/* + * Feature: AbilityManagerService + * Function: GetTopAbilityByUserId + * SubFunction: NA + * FunctionPoints: VerifyAccountPermission returns CHECK_PERMISSION_FAILED + */ +HWTEST_F(AbilityManagerServiceTwelfthTest, GetTopAbilityByUserId_008, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "AbilityManagerServiceTwelfthTest GetTopAbilityByUserId_008 start"); + auto abilityMs_ = std::make_shared(); + EXPECT_TRUE(abilityMs_ != nullptr); + + // 分支: VerifyAccountPermission 返回 CHECK_PERMISSION_FAILED + // callerUser=0=U0, IsForegroundUser(100)=false, callerUser!=100 → falls to mock + MyFlag::flag_ = CHECK_PERMISSION_FAILED; + sptr token = nullptr; + int32_t userId = 100; + uint64_t displayId = 0; + + EXPECT_EQ(abilityMs_->GetTopAbilityByUserId(token, userId, displayId), ERR_INVALID_VALUE); + + TAG_LOGI(AAFwkTag::TEST, "AbilityManagerServiceTwelfthTest GetTopAbilityByUserId_008 end"); +} + +/* + * Feature: AbilityManagerService + * Function: GetTopAbilityByUserId + * SubFunction: NA + * FunctionPoints: callerUser matches userId via IPCSkeleton uid, permission OK, wmsHandler empty + */ +HWTEST_F(AbilityManagerServiceTwelfthTest, GetTopAbilityByUserId_009, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "AbilityManagerServiceTwelfthTest GetTopAbilityByUserId_009 start"); + auto abilityMs_ = std::make_shared(); + EXPECT_TRUE(abilityMs_ != nullptr); + + // 分支: callerUser == userId (通过 IPCSkeleton uid 匹配) → VerifyAccountPermission OK + // 但 wmsHandler 为空 → ERR_INVALID_VALUE + MyFlag::flag_ = 0; + sptr token = nullptr; + int32_t userId = 100; + uint64_t displayId = 0; + IPCSkeleton::SetCallingUid(userId * BASE_USER_RANGE); // callerUser = 100 = userId + + EXPECT_EQ(abilityMs_->GetTopAbilityByUserId(token, userId, displayId), ERR_INVALID_VALUE); + + IPCSkeleton::SetCallingUid(0); // cleanup + TAG_LOGI(AAFwkTag::TEST, "AbilityManagerServiceTwelfthTest GetTopAbilityByUserId_009 end"); +} + +/* + * Feature: AbilityManagerService + * Function: GetTopAbilityByUserId + * SubFunction: NA + * FunctionPoints: U0 callerUser with IsForegroundUser true, permission OK, wmsHandler empty + */ +HWTEST_F(AbilityManagerServiceTwelfthTest, GetTopAbilityByUserId_010, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "AbilityManagerServiceTwelfthTest GetTopAbilityByUserId_010 start"); + auto abilityMs_ = std::make_shared(); + EXPECT_TRUE(abilityMs_ != nullptr); + + // 分支: callerUser=0=U0, SetForegroundUserId(100, 1) → IsForegroundUser(100)=true + // → VerifyAccountPermission returns ERR_OK → wmsHandler 为空 → ERR_INVALID_VALUE + MyFlag::flag_ = 0; + sptr token = nullptr; + int32_t userId = 100; + uint64_t displayId = 1; + + AbilityRuntime::UserController::GetInstance().SetForegroundUserId(userId, displayId); + + EXPECT_EQ(abilityMs_->GetTopAbilityByUserId(token, userId, displayId), ERR_INVALID_VALUE); + + AbilityRuntime::UserController::GetInstance().ClearUserId(userId); + TAG_LOGI(AAFwkTag::TEST, "AbilityManagerServiceTwelfthTest GetTopAbilityByUserId_010 end"); +} + +/* + * Feature: AbilityManagerService + * Function: RequestModalUIExtensionWithAccount + * SubFunction: NA + * FunctionPoints: accountId=DEFAULT_INVAL_VALUE, GetDisplayIdByAccount succeeds, fallback path + */ +HWTEST_F(AbilityManagerServiceTwelfthTest, RequestModalUIExtensionWithAccount_013, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "AbilityManagerServiceTwelfthTest RequestModalUIExtensionWithAccount_013 start"); + auto abilityMs_ = std::make_shared(); + EXPECT_TRUE(abilityMs_ != nullptr); + + // 分支: accountId=DEFAULT_INVAL_VALUE → GetValidUserId returns 0 (callerUserId) + // SetForegroundUserId(0, 1) → GetDisplayIdByAccount(0) succeeds + // GetTopAbilityByUserId fails → fallback → ModalSystemUiExtension → ERR_OK + MyFlag::retCreateModalUIExtension_ = true; + MyFlag::flag_ = 1; + + Want want; + std::string bundleName = "com.test.demo"; + want.SetParam("bundleName", bundleName); + int32_t accountId = DEFAULT_INVAL_VALUE; // -1 + + // SetForegroundUserId for user 0 so GetDisplayIdByAccount succeeds + AbilityRuntime::UserController::GetInstance().SetForegroundUserId(0, 1); + + auto result = abilityMs_->RequestModalUIExtensionWithAccount(want, accountId); + EXPECT_EQ(result, ERR_OK); + + AbilityRuntime::UserController::GetInstance().ClearUserId(0); + MyFlag::retCreateModalUIExtension_ = true; + TAG_LOGI(AAFwkTag::TEST, "AbilityManagerServiceTwelfthTest RequestModalUIExtensionWithAccount_013 end"); +} + +/* + * Feature: AbilityManagerService + * Function: RequestModalUIExtensionWithAccount + * SubFunction: NA + * FunctionPoints: accountId=DEFAULT_INVAL_VALUE, GetDisplayIdByAccount succeeds, ModalSystemUiExtension fails + */ +HWTEST_F(AbilityManagerServiceTwelfthTest, RequestModalUIExtensionWithAccount_014, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "AbilityManagerServiceTwelfthTest RequestModalUIExtensionWithAccount_014 start"); + auto abilityMs_ = std::make_shared(); + EXPECT_TRUE(abilityMs_ != nullptr); + + // 分支: accountId=DEFAULT_INVAL_VALUE, GetDisplayIdByAccount succeeds + // GetTopAbilityByUserId fails → fallback → ModalSystemUiExtension returns false → INNER_ERR + MyFlag::retCreateModalUIExtension_ = false; + MyFlag::flag_ = 1; + + Want want; + std::string bundleName = "com.test.demo"; + want.SetParam("bundleName", bundleName); + int32_t accountId = DEFAULT_INVAL_VALUE; + + AbilityRuntime::UserController::GetInstance().SetForegroundUserId(0, 1); + + auto result = abilityMs_->RequestModalUIExtensionWithAccount(want, accountId); + EXPECT_EQ(result, INNER_ERR); + + AbilityRuntime::UserController::GetInstance().ClearUserId(0); + MyFlag::retCreateModalUIExtension_ = true; + TAG_LOGI(AAFwkTag::TEST, "AbilityManagerServiceTwelfthTest RequestModalUIExtensionWithAccount_014 end"); +} + +/* + * Feature: AbilityManagerService + * Function: RequestModalUIExtensionWithAccount + * SubFunction: NA + * FunctionPoints: Positive accountId, callerUser matches userId, GetDisplayIdByAccount succeeds, fallback + */ +HWTEST_F(AbilityManagerServiceTwelfthTest, RequestModalUIExtensionWithAccount_015, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "AbilityManagerServiceTwelfthTest RequestModalUIExtensionWithAccount_015 start"); + auto abilityMs_ = std::make_shared(); + EXPECT_TRUE(abilityMs_ != nullptr); + + // 分支: accountId=100, IPCSkeleton uid=100*BASE_USER_RANGE → callerUser=100=accountId + // VerifyAccountPermission OK (callerUser==userId), SetForegroundUserId(100, 2) + // GetDisplayIdByAccount succeeds → GetTopAbilityByUserId fails → fallback → ERR_OK + MyFlag::retCreateModalUIExtension_ = true; + MyFlag::flag_ = 1; + + Want want; + std::string bundleName = "com.test.demo"; + want.SetParam("bundleName", bundleName); + int32_t accountId = 100; + uint64_t displayId = 2; + + IPCSkeleton::SetCallingUid(accountId * BASE_USER_RANGE); + AbilityRuntime::UserController::GetInstance().SetForegroundUserId(accountId, displayId); + + auto result = abilityMs_->RequestModalUIExtensionWithAccount(want, accountId); + EXPECT_EQ(result, ERR_OK); + + IPCSkeleton::SetCallingUid(0); + AbilityRuntime::UserController::GetInstance().ClearUserId(accountId); + MyFlag::retCreateModalUIExtension_ = true; + TAG_LOGI(AAFwkTag::TEST, "AbilityManagerServiceTwelfthTest RequestModalUIExtensionWithAccount_015 end"); +} + +/* + * Feature: AbilityManagerService + * Function: RequestModalUIExtensionWithAccount + * SubFunction: NA + * FunctionPoints: Positive accountId, U0 with foreground user, ModalSystemUiExtension fails + */ +HWTEST_F(AbilityManagerServiceTwelfthTest, RequestModalUIExtensionWithAccount_016, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "AbilityManagerServiceTwelfthTest RequestModalUIExtensionWithAccount_016 start"); + auto abilityMs_ = std::make_shared(); + EXPECT_TRUE(abilityMs_ != nullptr); + + // 分支: accountId=100, callerUser=0=U0, IsForegroundUser(100)=true → VerifyAccountPermission OK + // SetForegroundUserId(100, 1) → GetDisplayIdByAccount succeeds + // GetTopAbilityByUserId fails → fallback → ModalSystemUiExtension returns false → INNER_ERR + MyFlag::retCreateModalUIExtension_ = false; + MyFlag::flag_ = 0; + + Want want; + std::string bundleName = "com.test.demo"; + want.SetParam("bundleName", bundleName); + int32_t accountId = 100; + uint64_t displayId = 1; + + AbilityRuntime::UserController::GetInstance().SetForegroundUserId(accountId, displayId); + + auto result = abilityMs_->RequestModalUIExtensionWithAccount(want, accountId); + EXPECT_EQ(result, INNER_ERR); + + AbilityRuntime::UserController::GetInstance().ClearUserId(accountId); + MyFlag::retCreateModalUIExtension_ = true; + TAG_LOGI(AAFwkTag::TEST, "AbilityManagerServiceTwelfthTest RequestModalUIExtensionWithAccount_016 end"); +} + +/* + * Feature: AbilityManagerService + * Function: RequestModalUIExtensionWithAccount + * SubFunction: NA + * FunctionPoints: CHECK_PERMISSION_FAILED from VerifyAccountPermission + */ +HWTEST_F(AbilityManagerServiceTwelfthTest, RequestModalUIExtensionWithAccount_017, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "AbilityManagerServiceTwelfthTest RequestModalUIExtensionWithAccount_017 start"); + auto abilityMs_ = std::make_shared(); + EXPECT_TRUE(abilityMs_ != nullptr); + + // 分支: VerifyAccountPermission returns CHECK_PERMISSION_FAILED via mock + // GetDisplayIdByAccount fails with CHECK_PERMISSION_FAILED + MyFlag::flag_ = CHECK_PERMISSION_FAILED; + + Want want; + std::string bundleName = "com.test.demo"; + want.SetParam("bundleName", bundleName); + int32_t accountId = 100; + + auto result = abilityMs_->RequestModalUIExtensionWithAccount(want, accountId); + EXPECT_EQ(result, ERR_OK); + + TAG_LOGI(AAFwkTag::TEST, "AbilityManagerServiceTwelfthTest RequestModalUIExtensionWithAccount_017 end"); +} + +/* + * Feature: AbilityManagerService + * Function: RequestModalUIExtensionWithAccount + * SubFunction: NA + * FunctionPoints: accountId=DEFAULT_INVAL_VALUE, no foreground user, GetDisplayIdByAccount fails + */ +HWTEST_F(AbilityManagerServiceTwelfthTest, RequestModalUIExtensionWithAccount_018, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "AbilityManagerServiceTwelfthTest RequestModalUIExtensionWithAccount_018 start"); + auto abilityMs_ = std::make_shared(); + EXPECT_TRUE(abilityMs_ != nullptr); + + // 分支: accountId=DEFAULT_INVAL_VALUE, GetValidUserId returns 0 + // callerUser=0=userId → VerifyAccountPermission(0) OK + // But GetDisplayIdByForegroundUserId(0) returns false (no foreground user 0) → ERR_INVALID_VALUE + MyFlag::flag_ = 0; + + Want want; + int32_t accountId = DEFAULT_INVAL_VALUE; + + auto result = abilityMs_->RequestModalUIExtensionWithAccount(want, accountId); + EXPECT_EQ(result, ERR_OK); + + TAG_LOGI(AAFwkTag::TEST, "AbilityManagerServiceTwelfthTest RequestModalUIExtensionWithAccount_018 end"); +} } // namespace AAFwk } // namespace OHOS diff --git a/test/unittest/ability_manager_stub_second_test/ability_manager_stub_second_test.cpp b/test/unittest/ability_manager_stub_second_test/ability_manager_stub_second_test.cpp index ae84dd91b7..76ab748efb 100644 --- a/test/unittest/ability_manager_stub_second_test/ability_manager_stub_second_test.cpp +++ b/test/unittest/ability_manager_stub_second_test/ability_manager_stub_second_test.cpp @@ -21,6 +21,7 @@ #include "iremote_proxy.h" #include "mock_ability_connect_callback.h" #include "mock_ability_token.h" +#include "skill_execute_result.h" using namespace testing::ext; using namespace testing; @@ -1150,5 +1151,188 @@ HWTEST_F(AbilityManagerStubSecondTest, StartAbilityByOEExtInner_001, TestSize.Le TAG_LOGI(AAFwkTag::TEST, "StartAbilityByOEExtInner_001 end"); } + +/** + * @tc.name: ExecuteSkillDoneWithTokenInner_001 + * @tc.desc: Test ExecuteSkillDoneWithTokenInner with null token + * @tc.type: FUNC + */ +HWTEST_F(AbilityManagerStubSecondTest, ExecuteSkillDoneWithTokenInner_001, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "ExecuteSkillDoneWithTokenInner_001 begin"); + + MessageParcel data; + WriteInterfaceToken(data); + + data.WriteRemoteObject(nullptr); + + MessageParcel reply; + MessageOption option; + + auto ret = stub_->OnRemoteRequest( + static_cast(AbilityManagerInterfaceCode::EXECUTE_SKILL_DONE_WITH_TOKEN), data, reply, option); + EXPECT_EQ(ret, ERR_INVALID_VALUE); + + TAG_LOGI(AAFwkTag::TEST, "ExecuteSkillDoneWithTokenInner_001 end"); +} + +/** + * @tc.name: QuerySkillTypeInner_001 + * @tc.desc: Test QuerySkillTypeInner with normal parameters + * @tc.type: FUNC + */ +HWTEST_F(AbilityManagerStubSecondTest, QuerySkillTypeInner_001, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "QuerySkillTypeInner_001 begin"); + + MessageParcel data; + WriteInterfaceToken(data); + + data.WriteString16(Str8ToStr16("bundleName")); + data.WriteString16(Str8ToStr16("moduleName")); + data.WriteString16(Str8ToStr16("skillName")); + + MessageParcel reply; + MessageOption option; + + EXPECT_CALL(*stub_, QuerySkillType(_, _, _, _)) + .Times(1) + .WillOnce(Return(ERR_OK)); + + auto ret = stub_->OnRemoteRequest( + static_cast(AbilityManagerInterfaceCode::QUERY_SKILL_TYPE), data, reply, option); + EXPECT_EQ(ret, NO_ERROR); + EXPECT_EQ(reply.ReadInt32(), ERR_OK); + + TAG_LOGI(AAFwkTag::TEST, "QuerySkillTypeInner_001 end"); +} + +/** + * @tc.name: StartSelfUIAbilityInChildProcessInner_0100 + * @tc.desc: Test StartSelfUIAbilityInChildProcessInner with valid parameters + * @tc.type: FUNC + */ +HWTEST_F(AbilityManagerStubSecondTest, StartSelfUIAbilityInChildProcessInner_0100, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "StartSelfUIAbilityInChildProcessInner_0100 begin"); + + MessageParcel data; + WriteInterfaceToken(data); + + Want want; + want.SetElementName("com.test.bundle", "com.test.TestAbility", "test"); + data.WriteParcelable(&want); + + data.WriteString("testFlag"); + + data.WriteBool(true); + auto token = sptr(new AbilityScheduler()); + data.WriteRemoteObject(token); + + MessageParcel reply; + MessageOption option; + + EXPECT_CALL(*stub_, StartSelfUIAbilityInChildProcess(_, _, _)) + .Times(1) + .WillOnce(Return(ERR_OK)); + + auto ret = stub_->OnRemoteRequest( + static_cast(AbilityManagerInterfaceCode::START_SELF_UI_ABILITY_IN_CHILD_PROCESS), data, reply, + option); + EXPECT_EQ(ret, NO_ERROR); + EXPECT_EQ(reply.ReadInt32(), ERR_OK); + + TAG_LOGI(AAFwkTag::TEST, "StartSelfUIAbilityInChildProcessInner_0100 end"); +} + +/** + * @tc.name: StartSelfUIAbilityInChildProcessInner_0200 + * @tc.desc: Test StartSelfUIAbilityInChildProcessInner with invalid interface token + * @tc.type: FUNC + */ +HWTEST_F(AbilityManagerStubSecondTest, StartSelfUIAbilityInChildProcessInner_0200, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "StartSelfUIAbilityInChildProcessInner_0200 begin"); + + MessageParcel data; + // Not writing interface token + + Want want; + want.SetElementName("com.test.bundle", "com.test.TestAbility", "test"); + data.WriteParcelable(&want); + + data.WriteString("testFlag"); + + MessageParcel reply; + MessageOption option; + + auto ret = stub_->OnRemoteRequest( + static_cast(AbilityManagerInterfaceCode::START_SELF_UI_ABILITY_IN_CHILD_PROCESS), data, reply, + option); + EXPECT_EQ(ret, ERR_INVALID_STATE); + + TAG_LOGI(AAFwkTag::TEST, "StartSelfUIAbilityInChildProcessInner_0200 end"); +} + +/** + * @tc.name: StartSelfUIAbilityInChildProcessInner_0300 + * @tc.desc: Test StartSelfUIAbilityInChildProcessInner with null want + * @tc.type: FUNC + */ +HWTEST_F(AbilityManagerStubSecondTest, StartSelfUIAbilityInChildProcessInner_0300, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "StartSelfUIAbilityInChildProcessInner_0300 begin"); + + MessageParcel data; + WriteInterfaceToken(data); + + // Not writing want + + MessageParcel reply; + MessageOption option; + + auto ret = stub_->OnRemoteRequest( + static_cast(AbilityManagerInterfaceCode::START_SELF_UI_ABILITY_IN_CHILD_PROCESS), data, reply, + option); + EXPECT_EQ(ret, ERR_INVALID_VALUE); + + TAG_LOGI(AAFwkTag::TEST, "StartSelfUIAbilityInChildProcessInner_0300 end"); +} + +/** + * @tc.name: StartSelfUIAbilityInChildProcessInner_0400 + * @tc.desc: Test StartSelfUIAbilityInChildProcessInner without callerToken + * @tc.type: FUNC + */ +HWTEST_F(AbilityManagerStubSecondTest, StartSelfUIAbilityInChildProcessInner_0400, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "StartSelfUIAbilityInChildProcessInner_0400 begin"); + + MessageParcel data; + WriteInterfaceToken(data); + + Want want; + want.SetElementName("com.test.bundle", "com.test.TestAbility", "test"); + data.WriteParcelable(&want); + + data.WriteString("testFlag"); + + data.WriteBool(false); // No callerToken + + MessageParcel reply; + MessageOption option; + + EXPECT_CALL(*stub_, StartSelfUIAbilityInChildProcess(_, _, _)) + .Times(1) + .WillOnce(Return(ERR_OK)); + + auto ret = stub_->OnRemoteRequest( + static_cast(AbilityManagerInterfaceCode::START_SELF_UI_ABILITY_IN_CHILD_PROCESS), data, reply, + option); + EXPECT_EQ(ret, NO_ERROR); + EXPECT_EQ(reply.ReadInt32(), ERR_OK); + + TAG_LOGI(AAFwkTag::TEST, "StartSelfUIAbilityInChildProcessInner_0400 end"); +} } // namespace AAFwk } // namespace OHOS diff --git a/test/unittest/ability_manager_stub_test/ability_manager_stub_impl_mock.h b/test/unittest/ability_manager_stub_test/ability_manager_stub_impl_mock.h index c7ccbf8da2..4857d11c75 100644 --- a/test/unittest/ability_manager_stub_test/ability_manager_stub_impl_mock.h +++ b/test/unittest/ability_manager_stub_test/ability_manager_stub_impl_mock.h @@ -70,8 +70,8 @@ public: MOCK_METHOD1(GetAbilityRunningInfos, int(std::vector& info)); MOCK_METHOD2(GetExtensionRunningInfos, int(int upperLimit, std::vector& info)); MOCK_METHOD1(GetProcessRunningInfos, int(std::vector& info)); - MOCK_METHOD7(StartAbilityByCall, - int(const Want&, const sptr&, const sptr&, int32_t, bool, bool, bool)); + MOCK_METHOD8(StartAbilityByCall, int(const Want&, const sptr&, + const sptr&, int32_t, bool, bool, bool, uint64_t)); MOCK_METHOD2(AcquireShareData, int32_t(const int32_t &missionId, const sptr &shareData)); MOCK_METHOD4(ShareDataDone, int32_t(const sptr &token, const int32_t &resultCode, const int32_t &uniqueId, WantParams &wantParam)); @@ -483,6 +483,8 @@ public: int32_t userId)); MOCK_METHOD1(GetAutoStartupStatusForSelf, int32_t(bool &isAutoStartEnabled)); MOCK_METHOD4(StartAbilityByOEExt, int32_t(const Want&, sptr, int32_t, const std::string&)); + MOCK_METHOD1(StartSelf, int(sptr token)); + MOCK_METHOD3(StartSelfUIAbilityInChildProcess, ErrCode(const Want&, const std::string&, sptr)); int32_t GetUserLockedBundleList(int32_t userId, std::unordered_set &userLockedBundleList) override { @@ -490,6 +492,16 @@ int32_t GetUserLockedBundleList(int32_t userId, std::unordered_set userLockedBundleList.insert("com.ohos.test"); return ERR_OK; } + + MOCK_METHOD7(ExecuteInAppSkill, int32_t(const std::string &, const std::string &, + const std::string &, const std::string &, const std::string &, + const std::shared_ptr &, const sptr &)); + MOCK_METHOD2(ExecuteInAppSkillWithTokenId, int32_t(const AppExecFwk::SkillExecuteRequest &, + const sptr &)); + MOCK_METHOD4(ExecuteSkillDone, int32_t(const sptr &, const std::string &, + int32_t, const AppExecFwk::SkillExecuteResult &)); + MOCK_METHOD4(QuerySkillType, int32_t(const std::string &, const std::string &, + const std::string &, int32_t &)); }; } // namespace AAFwk } // namespace OHOS diff --git a/test/unittest/ability_manager_stub_test/ability_manager_stub_test.cpp b/test/unittest/ability_manager_stub_test/ability_manager_stub_test.cpp index 2b66e81931..6816678fe8 100644 --- a/test/unittest/ability_manager_stub_test/ability_manager_stub_test.cpp +++ b/test/unittest/ability_manager_stub_test/ability_manager_stub_test.cpp @@ -5380,5 +5380,130 @@ HWTEST_F(AbilityManagerStubTest, AbilityManagerStub_StartSelfUIAbilityWithStartO auto result = stub_->StartSelfUIAbilityWithStartOptionsAndTokenInner(data, reply); EXPECT_EQ(result, ERR_READ_START_OPTIONS); } + +/* + * Feature: AbilityManagerService + * Function: OnRemoteRequest + * SubFunction: NA + * FunctionPoints: AbilityManagerStub StartSelfUIAbilityByAppContextInner + * EnvConditions: NA + * CaseDescription: Verify the normal behavior of StartSelfUIAbilityByAppContextInner + */ +HWTEST_F(AbilityManagerStubTest, AbilityManagerStub_StartSelfUIAbilityByAppContextInner_0100, TestSize.Level1) +{ + MessageParcel data; + MessageParcel reply; + ASSERT_TRUE(data.WriteInterfaceToken(AbilityManagerStub::GetDescriptor())); + Want want; + ASSERT_TRUE(data.WriteParcelable(&want)); + auto result = stub_->StartSelfUIAbilityByAppContextInner(data, reply); + EXPECT_EQ(result, NO_ERROR); +} + +/* + * Feature: AbilityManagerService + * Function: OnRemoteRequest + * SubFunction: NA + * FunctionPoints: AbilityManagerStub StartSelfUIAbilityByAppContextInner + * EnvConditions: NA + * CaseDescription: Verify StartSelfUIAbilityByAppContextInner with null want + */ +HWTEST_F(AbilityManagerStubTest, AbilityManagerStub_StartSelfUIAbilityByAppContextInner_0200, TestSize.Level1) +{ + MessageParcel data; + MessageParcel reply; + auto res = stub_->StartSelfUIAbilityByAppContextInner(data, reply); + EXPECT_EQ(res, ERR_INVALID_VALUE); +} + +/* + * Feature: AbilityManagerService + * Function: OnRemoteRequest + * SubFunction: NA + * FunctionPoints: AbilityManagerStub StartSelfUIAbilityByAppContextInner via OnRemoteRequest + * EnvConditions: NA + * CaseDescription: Verify dispatching START_SELF_UI_ABILITY_BY_APP_CONTEXT through OnRemoteRequest + */ +HWTEST_F(AbilityManagerStubTest, AbilityManagerStub_StartSelfUIAbilityByAppContext_OnRemote_0100, TestSize.Level1) +{ + MessageParcel data; + MessageParcel reply; + MessageOption option; + ASSERT_TRUE(data.WriteInterfaceToken(AbilityManagerStub::GetDescriptor())); + Want want; + ASSERT_TRUE(data.WriteParcelable(&want)); + auto result = stub_->OnRemoteRequest( + static_cast(AbilityManagerInterfaceCode::START_SELF_UI_ABILITY_BY_APP_CONTEXT), + data, reply, option); + EXPECT_EQ(result, NO_ERROR); +} + +/* + * Feature: AbilityManagerService + * Function: StartSelfInner + * SubFunction: NA + * FunctionPoints: AbilityManagerStub StartSelfInner + * EnvConditions: NA + * CaseDescription: Verify StartSelfInner with valid token returns NO_ERROR and reply contains result + */ +HWTEST_F(AbilityManagerStubTest, AbilityManagerStub_StartSelfInner_0100, TestSize.Level1) +{ + MessageParcel data; + MessageParcel reply; + data.WriteInterfaceToken(AbilityManagerStub::GetDescriptor()); + auto token = sptr::MakeSptr(); + data.WriteRemoteObject(token); + + EXPECT_CALL(*stub_, StartSelf(_)).WillOnce(Return(ERR_OK)); + auto result = stub_->StartSelfInner(data, reply); + EXPECT_EQ(result, NO_ERROR); + EXPECT_EQ(reply.ReadInt32(), ERR_OK); +} + +/* + * Feature: AbilityManagerService + * Function: StartSelfInner + * SubFunction: NA + * FunctionPoints: AbilityManagerStub StartSelfInner + * EnvConditions: NA + * CaseDescription: Verify StartSelfInner with null token + */ +HWTEST_F(AbilityManagerStubTest, AbilityManagerStub_StartSelfInner_0200, TestSize.Level1) +{ + MessageParcel data; + MessageParcel reply; + data.WriteInterfaceToken(AbilityManagerStub::GetDescriptor()); + data.WriteRemoteObject(nullptr); + + EXPECT_CALL(*stub_, StartSelf(_)).WillOnce(Return(ERR_INVALID_VALUE)); + auto result = stub_->StartSelfInner(data, reply); + EXPECT_EQ(result, NO_ERROR); + EXPECT_EQ(reply.ReadInt32(), ERR_INVALID_VALUE); +} + +/* + * Feature: AbilityManagerService + * Function: OnRemoteRequest + * SubFunction: NA + * FunctionPoints: AbilityManagerStub StartSelfInner via OnRemoteRequest + * EnvConditions: code is START_SELF + * CaseDescription: Verify dispatching START_SELF through OnRemoteRequest + */ +HWTEST_F(AbilityManagerStubTest, AbilityManagerStub_StartSelf_OnRemote_0100, TestSize.Level1) +{ + MessageParcel data; + MessageParcel reply; + MessageOption option; + + WriteInterfaceToken(data); + auto token = sptr::MakeSptr(); + data.WriteRemoteObject(token); + + EXPECT_CALL(*stub_, StartSelf(_)).WillOnce(Return(ERR_OK)); + int res = stub_->OnRemoteRequest( + static_cast(AbilityManagerInterfaceCode::START_SELF), data, reply, option); + EXPECT_EQ(res, NO_ERROR); + EXPECT_EQ(reply.ReadInt32(), ERR_OK); +} } // namespace AAFwk } // namespace OHOS diff --git a/test/unittest/ability_manager_test/ability_manager_stub_mock.h b/test/unittest/ability_manager_test/ability_manager_stub_mock.h index 5bc2b00ec5..c0f75540c1 100644 --- a/test/unittest/ability_manager_test/ability_manager_stub_mock.h +++ b/test/unittest/ability_manager_test/ability_manager_stub_mock.h @@ -360,7 +360,7 @@ public: int StartAbilityByCall(const Want& want, const sptr& connect, const sptr& callerToken, int32_t accountId = DEFAULT_INVAL_VALUE, bool isSilent = false, - bool promotePriority = false, bool isVisible = false) override + bool promotePriority = false, bool isVisible = false, uint64_t specifiedFullTokenId = 0) override { return 0; } diff --git a/test/unittest/ability_permission_util_test/BUILD.gn b/test/unittest/ability_permission_util_test/BUILD.gn index 4e237bb761..f3e539608b 100644 --- a/test/unittest/ability_permission_util_test/BUILD.gn +++ b/test/unittest/ability_permission_util_test/BUILD.gn @@ -49,6 +49,7 @@ ohos_unittest("ability_permission_util_test") { "mock/src/mock_app_mgr_impl.cpp", "mock/src/mock_my_flag.cpp", "mock/src/permission_verification.cpp", + "mock/src/user_controller.cpp", ] deps = [ diff --git a/test/unittest/ability_permission_util_test/ability_permission_util_test.cpp b/test/unittest/ability_permission_util_test/ability_permission_util_test.cpp index c06975e808..0cffe12348 100644 --- a/test/unittest/ability_permission_util_test/ability_permission_util_test.cpp +++ b/test/unittest/ability_permission_util_test/ability_permission_util_test.cpp @@ -37,6 +37,7 @@ #include "parameters.h" #include "running_process_info.h" #include "start_ability_utils.h" +#include "user_controller.h" using namespace testing; using namespace testing::ext; @@ -106,7 +107,12 @@ void AbilityPermissionUtilTest::SetUpTestCase(void) } void AbilityPermissionUtilTest::TearDownTestCase(void) {} -void AbilityPermissionUtilTest::SetUp() {} +void AbilityPermissionUtilTest::SetUp() +{ + // Reset UserController mock status before each test + AbilityRuntime::UserController::GetInstance().SetMockUserLockStatus( + AbilityRuntime::UserController::UserLockStatus::USER_LOCK_STATUS_BUTT); +} void AbilityPermissionUtilTest::TearDown() {} /** @@ -1427,5 +1433,79 @@ HWTEST_F(AbilityPermissionUtilTest, NeedCheckStatusBar_0900, TestSize.Level1) EXPECT_EQ(ret, false); TAG_LOGI(AAFwkTag::TEST, "AbilityPermissionUtil NeedCheckStatusBar_0900 end"); } + +/** + * @tc.name: CheckStartUIAbilityByUserLockStatus_0100 + * @tc.desc: bundleName is empty, should return true + * @tc.type: FUNC + * @tc.require: NA + */ +HWTEST_F(AbilityPermissionUtilTest, CheckStartUIAbilityByUserLockStatus_0100, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "CheckStartUIAbilityByUserLockStatus_0100 start"); + std::string emptyBundleName = ""; + auto ret = AbilityPermissionUtil::GetInstance().CheckStartUIAbilityByUserLockStatus(emptyBundleName); + EXPECT_EQ(ret, true); + TAG_LOGI(AAFwkTag::TEST, "CheckStartUIAbilityByUserLockStatus_0100 end"); +} + +/** + * @tc.name: CheckStartUIAbilityByUserLockStatus_0200 + * @tc.desc: user unlocked, should return true for all apps + * @tc.type: FUNC + * @tc.require: NA + */ +HWTEST_F(AbilityPermissionUtilTest, CheckStartUIAbilityByUserLockStatus_0200, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "CheckStartUIAbilityByUserLockStatus_0200 start"); + + AbilityRuntime::UserController::GetInstance().SetMockUserLockStatus( + AbilityRuntime::UserController::UserLockStatus::USER_UNLOCKED); + std::string bundleName = "com.example.test"; + auto ret = AbilityPermissionUtil::GetInstance().CheckStartUIAbilityByUserLockStatus(bundleName); + EXPECT_EQ(ret, true); + TAG_LOGI(AAFwkTag::TEST, "CheckStartUIAbilityByUserLockStatus_0200 end"); +} + +/** + * @tc.name: CheckStartUIAbilityByUserLockStatus_0300 + * @tc.desc: user locked + system app, should return true + * @tc.type: FUNC + * @tc.require: NA + */ +HWTEST_F(AbilityPermissionUtilTest, CheckStartUIAbilityByUserLockStatus_0300, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "CheckStartUIAbilityByUserLockStatus_0300 start"); + AbilityRuntime::UserController::GetInstance().SetMockUserLockStatus( + AbilityRuntime::UserController::UserLockStatus::USER_LOCKED); + BundleMgrHelper::retGetBundleInfo = true; + BundleMgrHelper::retBundleInfo.applicationInfo.isSystemApp = true; + BundleMgrHelper::retBundleInfo.applicationInfo.name = "systemApp"; + BundleMgrHelper::retBundleInfo.applicationInfo.bundleName = "com.example.system"; + std::string bundleName = "com.example.system"; + auto ret = AbilityPermissionUtil::GetInstance().CheckStartUIAbilityByUserLockStatus(bundleName); + EXPECT_EQ(ret, true); + BundleMgrHelper::retBundleInfo.applicationInfo.isSystemApp = false; + TAG_LOGI(AAFwkTag::TEST, "CheckStartUIAbilityByUserLockStatus_0300 end"); +} + +/** + * @tc.name: CheckStartUIAbilityByUserLockStatus_0400 + * @tc.desc: user locked + bms is null, should return true (fail open) + * @tc.type: FUNC + * @tc.require: NA + */ +HWTEST_F(AbilityPermissionUtilTest, CheckStartUIAbilityByUserLockStatus_0400, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "CheckStartUIAbilityByUserLockStatus_0400 start"); + AbilityRuntime::UserController::GetInstance().SetMockUserLockStatus( + AbilityRuntime::UserController::UserLockStatus::USER_LOCKED); + BundleMgrHelper::isNullBundleMgrInstance = true; + std::string bundleName = "com.example.test"; + auto ret = AbilityPermissionUtil::GetInstance().CheckStartUIAbilityByUserLockStatus(bundleName); + EXPECT_EQ(ret, true); + BundleMgrHelper::isNullBundleMgrInstance = false; + TAG_LOGI(AAFwkTag::TEST, "CheckStartUIAbilityByUserLockStatus_0400 end"); +} } // namespace AAFwk } // namespace OHOS diff --git a/test/unittest/ability_permission_util_test/mock/include/bundle_mgr_helper.h b/test/unittest/ability_permission_util_test/mock/include/bundle_mgr_helper.h index 3917f1dd97..c2e9e57c48 100644 --- a/test/unittest/ability_permission_util_test/mock/include/bundle_mgr_helper.h +++ b/test/unittest/ability_permission_util_test/mock/include/bundle_mgr_helper.h @@ -54,6 +54,12 @@ public: bundleInfo = retBundleInfo; return retGetBundleInfo; } + + bool GetBundleInfo(const std::string &bundleName, int32_t flags, BundleInfo &bundleInfo, int32_t userId) + { + bundleInfo = retBundleInfo; + return retGetBundleInfo; + } }; } // namespace AppExecFwk } // namespace OHOS diff --git a/test/unittest/ability_permission_util_test/mock/include/user_controller.h b/test/unittest/ability_permission_util_test/mock/include/user_controller.h new file mode 100644 index 0000000000..3123a68ce3 --- /dev/null +++ b/test/unittest/ability_permission_util_test/mock/include/user_controller.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2025 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_USER_CONTROLLER_H +#define OHOS_ABILITY_RUNTIME_USER_CONTROLLER_H + +#include +#include +#include + +namespace OHOS { +namespace AAFwk { +class IUserCallback; +} +namespace AbilityRuntime { +class UserController final { +public: + enum class UserLockStatus { + USER_LOCKED = 0, + USER_UNLOCKED = 1, + USER_LOCK_STATUS_BUTT + }; + +public: + UserController() = default; + ~UserController() = default; + static UserController& GetInstance(); + + UserLockStatus GetUserLockStatus(int32_t userId); + + // Mock function to set lock status for testing (applies to userId 0 and 100) + void SetMockUserLockStatus(UserLockStatus status); + +private: + UserLockStatus mockUserLockStatus_ = UserLockStatus::USER_LOCK_STATUS_BUTT; +}; +} // namespace AbilityRuntime +} // namespace OHOS +#endif // OHOS_ABILITY_RUNTIME_USER_CONTROLLER_H diff --git a/test/unittest/ability_permission_util_test/mock/src/user_controller.cpp b/test/unittest/ability_permission_util_test/mock/src/user_controller.cpp new file mode 100644 index 0000000000..c5c64d6a7d --- /dev/null +++ b/test/unittest/ability_permission_util_test/mock/src/user_controller.cpp @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2025 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 "user_controller.h" + +namespace OHOS { +namespace AbilityRuntime { +UserController& UserController::GetInstance() +{ + static UserController instance; + return instance; +} + +UserController::UserLockStatus UserController::GetUserLockStatus(int32_t userId) +{ + return mockUserLockStatus_; +} + +void UserController::SetMockUserLockStatus(UserLockStatus status) +{ + mockUserLockStatus_ = status; +} +} // namespace AbilityRuntime +} // namespace OHOS diff --git a/test/unittest/ability_record_test/ability_record_test.cpp b/test/unittest/ability_record_test/ability_record_test.cpp index 23e177aca9..8790363a45 100644 --- a/test/unittest/ability_record_test/ability_record_test.cpp +++ b/test/unittest/ability_record_test/ability_record_test.cpp @@ -1946,22 +1946,6 @@ HWTEST_F(AbilityRecordTest, AaFwk_AbilityMS_ForegroundAbility_005, TestSize.Leve EXPECT_NE(abilityRecord_, nullptr); } -/* - * Feature: AbilityRecord - * Function: ForegroundAbility - * SubFunction: ForegroundAbility - * FunctionPoints: NA - * EnvConditions: NA - * CaseDescription: Verify AbilityRecord ForegroundAbility - */ -HWTEST_F(AbilityRecordTest, AaFwk_AbilityMS_ForegroundAbility_006, TestSize.Level1) -{ - std::shared_ptr abilityRecord = GetAbilityRecord(); - abilityRecord->SetLastWant(std::make_shared()); - abilityRecord->ForegroundAbility(0, true); - EXPECT_TRUE(abilityRecord != nullptr); -} - /* * Feature: AbilityRecord * Function: ForegroundAbility @@ -1973,11 +1957,10 @@ HWTEST_F(AbilityRecordTest, AaFwk_AbilityMS_ForegroundAbility_006, TestSize.Leve HWTEST_F(AbilityRecordTest, AaFwk_AbilityMS_ForegroundAbility_007, TestSize.Level1) { std::shared_ptr abilityRecord = GetAbilityRecord(); - abilityRecord->SetLastWant(std::make_shared()); AppUtils::GetInstance().isStartSpecifiedProcess_.isLoaded = true; AppUtils::GetInstance().isStartSpecifiedProcess_.value = true; abilityRecord->abilityInfo_.applicationInfo.isSystemApp = true; - abilityRecord->ForegroundAbility(0, true); + abilityRecord->ForegroundAbility(0); EXPECT_TRUE(abilityRecord != nullptr); } @@ -1992,11 +1975,10 @@ HWTEST_F(AbilityRecordTest, AaFwk_AbilityMS_ForegroundAbility_007, TestSize.Leve HWTEST_F(AbilityRecordTest, AaFwk_AbilityMS_ForegroundAbility_008, TestSize.Level1) { std::shared_ptr abilityRecord = GetAbilityRecord(); - abilityRecord->SetLastWant(std::make_shared()); AppUtils::GetInstance().isStartSpecifiedProcess_.isLoaded = true; AppUtils::GetInstance().isStartSpecifiedProcess_.value = true; abilityRecord->abilityInfo_.applicationInfo.isSystemApp = false; - abilityRecord->ForegroundAbility(0, true); + abilityRecord->ForegroundAbility(0); EXPECT_TRUE(abilityRecord != nullptr); } @@ -2011,11 +1993,10 @@ HWTEST_F(AbilityRecordTest, AaFwk_AbilityMS_ForegroundAbility_008, TestSize.Leve HWTEST_F(AbilityRecordTest, AaFwk_AbilityMS_ForegroundAbility_009, TestSize.Level1) { std::shared_ptr abilityRecord = GetAbilityRecord(); - abilityRecord->SetLastWant(std::make_shared()); AppUtils::GetInstance().isStartSpecifiedProcess_.isLoaded = true; AppUtils::GetInstance().isStartSpecifiedProcess_.value = false; abilityRecord->abilityInfo_.applicationInfo.isSystemApp = true; - abilityRecord->ForegroundAbility(0, true); + abilityRecord->ForegroundAbility(0); EXPECT_TRUE(abilityRecord != nullptr); } @@ -2030,11 +2011,10 @@ HWTEST_F(AbilityRecordTest, AaFwk_AbilityMS_ForegroundAbility_009, TestSize.Leve HWTEST_F(AbilityRecordTest, AaFwk_AbilityMS_ForegroundAbility_010, TestSize.Level1) { std::shared_ptr abilityRecord = GetAbilityRecord(); - abilityRecord->SetLastWant(std::make_shared()); AppUtils::GetInstance().isStartSpecifiedProcess_.isLoaded = true; AppUtils::GetInstance().isStartSpecifiedProcess_.value = false; abilityRecord->abilityInfo_.applicationInfo.isSystemApp = false; - abilityRecord->ForegroundAbility(0, true); + abilityRecord->ForegroundAbility(0); EXPECT_TRUE(abilityRecord != nullptr); } diff --git a/test/unittest/ability_record_test/ability_record_test_call.cpp b/test/unittest/ability_record_test/ability_record_test_call.cpp index d42a288f08..9d314c8aee 100644 --- a/test/unittest/ability_record_test/ability_record_test_call.cpp +++ b/test/unittest/ability_record_test/ability_record_test_call.cpp @@ -923,5 +923,32 @@ HWTEST_F(AbilityRecordTest, AbilityRecord_PrepareTerminateAbility_002, TestSize. EXPECT_EQ(result, false); EXPECT_NE(abilityRecord_, nullptr); } + +/** + * @tc.name: GetWantParam_0100 + * @tc.desc: Call GetWantParam + * @tc.type: FUNC + */ +HWTEST_F(AbilityRecordTest, GetWantParam_0100, TestSize.Level1) +{ + AbilityRequest abilityRequest; + + abilityRequest.want.SetElementName("", "testBundle", "testAbility", "testModule"); + abilityRequest.want.SetParam("boolKey", true); + std::string testStringValue = "testStringValue"; + abilityRequest.want.SetParam("testStringKey", testStringValue); + auto abilityRecord = AbilityRecord::CreateAbilityRecord(abilityRequest); + + EXPECT_TRUE(abilityRecord->HasParameter("testStringKey")); + EXPECT_TRUE(abilityRecord->GetBoolParam("boolKey", false)); + EXPECT_EQ(abilityRecord->GetStringParam("testStringKey"), testStringValue); + + EXPECT_EQ(abilityRecord->GetBundleName(), "testBundle"); + EXPECT_EQ(abilityRecord->GetAbilityName(), "testAbility"); + EXPECT_EQ(abilityRecord->GetModuleName(), "testModule"); + + abilityRecord->RemoveSpecifiedWantParam("testStringKey"); + EXPECT_FALSE(abilityRecord->HasParameter("testStringKey")); +} } // namespace AAFwk } // namespace OHOS diff --git a/test/unittest/ability_scheduler_proxy_test/ability_scheduler_proxy_test.cpp b/test/unittest/ability_scheduler_proxy_test/ability_scheduler_proxy_test.cpp index 96e4dfe838..6a27a1ca76 100644 --- a/test/unittest/ability_scheduler_proxy_test/ability_scheduler_proxy_test.cpp +++ b/test/unittest/ability_scheduler_proxy_test/ability_scheduler_proxy_test.cpp @@ -539,5 +539,40 @@ HWTEST_F(AbilitySchedulerProxyTest, ScheduleAbilitiesRequestDone_001, TestSize.L abilitySchedulerProxy_->ScheduleAbilitiesRequestDone(requestKey, resultCode); EXPECT_EQ(IAbilityScheduler::SCHEDULE_ABILITIES_REQUEST_DONE, mock_->code_); } + +/* + * Feature: AbilitySchedulerProxy + * Function: ExecuteSkill + * SubFunction: NA + * FunctionPoints: AbilitySchedulerProxy ExecuteSkill + * EnvConditions: NA + * CaseDescription: verify ExecuteSkill with SendRequest success + */ +HWTEST_F(AbilitySchedulerProxyTest, ExecuteSkill_001, TestSize.Level1) +{ + EXPECT_CALL(*mock_, SendRequest(_, _, _, _)) + .Times(1) + .WillOnce(Invoke(mock_.GetRefPtr(), &AbilitySchedulerMock::InvokeSendRequest)); + Want want; + abilitySchedulerProxy_->ExecuteSkill(want); + EXPECT_EQ(IAbilityScheduler::SCHEDULE_EXECUTE_SKILL, mock_->code_); +} + +/* + * Feature: AbilitySchedulerProxy + * Function: ExecuteSkill + * SubFunction: NA + * FunctionPoints: AbilitySchedulerProxy ExecuteSkill + * EnvConditions: NA + * CaseDescription: verify ExecuteSkill with SendRequest failure + */ +HWTEST_F(AbilitySchedulerProxyTest, ExecuteSkill_002, TestSize.Level1) +{ + EXPECT_CALL(*mock_, SendRequest(_, _, _, _)) + .Times(1) + .WillOnce(Return(-1)); + Want want; + abilitySchedulerProxy_->ExecuteSkill(want); +} } // namespace AAFwk } // namespace OHOS diff --git a/test/unittest/ability_scheduler_stub_second_test/ability_scheduler_stub_second_test.cpp b/test/unittest/ability_scheduler_stub_second_test/ability_scheduler_stub_second_test.cpp index af40a03c9f..9fbe2bc873 100644 --- a/test/unittest/ability_scheduler_stub_second_test/ability_scheduler_stub_second_test.cpp +++ b/test/unittest/ability_scheduler_stub_second_test/ability_scheduler_stub_second_test.cpp @@ -874,5 +874,35 @@ HWTEST_F(AbilitySchedulerStubSecondTest, AbilitySchedulerStubSecond_037, TestSiz res = stub_->ReloadInner(data2, reply); EXPECT_EQ(res, NO_ERROR); } + +/** + * @tc.name: AbilitySchedulerStubSecond_038 + * @tc.desc: test ExecuteSkillInner with null want + * @tc.type: FUNC + */ +HWTEST_F(AbilitySchedulerStubSecondTest, AbilitySchedulerStubSecond_038, TestSize.Level1) +{ + MessageParcel data; + MessageParcel reply; + + auto res = stub_->ExecuteSkillInner(data, reply); + EXPECT_EQ(res, ERR_INVALID_VALUE); +} + +/** + * @tc.name: AbilitySchedulerStubSecond_039 + * @tc.desc: test ExecuteSkillInner with valid want + * @tc.type: FUNC + */ +HWTEST_F(AbilitySchedulerStubSecondTest, AbilitySchedulerStubSecond_039, TestSize.Level1) +{ + MessageParcel data; + MessageParcel reply; + + Want want; + data.WriteParcelable(&want); + auto res = stub_->ExecuteSkillInner(data, reply); + EXPECT_EQ(res, NO_ERROR); +} } // namespace AAFwk } // namespace OHOS diff --git a/test/unittest/agent_runtime_framework/agent_card_mgr_test/agent_card_mgr_test.cpp b/test/unittest/agent_runtime_framework/agent_card_mgr_test/agent_card_mgr_test.cpp index 26d39b4e8f..7f171fefbf 100644 --- a/test/unittest/agent_runtime_framework/agent_card_mgr_test/agent_card_mgr_test.cpp +++ b/test/unittest/agent_runtime_framework/agent_card_mgr_test/agent_card_mgr_test.cpp @@ -15,6 +15,7 @@ #include #include +#include #define private public #define protected public @@ -104,6 +105,7 @@ void AgentCardMgrTest::SetUp(void) MyFlag::queryDataCards.clear(); MyFlag::queryAllDataEntries.clear(); MyFlag::queryAllDataCards.clear(); + MyFlag::syncQueryDataWithInsert = false; MyFlag::retGetBundleInfo = true; MyFlag::retGetResConfigFile = true; MyFlag::retFromJson = true; @@ -1129,6 +1131,86 @@ HWTEST_F(AgentCardMgrTest, RegisterAgentCard_010, TestSize.Level1) EXPECT_EQ(agentCardMgr.RegisterAgentCard(card), AAFwk::ERR_NOT_SYSTEM_APP); } +/** + * @tc.name: RegisterAgentCard_011 + * @tc.desc: RegisterAgentCard persists concurrent registrations with distinct agentIds + * @tc.type: FUNC + */ +HWTEST_F(AgentCardMgrTest, RegisterAgentCard_011, TestSize.Level1) +{ + AgentCardMgr agentCardMgr; + MyFlag::mockExtensionInfos.push_back(BuildAgentExtensionInfo()); + MyFlag::retQueryData = ERR_NAME_NOT_FOUND; + MyFlag::syncQueryDataWithInsert = true; + + constexpr int32_t cardCount = 10; + std::vector threads; + std::vector results(cardCount, ERR_INVALID_VALUE); + threads.reserve(cardCount); + for (int32_t i = 0; i < cardCount; ++i) { + threads.emplace_back([&agentCardMgr, &results, i]() { + AgentCard card = BuildCard("testAgent" + std::to_string(i), "1.0.0"); + results[i] = agentCardMgr.RegisterAgentCard(card); + }); + } + for (auto &thread : threads) { + thread.join(); + } + + for (int32_t result : results) { + EXPECT_EQ(result, ERR_OK); + } + ASSERT_EQ(MyFlag::insertedCards.size(), static_cast(cardCount)); + for (int32_t i = 0; i < cardCount; ++i) { + auto it = std::find_if(MyFlag::insertedCards.begin(), MyFlag::insertedCards.end(), + [i](const AgentCard &card) { return card.agentId == "testAgent" + std::to_string(i); }); + EXPECT_NE(it, MyFlag::insertedCards.end()); + } +} + +/** + * @tc.name: RegisterAgentCard_012 + * @tc.desc: RegisterAgentCard rejects new cards when the bundle has reached the max card count + * @tc.type: FUNC + */ +HWTEST_F(AgentCardMgrTest, RegisterAgentCard_012, TestSize.Level1) +{ + AgentCardMgr agentCardMgr; + MyFlag::mockExtensionInfos.push_back(BuildAgentExtensionInfo()); + MyFlag::retQueryData = ERR_OK; + constexpr int32_t maxAgentCardSize = 1000; + MyFlag::queryDataCards.reserve(maxAgentCardSize); + for (int32_t i = 0; i < maxAgentCardSize; ++i) { + MyFlag::queryDataCards.emplace_back(BuildCard("storedAgent" + std::to_string(i), "1.0.0")); + } + + AgentCard card = BuildCard("newAgent", "1.0.0"); + EXPECT_EQ(agentCardMgr.RegisterAgentCard(card), AAFwk::ERR_AGENT_CARD_LIST_OUT_OF_RANGE); + EXPECT_TRUE(MyFlag::insertedCards.empty()); +} + +/** + * @tc.name: RegisterAgentCard_013 + * @tc.desc: RegisterAgentCard allows adding the card that reaches the max card count + * @tc.type: FUNC + */ +HWTEST_F(AgentCardMgrTest, RegisterAgentCard_013, TestSize.Level1) +{ + AgentCardMgr agentCardMgr; + MyFlag::mockExtensionInfos.push_back(BuildAgentExtensionInfo()); + MyFlag::retQueryData = ERR_OK; + constexpr int32_t maxAgentCardSize = 1000; + MyFlag::queryDataCards.reserve(maxAgentCardSize - 1); + for (int32_t i = 0; i < maxAgentCardSize - 1; ++i) { + MyFlag::queryDataCards.emplace_back(BuildCard("storedAgent" + std::to_string(i), "1.0.0")); + } + + AgentCard card = BuildCard("newAgent", "1.0.0"); + EXPECT_EQ(agentCardMgr.RegisterAgentCard(card), ERR_OK); + ASSERT_EQ(MyFlag::insertedCards.size(), static_cast(maxAgentCardSize)); + EXPECT_EQ(MyFlag::insertedCards.back().agentId, "newAgent"); +} + /** * @tc.name: UpdateAgentCard_002 * @tc.desc: UpdateAgentCard returns ERR_INVALID_AGENT_CARD_VERSION when semver is invalid diff --git a/test/unittest/agent_runtime_framework/agent_card_mgr_test/mock/include/mock_my_flag.h b/test/unittest/agent_runtime_framework/agent_card_mgr_test/mock/include/mock_my_flag.h index e5166736a2..149e7a7b3e 100644 --- a/test/unittest/agent_runtime_framework/agent_card_mgr_test/mock/include/mock_my_flag.h +++ b/test/unittest/agent_runtime_framework/agent_card_mgr_test/mock/include/mock_my_flag.h @@ -20,6 +20,7 @@ #include "agent_card.h" #include "extension_ability_info.h" #include "hap_module_info.h" +#include #include #include @@ -37,6 +38,8 @@ public: static std::vector queryDataCards; static std::vector queryAllDataEntries; static std::vector queryAllDataCards; + static bool syncQueryDataWithInsert; + static std::mutex dbMutex; static bool retGetBundleInfo; static bool retGetResConfigFile; static bool retFromJson; diff --git a/test/unittest/agent_runtime_framework/agent_card_mgr_test/mock/src/mock_agent_card_db_mgr.cpp b/test/unittest/agent_runtime_framework/agent_card_mgr_test/mock/src/mock_agent_card_db_mgr.cpp index ca39a7f363..50b2d47cf7 100644 --- a/test/unittest/agent_runtime_framework/agent_card_mgr_test/mock/src/mock_agent_card_db_mgr.cpp +++ b/test/unittest/agent_runtime_framework/agent_card_mgr_test/mock/src/mock_agent_card_db_mgr.cpp @@ -27,6 +27,8 @@ std::vector AgentRuntime::MyFlag::queryDataE std::vector AgentRuntime::MyFlag::queryDataCards; std::vector AgentRuntime::MyFlag::queryAllDataEntries; std::vector AgentRuntime::MyFlag::queryAllDataCards; +bool AgentRuntime::MyFlag::syncQueryDataWithInsert = false; +std::mutex AgentRuntime::MyFlag::dbMutex; namespace AgentRuntime { AgentCardDbMgr &AgentCardDbMgr::GetInstance() @@ -44,11 +46,17 @@ AgentCardDbMgr::~AgentCardDbMgr() int32_t AgentCardDbMgr::InsertData(const std::string &bundleName, int32_t userId, const std::vector &cards) { + std::lock_guard lock(MyFlag::dbMutex); MyFlag::insertedEntries = cards; MyFlag::insertedCards.clear(); for (const auto &entry : cards) { MyFlag::insertedCards.emplace_back(entry.card); } + if (MyFlag::syncQueryDataWithInsert) { + MyFlag::queryDataEntries = cards; + MyFlag::queryDataCards = MyFlag::insertedCards; + MyFlag::retQueryData = ERR_OK; + } return MyFlag::retInsertData; } @@ -60,6 +68,7 @@ int32_t AgentCardDbMgr::DeleteData(const std::string &bundleName, int32_t userId int32_t AgentCardDbMgr::QueryData(const std::string &bundleName, int32_t userId, std::vector &cards) { + std::lock_guard lock(MyFlag::dbMutex); if (!MyFlag::queryDataEntries.empty()) { cards = MyFlag::queryDataEntries; return MyFlag::retQueryData; diff --git a/test/unittest/agent_runtime_framework/agent_card_test/agent_card_test.cpp b/test/unittest/agent_runtime_framework/agent_card_test/agent_card_test.cpp index 3b39c3fe59..df6835fa6a 100644 --- a/test/unittest/agent_runtime_framework/agent_card_test/agent_card_test.cpp +++ b/test/unittest/agent_runtime_framework/agent_card_test/agent_card_test.cpp @@ -52,6 +52,33 @@ void AgentCardTest::SetUp(void) void AgentCardTest::TearDown(void) {} +namespace { +nlohmann::json BuildValidSkillJson(const std::string &id = "test") +{ + return nlohmann::json { + { "id", id }, + { "name", "test" }, + { "description", "test" }, + { "tags", nlohmann::json::array({ "test" }) }, + }; +} + +nlohmann::json BuildValidAgentCardJson() +{ + return nlohmann::json { + { "agentId", "1" }, + { "name", "test" }, + { "description", "test description" }, + { "version", "1.0" }, + { "category", "productivity" }, + { "defaultInputModes", nlohmann::json::array({ "text" }) }, + { "defaultOutputModes", nlohmann::json::array({ "text" }) }, + { "skills", nlohmann::json::array({ BuildValidSkillJson() }) }, + { "iconUrl", "http://example.com/icon.png" }, + }; +} +} + /** * @tc.name: ProviderMarshallingTest_001 * @tc.desc: Test AgentProvider Marshalling method with valid data @@ -3939,5 +3966,800 @@ HWTEST_F(AgentCardTest, CapabilitiesFromJson_010, TestSize.Level1) AgentCapabilities capabilities = AgentCapabilities::FromJson(jsonObject); EXPECT_TRUE(capabilities.extension.empty()); } + +/** + * @tc.name: CapabilitiesFromJson_011 + * @tc.desc: Test Capabilities FromJson ignores wrong types and clears invalid extension json + * @tc.type: FUNC + */ +HWTEST_F(AgentCardTest, CapabilitiesFromJson_011, TestSize.Level1) +{ + nlohmann::json jsonObject = nlohmann::json { + { "streaming", "true" }, + { "pushNotifications", 1 }, + { "stateTransitionHistory", nullptr }, + { "extendedAgentCard", "false" }, + { "extension", "{invalid json" }, + }; + + AgentCapabilities capabilities = AgentCapabilities::FromJson(jsonObject); + + EXPECT_FALSE(capabilities.streaming); + EXPECT_FALSE(capabilities.pushNotifications); + EXPECT_FALSE(capabilities.stateTransitionHistory); + EXPECT_FALSE(capabilities.extendedAgentCard); + EXPECT_TRUE(capabilities.extension.empty()); +} + +/** + * @tc.name: AgentCardFromJson_050 + * @tc.desc: Test FromJson rejects invalid type value kinds + * @tc.type: FUNC + */ +HWTEST_F(AgentCardTest, AgentCardFromJson_050, TestSize.Level1) +{ + auto jsonObject = BuildValidAgentCardJson(); + jsonObject["type"] = true; + + AgentCard agentCard; + EXPECT_FALSE(AgentCard::FromJson(jsonObject, agentCard)); +} + +/** + * @tc.name: AgentCardFromJson_051 + * @tc.desc: Test FromJson rejects missing and invalid default mode arrays + * @tc.type: FUNC + */ +HWTEST_F(AgentCardTest, AgentCardFromJson_051, TestSize.Level1) +{ + auto missingInputModes = BuildValidAgentCardJson(); + missingInputModes.erase("defaultInputModes"); + AgentCard agentCard; + EXPECT_FALSE(AgentCard::FromJson(missingInputModes, agentCard)); + + auto invalidInputModes = BuildValidAgentCardJson(); + invalidInputModes["defaultInputModes"] = nlohmann::json::array({ "", 1, std::string(33, 'a') }); + EXPECT_FALSE(AgentCard::FromJson(invalidInputModes, agentCard)); + + auto missingOutputModes = BuildValidAgentCardJson(); + missingOutputModes.erase("defaultOutputModes"); + EXPECT_FALSE(AgentCard::FromJson(missingOutputModes, agentCard)); + + auto invalidOutputModes = BuildValidAgentCardJson(); + invalidOutputModes["defaultOutputModes"] = nlohmann::json::array({ "", false, std::string(33, 'b') }); + EXPECT_FALSE(AgentCard::FromJson(invalidOutputModes, agentCard)); +} + +/** + * @tc.name: AgentCardFromJson_052 + * @tc.desc: Test FromJson skips invalid optional provider and skill entries + * @tc.type: FUNC + */ +HWTEST_F(AgentCardTest, AgentCardFromJson_052, TestSize.Level1) +{ + auto jsonObject = BuildValidAgentCardJson(); + jsonObject["provider"] = nlohmann::json { + { "organization", "" }, + { "url", "http://example.com" }, + }; + jsonObject["skills"] = nlohmann::json::array({ + BuildValidSkillJson("dup"), + nlohmann::json { { "id", "" }, { "name", "bad" }, { "description", "bad" }, + { "tags", nlohmann::json::array({ "bad" }) } }, + nlohmann::json { { "id", "dup" }, { "name", "replacement" }, { "description", "test" }, + { "tags", nlohmann::json::array({ "test" }) } }, + "notObject", + }); + + AgentCard agentCard; + EXPECT_TRUE(AgentCard::FromJson(jsonObject, agentCard)); + EXPECT_EQ(agentCard.provider, nullptr); + ASSERT_EQ(agentCard.skills.size(), 1); + EXPECT_EQ(agentCard.skills[0]->id, "dup"); + EXPECT_EQ(agentCard.skills[0]->name, "replacement"); +} + +/** + * @tc.name: AgentCardFromJson_053 + * @tc.desc: Test FromJson clears invalid card extension json + * @tc.type: FUNC + */ +HWTEST_F(AgentCardTest, AgentCardFromJson_053, TestSize.Level1) +{ + auto jsonObject = BuildValidAgentCardJson(); + jsonObject["extension"] = "{invalid json"; + + AgentCard agentCard; + EXPECT_TRUE(AgentCard::FromJson(jsonObject, agentCard)); + EXPECT_TRUE(agentCard.extension.empty()); +} + +/** + * @tc.name: AgentAppInfoFromJson_007 + * @tc.desc: Test AgentAppInfo FromJson ignores invalid optional value types and device type lengths + * @tc.type: FUNC + */ +HWTEST_F(AgentCardTest, AgentAppInfoFromJson_007, TestSize.Level1) +{ + nlohmann::json jsonObject = nlohmann::json { + { "bundleName", 1 }, + { "moduleName", false }, + { "abilityName", nullptr }, + { "deviceTypes", nlohmann::json::array({ "", "phone", std::string(33, 'd'), 10 }) }, + { "minAppVersion", 2 }, + }; + + AgentAppInfo appInfo; + EXPECT_TRUE(AgentAppInfo::FromJson(jsonObject, appInfo)); + EXPECT_TRUE(appInfo.bundleName.empty()); + EXPECT_TRUE(appInfo.moduleName.empty()); + EXPECT_TRUE(appInfo.abilityName.empty()); + ASSERT_EQ(appInfo.deviceTypes.size(), 1); + EXPECT_EQ(appInfo.deviceTypes[0], "phone"); + EXPECT_TRUE(appInfo.minAppVersion.empty()); +} + +/** + * @tc.name: ToAgentCardVec_005 + * @tc.desc: Test ToAgentCardVec returns error when payload is not valid AgentCard json + * @tc.type: FUNC + */ +HWTEST_F(AgentCardTest, ToAgentCardVec_005, TestSize.Level1) +{ + uint32_t count = 1; + std::string cardJson = "{invalid json"; + uint32_t cardSize = cardJson.size(); + std::string buffer; + buffer.append(reinterpret_cast(&count), sizeof(count)); + buffer.append(reinterpret_cast(&cardSize), sizeof(cardSize)); + buffer.append(cardJson); + + AgentCardsRawData rawData; + rawData.data = buffer.data(); + rawData.size = buffer.size(); + + std::vector cards; + EXPECT_EQ(AgentCardsRawData::ToAgentCardVec(rawData, cards), ERR_INVALID_AGENT_CARD_DATA); +} + +// ==================== AgentProvider::FromJson Branch Tests ==================== + +/** + * @tc.name: ProviderFromJson_001 + * @tc.desc: Test FromJson fails when organization is missing + * @tc.type: FUNC + */ +HWTEST_F(AgentCardTest, ProviderFromJson_011, TestSize.Level1) +{ + nlohmann::json jsonObject = {{"url", "http://example.com"}}; + AgentProvider provider; + EXPECT_FALSE(AgentProvider::FromJson(jsonObject, provider)); +} + +/** + * @tc.name: ProviderFromJson_012 + * @tc.desc: Test FromJson fails when organization is not a string + * @tc.type: FUNC + */ +HWTEST_F(AgentCardTest, ProviderFromJson_012, TestSize.Level1) +{ + nlohmann::json jsonObject = {{"organization", 123}, {"url", "http://example.com"}}; + AgentProvider provider; + EXPECT_FALSE(AgentProvider::FromJson(jsonObject, provider)); +} + +/** + * @tc.name: ProviderFromJson_013 + * @tc.desc: Test FromJson fails when organization is empty + * @tc.type: FUNC + */ +HWTEST_F(AgentCardTest, ProviderFromJson_013, TestSize.Level1) +{ + nlohmann::json jsonObject = {{"organization", ""}, {"url", "http://example.com"}}; + AgentProvider provider; + EXPECT_FALSE(AgentProvider::FromJson(jsonObject, provider)); +} + +/** + * @tc.name: ProviderFromJson_014 + * @tc.desc: Test FromJson fails when organization exceeds 128 chars + * @tc.type: FUNC + */ +HWTEST_F(AgentCardTest, ProviderFromJson_014, TestSize.Level1) +{ + nlohmann::json jsonObject = {{"organization", std::string(129, 'a')}, {"url", "http://example.com"}}; + AgentProvider provider; + EXPECT_FALSE(AgentProvider::FromJson(jsonObject, provider)); +} + +/** + * @tc.name: ProviderFromJson_015 + * @tc.desc: Test FromJson fails when url is missing + * @tc.type: FUNC + */ +HWTEST_F(AgentCardTest, ProviderFromJson_015, TestSize.Level1) +{ + nlohmann::json jsonObject = {{"organization", "org"}}; + AgentProvider provider; + EXPECT_FALSE(AgentProvider::FromJson(jsonObject, provider)); +} + +/** + * @tc.name: ProviderFromJson_016 + * @tc.desc: Test FromJson fails when url is not a string + * @tc.type: FUNC + */ +HWTEST_F(AgentCardTest, ProviderFromJson_016, TestSize.Level1) +{ + nlohmann::json jsonObject = {{"organization", "org"}, {"url", 42}}; + AgentProvider provider; + EXPECT_FALSE(AgentProvider::FromJson(jsonObject, provider)); +} + +/** + * @tc.name: ProviderFromJson_017 + * @tc.desc: Test FromJson fails when url is empty + * @tc.type: FUNC + */ +HWTEST_F(AgentCardTest, ProviderFromJson_017, TestSize.Level1) +{ + nlohmann::json jsonObject = {{"organization", "org"}, {"url", ""}}; + AgentProvider provider; + EXPECT_FALSE(AgentProvider::FromJson(jsonObject, provider)); +} + +/** + * @tc.name: ProviderFromJson_018 + * @tc.desc: Test FromJson fails when url exceeds 512 chars + * @tc.type: FUNC + */ +HWTEST_F(AgentCardTest, ProviderFromJson_018, TestSize.Level1) +{ + nlohmann::json jsonObject = {{"organization", "org"}, {"url", std::string(513, 'u')}}; + AgentProvider provider; + EXPECT_FALSE(AgentProvider::FromJson(jsonObject, provider)); +} + +/** + * @tc.name: ProviderFromJson_019 + * @tc.desc: Test FromJson succeeds with valid provider at boundary lengths + * @tc.type: FUNC + */ +HWTEST_F(AgentCardTest, ProviderFromJson_019, TestSize.Level1) +{ + nlohmann::json json128 = {{"organization", std::string(128, 'a')}, {"url", "http://example.com"}}; + AgentProvider provider; + EXPECT_TRUE(AgentProvider::FromJson(json128, provider)); + EXPECT_EQ(provider.organization.length(), 128u); + + nlohmann::json json512 = {{"organization", "org"}, {"url", std::string(512, 'u')}}; + AgentProvider provider2; + EXPECT_TRUE(AgentProvider::FromJson(json512, provider2)); + EXPECT_EQ(provider2.url.length(), 512u); +} + +// ==================== AgentSkill::FromJson Branch Tests ==================== + +/** + * @tc.name: SkillFromJson_001 + * @tc.desc: Test FromJson fails when id is missing + * @tc.type: FUNC + */ +HWTEST_F(AgentCardTest, SkillFromJson_034, TestSize.Level1) +{ + nlohmann::json jsonObject = {{"name", "n"}, {"description", "d"}, {"tags", {"t"}}}; + AgentSkill skill; + EXPECT_FALSE(AgentSkill::FromJson(jsonObject, skill)); +} + +/** + * @tc.name: SkillFromJson_035 + * @tc.desc: Test FromJson fails when id is not a string + * @tc.type: FUNC + */ +HWTEST_F(AgentCardTest, SkillFromJson_035, TestSize.Level1) +{ + nlohmann::json jsonObject = {{"id", 1}, {"name", "n"}, {"description", "d"}, {"tags", {"t"}}}; + AgentSkill skill; + EXPECT_FALSE(AgentSkill::FromJson(jsonObject, skill)); +} + +/** + * @tc.name: SkillFromJson_036 + * @tc.desc: Test FromJson fails when id is empty or exceeds 64 chars + * @tc.type: FUNC + */ +HWTEST_F(AgentCardTest, SkillFromJson_036, TestSize.Level1) +{ + AgentSkill skill; + auto emptyId = BuildValidSkillJson(); + emptyId["id"] = ""; + EXPECT_FALSE(AgentSkill::FromJson(emptyId, skill)); + + auto longId = BuildValidSkillJson(); + longId["id"] = std::string(65, 'i'); + EXPECT_FALSE(AgentSkill::FromJson(longId, skill)); +} + +/** + * @tc.name: SkillFromJson_037 + * @tc.desc: Test FromJson fails when name is missing or wrong type + * @tc.type: FUNC + */ +HWTEST_F(AgentCardTest, SkillFromJson_037, TestSize.Level1) +{ + AgentSkill skill; + auto missingName = BuildValidSkillJson(); + missingName.erase("name"); + EXPECT_FALSE(AgentSkill::FromJson(missingName, skill)); + + auto wrongName = BuildValidSkillJson(); + wrongName["name"] = 42; + EXPECT_FALSE(AgentSkill::FromJson(wrongName, skill)); +} + +/** + * @tc.name: SkillFromJson_038 + * @tc.desc: Test FromJson fails when name is empty or exceeds 128 chars + * @tc.type: FUNC + */ +HWTEST_F(AgentCardTest, SkillFromJson_038, TestSize.Level1) +{ + AgentSkill skill; + auto emptyName = BuildValidSkillJson(); + emptyName["name"] = ""; + EXPECT_FALSE(AgentSkill::FromJson(emptyName, skill)); + + auto longName = BuildValidSkillJson(); + longName["name"] = std::string(129, 'n'); + EXPECT_FALSE(AgentSkill::FromJson(longName, skill)); +} + +/** + * @tc.name: SkillFromJson_039 + * @tc.desc: Test FromJson fails when description is missing or wrong type + * @tc.type: FUNC + */ +HWTEST_F(AgentCardTest, SkillFromJson_039, TestSize.Level1) +{ + AgentSkill skill; + auto missingDesc = BuildValidSkillJson(); + missingDesc.erase("description"); + EXPECT_FALSE(AgentSkill::FromJson(missingDesc, skill)); + + auto wrongDesc = BuildValidSkillJson(); + wrongDesc["description"] = false; + EXPECT_FALSE(AgentSkill::FromJson(wrongDesc, skill)); +} + +/** + * @tc.name: SkillFromJson_040 + * @tc.desc: Test FromJson fails when description exceeds 512 chars + * @tc.type: FUNC + */ +HWTEST_F(AgentCardTest, SkillFromJson_040, TestSize.Level1) +{ + AgentSkill skill; + auto longDesc = BuildValidSkillJson(); + longDesc["description"] = std::string(513, 'd'); + EXPECT_FALSE(AgentSkill::FromJson(longDesc, skill)); +} + +/** + * @tc.name: SkillFromJson_041 + * @tc.desc: Test FromJson fails when tags is missing or not array + * @tc.type: FUNC + */ +HWTEST_F(AgentCardTest, SkillFromJson_041, TestSize.Level1) +{ + AgentSkill skill; + auto missingTags = BuildValidSkillJson(); + missingTags.erase("tags"); + EXPECT_FALSE(AgentSkill::FromJson(missingTags, skill)); + + auto wrongTags = BuildValidSkillJson(); + wrongTags["tags"] = "not_array"; + EXPECT_FALSE(AgentSkill::FromJson(wrongTags, skill)); +} + +/** + * @tc.name: SkillFromJson_042 + * @tc.desc: Test FromJson skips non-string and out-of-range tags, rejects empty tags result + * @tc.type: FUNC + */ +HWTEST_F(AgentCardTest, SkillFromJson_042, TestSize.Level1) +{ + AgentSkill skill; + auto badTags = BuildValidSkillJson(); + badTags["tags"] = {123, "", std::string(33, 't')}; + EXPECT_FALSE(AgentSkill::FromJson(badTags, skill)); +} + +/** + * @tc.name: SkillFromJson_043 + * @tc.desc: Test FromJson skips non-string examples and filters length + * @tc.type: FUNC + */ +HWTEST_F(AgentCardTest, SkillFromJson_043, TestSize.Level1) +{ + auto json = BuildValidSkillJson(); + json["examples"] = {"valid", 42, "", std::string(257, 'e')}; + AgentSkill skill; + EXPECT_TRUE(AgentSkill::FromJson(json, skill)); + ASSERT_EQ(skill.examples.size(), 1u); + EXPECT_EQ(skill.examples[0], "valid"); +} + +/** + * @tc.name: SkillFromJson_044 + * @tc.desc: Test FromJson skips non-string inputModes/outputModes + * @tc.type: FUNC + */ +HWTEST_F(AgentCardTest, SkillFromJson_044, TestSize.Level1) +{ + auto json = BuildValidSkillJson(); + json["inputModes"] = {"text", 42, "", std::string(33, 'i')}; + json["outputModes"] = {true, "text", std::string(33, 'o')}; + AgentSkill skill; + EXPECT_TRUE(AgentSkill::FromJson(json, skill)); + ASSERT_EQ(skill.inputModes.size(), 1u); + EXPECT_EQ(skill.inputModes[0], "text"); + ASSERT_EQ(skill.outputModes.size(), 1u); + EXPECT_EQ(skill.outputModes[0], "text"); +} + +/** + * @tc.name: SkillFromJson_045 + * @tc.desc: Test FromJson clears invalid extension (too long and invalid json) + * @tc.type: FUNC + */ +HWTEST_F(AgentCardTest, SkillFromJson_045, TestSize.Level1) +{ + auto tooLong = BuildValidSkillJson(); + tooLong["extension"] = std::string(1025, 'e'); + AgentSkill skill1; + EXPECT_TRUE(AgentSkill::FromJson(tooLong, skill1)); + EXPECT_TRUE(skill1.extension.empty()); + + auto badJson = BuildValidSkillJson(); + badJson["extension"] = "{not json"; + AgentSkill skill2; + EXPECT_TRUE(AgentSkill::FromJson(badJson, skill2)); + EXPECT_TRUE(skill2.extension.empty()); + + auto validExt = BuildValidSkillJson(); + validExt["extension"] = R"({"key":"value"})"; + AgentSkill skill3; + EXPECT_TRUE(AgentSkill::FromJson(validExt, skill3)); + EXPECT_EQ(skill3.extension, R"({"key":"value"})"); +} + +// ==================== AgentCard::FromJson Additional Branch Tests ==================== + +/** + * @tc.name: AgentCardFromJson_054 + * @tc.desc: Test FromJson with type as valid integer min and max + * @tc.type: FUNC + */ +HWTEST_F(AgentCardTest, AgentCardFromJson_054, TestSize.Level1) +{ + auto jsonMin = BuildValidAgentCardJson(); + jsonMin["type"] = 0; // APP + AgentCard card; + EXPECT_TRUE(AgentCard::FromJson(jsonMin, card)); + EXPECT_EQ(card.type, AgentCardType::APP); + + auto jsonMax = BuildValidAgentCardJson(); + jsonMax["type"] = 2; // LOW_CODE + AgentCard card2; + EXPECT_TRUE(AgentCard::FromJson(jsonMax, card2)); + EXPECT_EQ(card2.type, AgentCardType::LOW_CODE); +} + +/** + * @tc.name: AgentCardFromJson_055 + * @tc.desc: Test FromJson rejects integer type out of range + * @tc.type: FUNC + */ +HWTEST_F(AgentCardTest, AgentCardFromJson_055, TestSize.Level1) +{ + auto jsonNeg = BuildValidAgentCardJson(); + jsonNeg["type"] = -1; + AgentCard card; + EXPECT_FALSE(AgentCard::FromJson(jsonNeg, card)); + + auto jsonBig = BuildValidAgentCardJson(); + jsonBig["type"] = 99; + EXPECT_FALSE(AgentCard::FromJson(jsonBig, card)); +} + +/** + * @tc.name: AgentCardFromJson_056 + * @tc.desc: Test FromJson with type as valid string values + * @tc.type: FUNC + */ +HWTEST_F(AgentCardTest, AgentCardFromJson_056, TestSize.Level1) +{ + AgentCard card; + auto jsonApp = BuildValidAgentCardJson(); + jsonApp["type"] = "APP"; + EXPECT_TRUE(AgentCard::FromJson(jsonApp, card)); + EXPECT_EQ(card.type, AgentCardType::APP); + + auto jsonAtomic = BuildValidAgentCardJson(); + jsonAtomic["type"] = "ATOMIC_SERVICE"; + EXPECT_TRUE(AgentCard::FromJson(jsonAtomic, card)); + EXPECT_EQ(card.type, AgentCardType::ATOMIC_SERVICE); + + auto jsonLowCode = BuildValidAgentCardJson(); + jsonLowCode["type"] = "LOW_CODE"; + EXPECT_TRUE(AgentCard::FromJson(jsonLowCode, card)); + EXPECT_EQ(card.type, AgentCardType::LOW_CODE); +} + +/** + * @tc.name: AgentCardFromJson_057 + * @tc.desc: Test FromJson rejects invalid type string + * @tc.type: FUNC + */ +HWTEST_F(AgentCardTest, AgentCardFromJson_057, TestSize.Level1) +{ + auto json = BuildValidAgentCardJson(); + json["type"] = "INVALID_TYPE"; + AgentCard card; + EXPECT_FALSE(AgentCard::FromJson(json, card)); +} + +/** + * @tc.name: AgentCardFromJson_058 + * @tc.desc: Test FromJson fails when agentId is missing, empty, too long + * @tc.type: FUNC + */ +HWTEST_F(AgentCardTest, AgentCardFromJson_058, TestSize.Level1) +{ + AgentCard card; + auto missing = BuildValidAgentCardJson(); + missing.erase("agentId"); + EXPECT_FALSE(AgentCard::FromJson(missing, card)); + + auto empty = BuildValidAgentCardJson(); + empty["agentId"] = ""; + EXPECT_FALSE(AgentCard::FromJson(empty, card)); + + auto tooLong = BuildValidAgentCardJson(); + tooLong["agentId"] = std::string(65, 'a'); + EXPECT_FALSE(AgentCard::FromJson(tooLong, card)); +} + +/** + * @tc.name: AgentCardFromJson_059 + * @tc.desc: Test FromJson fails when name is missing, wrong type, empty, too long + * @tc.type: FUNC + */ +HWTEST_F(AgentCardTest, AgentCardFromJson_059, TestSize.Level1) +{ + AgentCard card; + auto missing = BuildValidAgentCardJson(); + missing.erase("name"); + EXPECT_FALSE(AgentCard::FromJson(missing, card)); + + auto wrongType = BuildValidAgentCardJson(); + wrongType["name"] = 42; + EXPECT_FALSE(AgentCard::FromJson(wrongType, card)); + + auto empty = BuildValidAgentCardJson(); + empty["name"] = ""; + EXPECT_FALSE(AgentCard::FromJson(empty, card)); + + auto tooLong = BuildValidAgentCardJson(); + tooLong["name"] = std::string(65, 'n'); + EXPECT_FALSE(AgentCard::FromJson(tooLong, card)); +} + +/** + * @tc.name: AgentCardFromJson_060 + * @tc.desc: Test FromJson fails when category is missing, wrong type, empty, too long + * @tc.type: FUNC + */ +HWTEST_F(AgentCardTest, AgentCardFromJson_060, TestSize.Level1) +{ + AgentCard card; + auto missing = BuildValidAgentCardJson(); + missing.erase("category"); + EXPECT_FALSE(AgentCard::FromJson(missing, card)); + + auto wrongType = BuildValidAgentCardJson(); + wrongType["category"] = 42; + EXPECT_FALSE(AgentCard::FromJson(wrongType, card)); + + auto tooLong = BuildValidAgentCardJson(); + tooLong["category"] = std::string(65, 'c'); + EXPECT_FALSE(AgentCard::FromJson(tooLong, card)); +} + +/** + * @tc.name: AgentCardFromJson_061 + * @tc.desc: Test FromJson fails when description is missing, wrong type, empty, too long + * @tc.type: FUNC + */ +HWTEST_F(AgentCardTest, AgentCardFromJson_061, TestSize.Level1) +{ + AgentCard card; + auto missing = BuildValidAgentCardJson(); + missing.erase("description"); + EXPECT_FALSE(AgentCard::FromJson(missing, card)); + + auto wrongType = BuildValidAgentCardJson(); + wrongType["description"] = true; + EXPECT_FALSE(AgentCard::FromJson(wrongType, card)); + + auto tooLong = BuildValidAgentCardJson(); + tooLong["description"] = std::string(513, 'd'); + EXPECT_FALSE(AgentCard::FromJson(tooLong, card)); +} + +/** + * @tc.name: AgentCardFromJson_062 + * @tc.desc: Test FromJson fails when version is missing, wrong type, empty, too long + * @tc.type: FUNC + */ +HWTEST_F(AgentCardTest, AgentCardFromJson_062, TestSize.Level1) +{ + AgentCard card; + auto missing = BuildValidAgentCardJson(); + missing.erase("version"); + EXPECT_FALSE(AgentCard::FromJson(missing, card)); + + auto wrongType = BuildValidAgentCardJson(); + wrongType["version"] = 1; + EXPECT_FALSE(AgentCard::FromJson(wrongType, card)); + + auto empty = BuildValidAgentCardJson(); + empty["version"] = ""; + EXPECT_FALSE(AgentCard::FromJson(empty, card)); + + auto tooLong = BuildValidAgentCardJson(); + tooLong["version"] = std::string(33, 'v'); + EXPECT_FALSE(AgentCard::FromJson(tooLong, card)); +} + +/** + * @tc.name: AgentCardFromJson_063 + * @tc.desc: Test FromJson fails when appInfo object has invalid inner content + * @tc.type: FUNC + */ +HWTEST_F(AgentCardTest, AgentCardFromJson_063, TestSize.Level1) +{ + auto json = BuildValidAgentCardJson(); + json["appInfo"] = nlohmann::json::object(); // empty object, still valid + AgentCard card; + EXPECT_TRUE(AgentCard::FromJson(json, card)); + EXPECT_NE(card.appInfo, nullptr); +} + +/** + * @tc.name: AgentCardFromJson_064 + * @tc.desc: Test FromJson succeeds with valid provider + * @tc.type: FUNC + */ +HWTEST_F(AgentCardTest, AgentCardFromJson_064, TestSize.Level1) +{ + auto json = BuildValidAgentCardJson(); + json["provider"] = {{"organization", "TestOrg"}, {"url", "http://example.com"}}; + AgentCard card; + EXPECT_TRUE(AgentCard::FromJson(json, card)); + ASSERT_NE(card.provider, nullptr); + EXPECT_EQ(card.provider->organization, "TestOrg"); +} + +/** + * @tc.name: AgentCardFromJson_065 + * @tc.desc: Test FromJson succeeds with valid capabilities + * @tc.type: FUNC + */ +HWTEST_F(AgentCardTest, AgentCardFromJson_065, TestSize.Level1) +{ + auto json = BuildValidAgentCardJson(); + json["capabilities"] = {{"streaming", true}, {"pushNotifications", false}}; + AgentCard card; + EXPECT_TRUE(AgentCard::FromJson(json, card)); + ASSERT_NE(card.capabilities, nullptr); + EXPECT_TRUE(card.capabilities->streaming); + EXPECT_FALSE(card.capabilities->pushNotifications); +} + +/** + * @tc.name: AgentCardFromJson_066 + * @tc.desc: Test FromJson rejects missing iconUrl and invalid iconUrl length + * @tc.type: FUNC + */ +HWTEST_F(AgentCardTest, AgentCardFromJson_066, TestSize.Level1) +{ + AgentCard card; + auto missing = BuildValidAgentCardJson(); + missing.erase("iconUrl"); + EXPECT_FALSE(AgentCard::FromJson(missing, card)); + + auto wrongType = BuildValidAgentCardJson(); + wrongType["iconUrl"] = 42; + EXPECT_FALSE(AgentCard::FromJson(wrongType, card)); + + auto tooLong = BuildValidAgentCardJson(); + tooLong["iconUrl"] = std::string(513, 'u'); + EXPECT_FALSE(AgentCard::FromJson(tooLong, card)); +} + +/** + * @tc.name: AgentCardFromJson_067 + * @tc.desc: Test FromJson with valid documentationUrl and oversized truncation + * @tc.type: FUNC + */ +HWTEST_F(AgentCardTest, AgentCardFromJson_067, TestSize.Level1) +{ + auto validDoc = BuildValidAgentCardJson(); + validDoc["documentationUrl"] = "http://docs.example.com"; + AgentCard card; + EXPECT_TRUE(AgentCard::FromJson(validDoc, card)); + EXPECT_EQ(card.documentationUrl, "http://docs.example.com"); + + auto oversizedDoc = BuildValidAgentCardJson(); + oversizedDoc["documentationUrl"] = std::string(513, 'd'); + AgentCard card2; + EXPECT_TRUE(AgentCard::FromJson(oversizedDoc, card2)); + EXPECT_TRUE(card2.documentationUrl.empty()); +} + +/** + * @tc.name: AgentCardFromJson_068 + * @tc.desc: Test FromJson clears extension that exceeds 5120 chars + * @tc.type: FUNC + */ +HWTEST_F(AgentCardTest, AgentCardFromJson_068, TestSize.Level1) +{ + auto json = BuildValidAgentCardJson(); + json["extension"] = std::string(5121, 'e'); + AgentCard card; + EXPECT_TRUE(AgentCard::FromJson(json, card)); + EXPECT_TRUE(card.extension.empty()); +} + +/** + * @tc.name: AgentCardsRawData_RoundTrip_001 + * @tc.desc: Test valid round trip through FromAgentCardVec and ToAgentCardVec + * @tc.type: FUNC + */ +HWTEST_F(AgentCardTest, AgentCardsRawData_RoundTrip_001, TestSize.Level1) +{ + AgentCard card; + auto json = BuildValidAgentCardJson(); + ASSERT_TRUE(AgentCard::FromJson(json, card)); + + std::vector original = {card}; + AgentCardsRawData rawData; + AgentCardsRawData::FromAgentCardVec(original, rawData); + + std::vector parsed; + EXPECT_EQ(AgentCardsRawData::ToAgentCardVec(rawData, parsed), ERR_OK); + ASSERT_EQ(parsed.size(), 1u); + EXPECT_EQ(parsed[0].agentId, "1"); + EXPECT_EQ(parsed[0].name, "test"); +} + +/** + * @tc.name: AgentCardsRawData_CountOverflow_001 + * @tc.desc: Test ToAgentCardVec rejects count exceeding MAX_AGENT_CARD_COUNT + * @tc.type: FUNC + */ +HWTEST_F(AgentCardTest, AgentCardsRawData_CountOverflow_001, TestSize.Level1) +{ + uint32_t tooMany = 200001; + std::string buffer(reinterpret_cast(&tooMany), sizeof(tooMany)); + AgentCardsRawData rawData; + rawData.data = buffer.data(); + rawData.size = buffer.size(); + + std::vector cards; + EXPECT_EQ(AgentCardsRawData::ToAgentCardVec(rawData, cards), ERR_AGENT_CARD_LIST_OUT_OF_RANGE); +} } // namespace AgentRuntime } // namespace OHOS diff --git a/test/unittest/agent_runtime_framework/agent_card_utils_test/agent_card_utils_test.cpp b/test/unittest/agent_runtime_framework/agent_card_utils_test/agent_card_utils_test.cpp index 6ef58fec77..6bab292fb3 100644 --- a/test/unittest/agent_runtime_framework/agent_card_utils_test/agent_card_utils_test.cpp +++ b/test/unittest/agent_runtime_framework/agent_card_utils_test/agent_card_utils_test.cpp @@ -117,6 +117,11 @@ HWTEST_F(AgentCardUtilsTest, HasRequiredRegisterFields_001, TestSize.Level1) auto invalidIconCard = BuildCard("invalidIcon", "1.0.0"); invalidIconCard.iconUrl.clear(); EXPECT_FALSE(AgentCardUtils::HasRequiredRegisterFields(invalidIconCard)); + invalidIconCard.iconUrl = std::string(513, 'a'); + EXPECT_FALSE(AgentCardUtils::HasRequiredRegisterFields(invalidIconCard)); + EXPECT_FALSE(AgentCardUtils::HasValidIconUrl("")); + EXPECT_TRUE(AgentCardUtils::HasValidIconUrl(std::string(512, 'a'))); + EXPECT_FALSE(AgentCardUtils::HasValidIconUrl(std::string(513, 'a'))); } HWTEST_F(AgentCardUtilsTest, ShouldValidateAppInfo_001, TestSize.Level1) @@ -124,6 +129,8 @@ HWTEST_F(AgentCardUtilsTest, ShouldValidateAppInfo_001, TestSize.Level1) EXPECT_TRUE(AgentCardUtils::ShouldValidateAppInfo(BuildCard("app", "1.0.0", AgentCardType::APP))); EXPECT_TRUE(AgentCardUtils::ShouldValidateAppInfo(BuildCard("lowCode", "1.0.0", AgentCardType::LOW_CODE))); EXPECT_TRUE(AgentCardUtils::ShouldValidateAppInfo(BuildCard("atomic", "1.0.0", AgentCardType::ATOMIC_SERVICE))); + EXPECT_FALSE(AgentCardUtils::ShouldValidateAppInfo(BuildCard( + "invalidType", "1.0.0", static_cast(99)))); } HWTEST_F(AgentCardUtilsTest, ShouldValidateBundleAbility_001, TestSize.Level1) @@ -137,12 +144,24 @@ HWTEST_F(AgentCardUtilsTest, ShouldValidateBundleAbility_001, TestSize.Level1) MyFlag::retGetBundleInfo = false; EXPECT_FALSE(AgentCardUtils::ShouldValidateBundleAbility( BuildCard("atomic", "1.0.0", AgentCardType::ATOMIC_SERVICE), 100)); + EXPECT_FALSE(AgentCardUtils::ShouldValidateBundleAbility(BuildCard( + "invalidType", "1.0.0", static_cast(99)), 100)); + + auto atomicWithoutAppInfo = BuildCard("atomicNoAppInfo", "1.0.0", AgentCardType::ATOMIC_SERVICE); + atomicWithoutAppInfo.appInfo = nullptr; + EXPECT_FALSE(AgentCardUtils::ShouldValidateBundleAbility(atomicWithoutAppInfo, 100)); } HWTEST_F(AgentCardUtilsTest, ValidateSystemAppRequirement_001, TestSize.Level1) { EXPECT_EQ(AgentCardUtils::ValidateSystemAppRequirement( BuildCard("app", "1.0.0", AgentCardType::APP), 100), ERR_OK); + auto lowCodeWithoutAppInfo = BuildCard("lowCode", "1.0.0", AgentCardType::LOW_CODE); + lowCodeWithoutAppInfo.appInfo = nullptr; + EXPECT_EQ(AgentCardUtils::ValidateSystemAppRequirement(lowCodeWithoutAppInfo, 100), ERR_OK); + auto lowCodeWithoutBundleName = BuildCard("lowCode", "1.0.0", AgentCardType::LOW_CODE); + lowCodeWithoutBundleName.appInfo->bundleName.clear(); + EXPECT_EQ(AgentCardUtils::ValidateSystemAppRequirement(lowCodeWithoutBundleName, 100), ERR_OK); } HWTEST_F(AgentCardUtilsTest, ValidateSystemAppRequirement_002, TestSize.Level1) @@ -170,6 +189,9 @@ HWTEST_F(AgentCardUtilsTest, IsCardOwnedByAbility_001, TestSize.Level1) auto card = BuildCard("testAgent", "1.0.0"); EXPECT_TRUE(AgentCardUtils::IsCardOwnedByAbility(card, "test.bundle", "TestAgent")); EXPECT_FALSE(AgentCardUtils::IsCardOwnedByAbility(card, "other.bundle", "TestAgent")); + EXPECT_FALSE(AgentCardUtils::IsCardOwnedByAbility(card, "test.bundle", "OtherAgent")); + card.appInfo = nullptr; + EXPECT_FALSE(AgentCardUtils::IsCardOwnedByAbility(card, "test.bundle", "TestAgent")); } HWTEST_F(AgentCardUtilsTest, ApplyDeviceTypes_001, TestSize.Level1) @@ -179,6 +201,13 @@ HWTEST_F(AgentCardUtilsTest, ApplyDeviceTypes_001, TestSize.Level1) AgentCardUtils::ApplyDeviceTypes({"phone", "tablet"}, card); ASSERT_EQ(card.appInfo->deviceTypes.size(), 1); EXPECT_EQ(card.appInfo->deviceTypes[0], "phone"); + + auto fallbackCard = BuildCard("fallbackAgent", "1.0.0"); + fallbackCard.appInfo->deviceTypes = {"watch"}; + AgentCardUtils::ApplyDeviceTypes({"phone", "tablet"}, fallbackCard); + ASSERT_EQ(fallbackCard.appInfo->deviceTypes.size(), 2); + EXPECT_EQ(fallbackCard.appInfo->deviceTypes[0], "phone"); + EXPECT_EQ(fallbackCard.appInfo->deviceTypes[1], "tablet"); } HWTEST_F(AgentCardUtilsTest, ApplyDeviceTypes_002, TestSize.Level1) @@ -210,6 +239,8 @@ HWTEST_F(AgentCardUtilsTest, ShouldKeepStoredCard_001, TestSize.Level1) BuildCard("testAgent", "1.0.0"), BuildCard("testAgent", "invalid-version"))); EXPECT_TRUE(AgentCardUtils::ShouldKeepStoredCard( BuildCard("testAgent", "1.0.0"), BuildCard("testAgent", "2.0.0"))); + EXPECT_FALSE(AgentCardUtils::ShouldKeepStoredCard( + BuildCard("testAgent", "1.0.0"), BuildCard("testAgent", "1.0.0"))); EXPECT_FALSE(AgentCardUtils::ShouldKeepStoredCard( BuildCard("testAgent", "2.0.0"), BuildCard("testAgent", "1.0.0"))); } @@ -226,6 +257,7 @@ HWTEST_F(AgentCardUtilsTest, FindHapDeviceTypes_001, TestSize.Level1) ASSERT_EQ(deviceTypes.size(), 2); EXPECT_EQ(deviceTypes[0], "phone"); EXPECT_EQ(deviceTypes[1], "tablet"); + EXPECT_TRUE(AgentCardUtils::FindHapDeviceTypes(bundleInfo, "missingModule").empty()); } } // namespace AgentRuntime } // namespace OHOS diff --git a/test/unittest/agent_runtime_framework/agent_manager_client_test/agent_manager_client_test.cpp b/test/unittest/agent_runtime_framework/agent_manager_client_test/agent_manager_client_test.cpp index fd5039d17b..ef340e5c5c 100644 --- a/test/unittest/agent_runtime_framework/agent_manager_client_test/agent_manager_client_test.cpp +++ b/test/unittest/agent_runtime_framework/agent_manager_client_test/agent_manager_client_test.cpp @@ -68,6 +68,8 @@ void AgentManagerClientTest::SetUp(void) MyFlag::retRegisterAgentCard = ERR_OK; MyFlag::retUpdateAgentCard = ERR_OK; MyFlag::retDeleteAgentCard = ERR_OK; + MyFlag::retToAgentCardVec = ERR_OK; + MyFlag::convertedCards.clear(); } void AgentManagerClientTest::TearDown(void) @@ -191,11 +193,35 @@ HWTEST_F(AgentManagerClientTest, GetAgentCardsByBundleName_003, TestSize.Level1) auto mockAgentMgr = sptr::MakeSptr(); client.agentMgr_ = mockAgentMgr; MyFlag::retGetAgentCardsByBundleName = ERR_OK; + AgentCard card; + card.agentId = "agent"; + MyFlag::convertedCards = { card }; std::vector cards; std::string bundleName = "bundle"; int32_t result = client.GetAgentCardsByBundleName(bundleName, cards); EXPECT_EQ(result, ERR_OK); + ASSERT_EQ(cards.size(), 1); + EXPECT_EQ(cards[0].agentId, "agent"); +} + +/** +* @tc.name : GetAgentCardsByBundleName_ShouldReturnError_WhenRawDataConversionFails +* @tc.number: GetAgentCardsByBundleName_004 +* @tc.desc : Test that GetAgentCardsByBundleName returns the error code when raw data conversion fails. +*/ +HWTEST_F(AgentManagerClientTest, GetAgentCardsByBundleName_004, TestSize.Level1) +{ + AgentManagerClient client; + auto mockAgentMgr = sptr::MakeSptr(); + client.agentMgr_ = mockAgentMgr; + MyFlag::retGetAgentCardsByBundleName = ERR_OK; + MyFlag::retToAgentCardVec = ERR_INVALID_VALUE; + + std::vector cards; + std::string bundleName = "bundle"; + int32_t result = client.GetAgentCardsByBundleName(bundleName, cards); + EXPECT_EQ(result, ERR_INVALID_VALUE); } /** diff --git a/test/unittest/agent_runtime_framework/agent_manager_client_test/mock/include/iagent_manager.h b/test/unittest/agent_runtime_framework/agent_manager_client_test/mock/include/iagent_manager.h index e6d9de7bad..df77464439 100644 --- a/test/unittest/agent_runtime_framework/agent_manager_client_test/mock/include/iagent_manager.h +++ b/test/unittest/agent_runtime_framework/agent_manager_client_test/mock/include/iagent_manager.h @@ -34,7 +34,7 @@ public: return 0; } - virtual int32_t GetAgentCardsByBundleName(const std::string &bundleName, std::vector &cards) + virtual int32_t GetAgentCardsByBundleName(const std::string &bundleName, AgentCardsRawData &rawData) { return 0; } diff --git a/test/unittest/agent_runtime_framework/agent_manager_client_test/mock/include/mock_agent_manager_service.h b/test/unittest/agent_runtime_framework/agent_manager_client_test/mock/include/mock_agent_manager_service.h index 9c708071ad..d7c48ab17d 100644 --- a/test/unittest/agent_runtime_framework/agent_manager_client_test/mock/include/mock_agent_manager_service.h +++ b/test/unittest/agent_runtime_framework/agent_manager_client_test/mock/include/mock_agent_manager_service.h @@ -28,7 +28,7 @@ public: virtual int32_t GetAllAgentCards(AgentCardsRawData &rawData) override; - virtual int32_t GetAgentCardsByBundleName(const std::string &bundleName, std::vector &cards) override; + virtual int32_t GetAgentCardsByBundleName(const std::string &bundleName, AgentCardsRawData &rawData) override; virtual int32_t GetAgentCardByAgentId(const std::string &bundleName, const std::string &agentId, AgentCard &card) override; diff --git a/test/unittest/agent_runtime_framework/agent_manager_client_test/mock/src/mock_agent_manager_service.cpp b/test/unittest/agent_runtime_framework/agent_manager_client_test/mock/src/mock_agent_manager_service.cpp index 7cdba1c226..32ec4a3930 100644 --- a/test/unittest/agent_runtime_framework/agent_manager_client_test/mock/src/mock_agent_manager_service.cpp +++ b/test/unittest/agent_runtime_framework/agent_manager_client_test/mock/src/mock_agent_manager_service.cpp @@ -45,8 +45,7 @@ int32_t MockAgentManagerService::GetAllAgentCards(AgentCardsRawData &rawData) return MyFlag::retGetAllAgentCards; } -int32_t MockAgentManagerService::GetAgentCardsByBundleName(const std::string &bundleName, - std::vector &cards) +int32_t MockAgentManagerService::GetAgentCardsByBundleName(const std::string &bundleName, AgentCardsRawData &rawData) { return MyFlag::retGetAgentCardsByBundleName; } diff --git a/test/unittest/agent_runtime_framework/agent_manager_service_test/agent_manager_service_test.cpp b/test/unittest/agent_runtime_framework/agent_manager_service_test/agent_manager_service_test.cpp index 0ddd5d5dec..d0c649b901 100644 --- a/test/unittest/agent_runtime_framework/agent_manager_service_test/agent_manager_service_test.cpp +++ b/test/unittest/agent_runtime_framework/agent_manager_service_test/agent_manager_service_test.cpp @@ -45,6 +45,31 @@ namespace OHOS { namespace AgentRuntime { const int BUNDLE_MGR_SERVICE_SYS_ABILITY_ID = 401; +AgentCard BuildServiceTestAgentCard(const std::string &agentId) +{ + AgentCard card; + card.agentId = agentId; + card.type = AgentCardType::APP; + card.name = agentId; + card.description = "desc"; + card.version = "1.0.0"; + card.category = "productivity"; + card.defaultInputModes = { "text/plain" }; + card.defaultOutputModes = { "text/plain" }; + card.iconUrl = "http://example.com/icon.png"; + auto skill = std::make_shared(); + skill->id = agentId + "_skill"; + skill->name = "skill"; + skill->description = "skill desc"; + skill->tags = { "tag" }; + card.skills = { skill }; + card.appInfo = std::make_shared(); + card.appInfo->bundleName = "bundle"; + card.appInfo->moduleName = "module"; + card.appInfo->abilityName = "ability"; + return card; +} + class AgentManagerServiceTest : public testing::Test { public: static void SetUpTestCase(void); @@ -86,6 +111,7 @@ void AgentManagerServiceTest::SetUp(void) MyFlag::retGetBundleNameByPid = ERR_OK; MyFlag::processState = AppExecFwk::AppProcessState::APP_STATE_FOREGROUND; MyFlag::retGetAgentCardByAgentId = ERR_OK; + MyFlag::agentCardsByBundleName.clear(); MyFlag::agentCardAgentId = "testAgent"; MyFlag::agentCardBundleName = "test.bundle"; MyFlag::agentCardModuleName = ""; @@ -345,7 +371,7 @@ HWTEST_F(AgentManagerServiceTest, GetAgentCardsByBundleName_000, TestSize.Level1 { MyFlag::retJudgeCallerIsAllowedToUseSystemAPI = false; std::string bundleName = "bundle"; - std::vector cards; + AgentCardsRawData cards; EXPECT_EQ(AgentManagerService::GetInstance()->GetAgentCardsByBundleName(bundleName, cards), ERR_NOT_SYSTEM_APP); } @@ -360,7 +386,7 @@ HWTEST_F(AgentManagerServiceTest, GetAgentCardsByBundleName_001, TestSize.Level1 MyFlag::retGetAgentCardsByBundleName = ERR_NAME_NOT_FOUND; MyFlag::retGetApplicationInfo = true; std::string bundleName = "bundle"; - std::vector cards; + AgentCardsRawData cards; EXPECT_EQ(AgentManagerService::GetInstance()->GetAgentCardsByBundleName(bundleName, cards), ERR_OK); MyFlag::retGetAgentCardsByBundleName = ERR_OK; } @@ -374,7 +400,7 @@ HWTEST_F(AgentManagerServiceTest, GetAgentCardsByBundleName_002, TestSize.Level1 { MyFlag::retVerifyGetAgentCardPermission = false; std::string bundleName = "bundle"; - std::vector cards; + AgentCardsRawData cards; EXPECT_EQ(AgentManagerService::GetInstance()->GetAgentCardsByBundleName(bundleName, cards), ERR_PERMISSION_DENIED); MyFlag::retVerifyGetAgentCardPermission = true; } @@ -389,7 +415,7 @@ HWTEST_F(AgentManagerServiceTest, GetAgentCardsByBundleName_003, TestSize.Level1 MyFlag::retVerifyCallingPermission = true; MyFlag::retGetAgentCardsByBundleName = ERR_INVALID_VALUE; std::string bundleName = "bundle"; - std::vector cards; + AgentCardsRawData cards; EXPECT_EQ(AgentManagerService::GetInstance()->GetAgentCardsByBundleName(bundleName, cards), ERR_INVALID_VALUE); MyFlag::retGetAgentCardsByBundleName = ERR_OK; } @@ -403,9 +429,15 @@ HWTEST_F(AgentManagerServiceTest, GetAgentCardsByBundleName_004, TestSize.Level1 { MyFlag::retVerifyCallingPermission = true; MyFlag::retGetAgentCardsByBundleName = ERR_OK; + MyFlag::agentCardsByBundleName = { BuildServiceTestAgentCard("agent1"), BuildServiceTestAgentCard("agent2") }; std::string bundleName = "bundle"; - std::vector cards; + AgentCardsRawData cards; EXPECT_EQ(AgentManagerService::GetInstance()->GetAgentCardsByBundleName(bundleName, cards), ERR_OK); + std::vector cardVec; + EXPECT_EQ(AgentCardsRawData::ToAgentCardVec(cards, cardVec), ERR_OK); + ASSERT_EQ(cardVec.size(), 2); + EXPECT_EQ(cardVec[0].agentId, "agent1"); + EXPECT_EQ(cardVec[1].agentId, "agent2"); } /** @@ -419,7 +451,7 @@ HWTEST_F(AgentManagerServiceTest, GetAgentCardsByBundleName_005, TestSize.Level1 MyFlag::retGetAgentCardsByBundleName = ERR_NAME_NOT_FOUND; MyFlag::retGetApplicationInfo = false; std::string bundleName = "bundle"; - std::vector cards; + AgentCardsRawData cards; EXPECT_EQ(AgentManagerService::GetInstance()->GetAgentCardsByBundleName(bundleName, cards), AAFwk::ERR_BUNDLE_NOT_EXIST); MyFlag::retGetAgentCardsByBundleName = ERR_OK; @@ -1407,7 +1439,7 @@ HWTEST_F(AgentManagerServiceTest, ConnectAgentExtensionAbility_026, TestSize.Lev EXPECT_EQ(AgentManagerService::GetInstance()->ConnectAgentExtensionAbility(wantB, connectionB), ERR_OK); EXPECT_EQ(MyFlag::connectAbilityWithExtensionTypeCallCount, 1); ASSERT_EQ(service->callerConnectionCounts_.size(), 1); - EXPECT_EQ(service->callerConnectionCounts_.begin()->second, 2); + EXPECT_EQ(service->callerConnectionCounts_.begin()->second, 1); EXPECT_EQ(receiver->agentInvokedCount, 2); ASSERT_EQ(receiver->invokedAgentIds.size(), 2); EXPECT_EQ(receiver->invokedAgentIds[1], "agentB"); @@ -1527,7 +1559,7 @@ HWTEST_F(AgentManagerServiceTest, ConnectAgentExtensionAbility_029, TestSize.Lev /** * @tc.name : ConnectAgentExtensionAbility_030 * @tc.number: ConnectAgentExtensionAbility_030 - * @tc.desc : Test low-code shared host still enforces MAX_CONNECTIONS_PER_CALLER per caller + * @tc.desc : Test low-code shared host allows MAX_AGENTS_PER_HOST_SESSION agents */ HWTEST_F(AgentManagerServiceTest, ConnectAgentExtensionAbility_030, TestSize.Level1) { @@ -1550,7 +1582,7 @@ HWTEST_F(AgentManagerServiceTest, ConnectAgentExtensionAbility_030, TestSize.Lev AppExecFwk::ElementName element("", "lowcode.bundle", "LowCodeExtAbility", "entry"); service->HandleAgentHostConnectDone(hostKey, element, receiver->AsObject(), ERR_OK); - for (size_t i = 1; i < AgentManagerService::MAX_CONNECTIONS_PER_CALLER; i++) { + for (size_t i = 1; i < AgentManagerService::MAX_AGENTS_PER_HOST_SESSION; i++) { std::string index = std::to_string(i); std::string agentId = "agent" + index; AAFwk::Want want; @@ -1560,9 +1592,9 @@ HWTEST_F(AgentManagerServiceTest, ConnectAgentExtensionAbility_030, TestSize.Lev EXPECT_EQ(service->ConnectAgentExtensionAbility(want, connection), ERR_OK); } - EXPECT_EQ(service->agentOwners_.size(), AgentManagerService::MAX_CONNECTIONS_PER_CALLER); + EXPECT_EQ(service->agentOwners_.size(), AgentManagerService::MAX_AGENTS_PER_HOST_SESSION); ASSERT_EQ(service->callerConnectionCounts_.size(), 1); - EXPECT_EQ(service->callerConnectionCounts_.begin()->second, AgentManagerService::MAX_CONNECTIONS_PER_CALLER); + EXPECT_EQ(service->callerConnectionCounts_.begin()->second, 1); EXPECT_EQ(MyFlag::connectAbilityWithExtensionTypeCallCount, 1); AAFwk::Want overflowWant; @@ -1573,6 +1605,41 @@ HWTEST_F(AgentManagerServiceTest, ConnectAgentExtensionAbility_030, TestSize.Lev ERR_MAX_AGENT_CONNECTIONS_REACHED); } +/** + * @tc.name : ConnectAgentExtensionAbility_032 + * @tc.number: ConnectAgentExtensionAbility_032 + * @tc.desc : Test reused low-code host session ignores caller-wide quota and uses the host session limit + */ +HWTEST_F(AgentManagerServiceTest, ConnectAgentExtensionAbility_032, TestSize.Level1) +{ + auto service = AgentManagerService::GetInstance(); + MyFlag::retGetAgentCardByAgentId = ERR_OK; + MyFlag::agentCardType = static_cast(AgentCardType::LOW_CODE); + MyFlag::agentCardBundleName = "lowcode.bundle"; + MyFlag::agentCardAbilityName = "LowCodeExtAbility"; + MyFlag::agentCardModuleName = "entry"; + + AAFwk::Want firstWant; + firstWant.SetParam(AGENTID_KEY, std::string("agent0")); + firstWant.SetElementName("", "lowcode.bundle", "LowCodeExtAbility", "entry"); + auto firstConnection = sptr::MakeSptr(); + EXPECT_EQ(service->ConnectAgentExtensionAbility(firstWant, firstConnection), ERR_OK); + ASSERT_EQ(service->callerConnectionCounts_.size(), 1); + + int32_t callerUid = IPCSkeleton::GetCallingUid(); + service->callerConnectionCounts_[callerUid] = AgentManagerService::MAX_CONNECTIONS_PER_CALLER; + + AAFwk::Want reuseWant; + reuseWant.SetParam(AGENTID_KEY, std::string("agent1")); + reuseWant.SetElementName("", "lowcode.bundle", "LowCodeExtAbility", "entry"); + auto reuseConnection = sptr::MakeSptr(); + EXPECT_EQ(service->ConnectAgentExtensionAbility(reuseWant, reuseConnection), ERR_OK); + EXPECT_EQ(MyFlag::connectAbilityWithExtensionTypeCallCount, 1); + EXPECT_EQ(service->agentOwners_.size(), 2); + ASSERT_EQ(service->callerConnectionCounts_.size(), 1); + EXPECT_EQ(service->callerConnectionCounts_[callerUid], AgentManagerService::MAX_CONNECTIONS_PER_CALLER); +} + /** * @tc.name : NotifyLowCodeAgentComplete_001 * @tc.number: NotifyLowCodeAgentComplete_001 @@ -2338,7 +2405,7 @@ HWTEST_F(AgentManagerServiceTest, ValidateConnectAgentRequest_003, TestSize.Leve /** * @tc.name : ValidateConnectAgentRequest_004 * @tc.number: ValidateConnectAgentRequest_004 -* @tc.desc : Test ValidateConnectAgentRequest rejects callers at the shared connection limit +* @tc.desc : Test ValidateConnectAgentRequest leaves quota checks to the classified connect path */ HWTEST_F(AgentManagerServiceTest, ValidateConnectAgentRequest_004, TestSize.Level1) { @@ -2348,8 +2415,8 @@ HWTEST_F(AgentManagerServiceTest, ValidateConnectAgentRequest_004, TestSize.Leve auto connection = sptr::MakeSptr(); int32_t outCallerUid = -1; - EXPECT_EQ(service->ValidateConnectAgentRequest(connection, outCallerUid), - AAFwk::ERR_MAX_AGENT_CONNECTIONS_REACHED); + EXPECT_EQ(service->ValidateConnectAgentRequest(connection, outCallerUid), ERR_OK); + EXPECT_EQ(outCallerUid, callerUid); } /** diff --git a/test/unittest/agent_runtime_framework/agent_manager_service_test/mock/include/mock_my_flag.h b/test/unittest/agent_runtime_framework/agent_manager_service_test/mock/include/mock_my_flag.h index ed7e86bc4a..ec4505b19c 100644 --- a/test/unittest/agent_runtime_framework/agent_manager_service_test/mock/include/mock_my_flag.h +++ b/test/unittest/agent_runtime_framework/agent_manager_service_test/mock/include/mock_my_flag.h @@ -19,6 +19,7 @@ #include #include #include "ability_connect_callback_interface.h" +#include "agent_card.h" #include "hap_module_info.h" #include "want.h" #include "iremote_object.h" @@ -51,6 +52,7 @@ public: static int32_t retGetAllAgentCards; static int32_t retGetAgentCardsByBundleName; static int32_t retGetAgentCardByAgentId; + static std::vector agentCardsByBundleName; static std::string agentCardAgentId; static std::string agentCardBundleName; static std::string agentCardModuleName; diff --git a/test/unittest/agent_runtime_framework/agent_manager_service_test/mock/src/mock_agent_card_mgr.cpp b/test/unittest/agent_runtime_framework/agent_manager_service_test/mock/src/mock_agent_card_mgr.cpp index 3c4e3787cb..6533d81d4f 100644 --- a/test/unittest/agent_runtime_framework/agent_manager_service_test/mock/src/mock_agent_card_mgr.cpp +++ b/test/unittest/agent_runtime_framework/agent_manager_service_test/mock/src/mock_agent_card_mgr.cpp @@ -29,6 +29,7 @@ std::string AgentRuntime::MyFlag::agentCardModuleName; std::string AgentRuntime::MyFlag::agentCardAbilityName = "TestAbility"; bool AgentRuntime::MyFlag::shouldCreateAgentCardAppInfo = true; int32_t AgentRuntime::MyFlag::agentCardType = 0; +std::vector AgentRuntime::MyFlag::agentCardsByBundleName; namespace AgentRuntime { AgentCardMgr &AgentCardMgr::GetInstance() @@ -50,6 +51,9 @@ int32_t AgentCardMgr::GetAllAgentCards(AgentCardsRawData &cards) int32_t AgentCardMgr::GetAgentCardsByBundleName(const std::string &bundleName, std::vector &cards) { + if (MyFlag::retGetAgentCardsByBundleName == ERR_OK) { + cards = MyFlag::agentCardsByBundleName; + } return MyFlag::retGetAgentCardsByBundleName; } diff --git a/test/unittest/agent_runtime_framework/sem_ver_test/sem_ver_test.cpp b/test/unittest/agent_runtime_framework/sem_ver_test/sem_ver_test.cpp index 4e1bf01c1d..d895b7ff1c 100644 --- a/test/unittest/agent_runtime_framework/sem_ver_test/sem_ver_test.cpp +++ b/test/unittest/agent_runtime_framework/sem_ver_test/sem_ver_test.cpp @@ -88,13 +88,20 @@ HWTEST_F(SemVerTest, SemVerCompareTest_005, TestSize.Level1) EXPECT_TRUE(IsValidSemVer("1.2.3-alpha.1+build.5")); EXPECT_FALSE(IsValidSemVer("")); EXPECT_FALSE(IsValidSemVer("1.0")); + EXPECT_FALSE(IsValidSemVer("1.0.0.0")); EXPECT_FALSE(IsValidSemVer("01.0.0")); + EXPECT_FALSE(IsValidSemVer("1.01.0")); + EXPECT_FALSE(IsValidSemVer("1.0.01")); EXPECT_FALSE(IsValidSemVer("1.0.0-01")); + EXPECT_FALSE(IsValidSemVer("1.0.0-")); EXPECT_FALSE(IsValidSemVer("1.0.0+")); EXPECT_FALSE(IsValidSemVer("1.0.0-alpha..1")); EXPECT_FALSE(IsValidSemVer("1.0.0-alpha_1")); EXPECT_FALSE(IsValidSemVer("1.0.0+build_1")); + EXPECT_FALSE(IsValidSemVer("1.0.0+build+meta")); + EXPECT_FALSE(IsValidSemVer("1.0.0-001.alpha")); EXPECT_FALSE(IsValidSemVer("1.a.0")); + EXPECT_FALSE(IsValidSemVer("1.0.0+build..1")); EXPECT_EQ(CompareSemVer("1.0", "1.0.0"), SemVerCompareResult::INVALID); EXPECT_EQ(CompareSemVer("1.0.0-alpha_1", "1.0.0"), SemVerCompareResult::INVALID); } @@ -108,6 +115,8 @@ HWTEST_F(SemVerTest, SemVerCompareTest_006, TestSize.Level1) { EXPECT_EQ(CompareSemVer("1.0.0-alpha.1", "1.0.0-alpha.1"), SemVerCompareResult::EQUAL); EXPECT_EQ(CompareSemVer("1.0.0-beta", "1.0.0-gamma"), SemVerCompareResult::LESS); + EXPECT_EQ(CompareSemVer("1.0.0-gamma", "1.0.0-beta"), SemVerCompareResult::GREATER); + EXPECT_EQ(CompareSemVer("1.0.1", "1.0.10"), SemVerCompareResult::LESS); } /** diff --git a/test/unittest/ams_ability_running_record_test/BUILD.gn b/test/unittest/ams_ability_running_record_test/BUILD.gn index 316a2a36c5..219883f322 100644 --- a/test/unittest/ams_ability_running_record_test/BUILD.gn +++ b/test/unittest/ams_ability_running_record_test/BUILD.gn @@ -92,6 +92,7 @@ ohos_unittest("AmsAbilityRunningRecordTest") { "init:libbeget_proxy", "init:libbegetutil", "ipc:ipc_core", + "ipc:ipc_single", "kv_store:distributeddata_inner", "memmgr:memmgrclient", "memory_utils:libmeminfo", diff --git a/test/unittest/ams_service_load_ability_process_test/BUILD.gn b/test/unittest/ams_service_load_ability_process_test/BUILD.gn index c9447b30d4..00db4f8e8e 100644 --- a/test/unittest/ams_service_load_ability_process_test/BUILD.gn +++ b/test/unittest/ams_service_load_ability_process_test/BUILD.gn @@ -104,6 +104,7 @@ ohos_unittest("AmsServiceLoadAbilityProcessTest") { "init:libbeget_proxy", "init:libbegetutil", "ipc:ipc_core", + "ipc:ipc_single", "kv_store:distributeddata_inner", "kv_store:distributeddata_mgr", "memmgr:memmgrclient", diff --git a/test/unittest/ams_service_startup_test/BUILD.gn b/test/unittest/ams_service_startup_test/BUILD.gn index c8d8d3e191..4ed5c72bc0 100644 --- a/test/unittest/ams_service_startup_test/BUILD.gn +++ b/test/unittest/ams_service_startup_test/BUILD.gn @@ -93,6 +93,7 @@ ohos_unittest("AmsServiceStartupTest") { "init:libbeget_proxy", "init:libbegetutil", "ipc:ipc_core", + "ipc:ipc_single", "kv_store:distributeddata_inner", "memmgr:memmgrclient", "memory_utils:libmeminfo", diff --git a/test/unittest/app_mgr_client_test/app_mgr_client_test.cpp b/test/unittest/app_mgr_client_test/app_mgr_client_test.cpp index f7a63b4bbe..945671a40f 100644 --- a/test/unittest/app_mgr_client_test/app_mgr_client_test.cpp +++ b/test/unittest/app_mgr_client_test/app_mgr_client_test.cpp @@ -1906,5 +1906,72 @@ HWTEST_F(AppMgrClientTest, AppMgrClient_UpdateFreezeExcludedPid_001, TestSize.Le int32_t profilerPid = 2; appMgrClient->UpdateFreezeExcludedPid(true, pid, profilerPid); } + +/** + * @tc.name: EnableDelayedProcessExit_001 + * @tc.desc: Enable delayed process exit with valid connection. + * @tc.type: FUNC + */ +HWTEST_F(AppMgrClientTest, EnableDelayedProcessExit_001, TestSize.Level2) +{ + auto appMgrClient = std::make_unique(); + EXPECT_NE(appMgrClient, nullptr); + + auto result = appMgrClient->ConnectAppMgrService(); + EXPECT_EQ(result, AppMgrResultCode::RESULT_OK); + + int32_t pid = 1234; + int32_t ret = appMgrClient->EnableDelayedProcessExit(pid, true); + EXPECT_NE(ret, AppMgrResultCode::ERROR_SERVICE_NOT_CONNECTED); +} + +/** + * @tc.name: EnableDelayedProcessExit_002 + * @tc.desc: Disable delayed process exit with valid connection. + * @tc.type: FUNC + */ +HWTEST_F(AppMgrClientTest, EnableDelayedProcessExit_002, TestSize.Level2) +{ + auto appMgrClient = std::make_unique(); + EXPECT_NE(appMgrClient, nullptr); + + auto result = appMgrClient->ConnectAppMgrService(); + EXPECT_EQ(result, AppMgrResultCode::RESULT_OK); + + int32_t pid = 1234; + int32_t ret = appMgrClient->EnableDelayedProcessExit(pid, false); + EXPECT_NE(ret, AppMgrResultCode::ERROR_SERVICE_NOT_CONNECTED); +} + +/** + * @tc.name: CancelDelayedExitTask_001 + * @tc.desc: Cancel delayed exit task with valid connection. + * @tc.type: FUNC + */ +HWTEST_F(AppMgrClientTest, CancelDelayedExitTask_001, TestSize.Level2) +{ + auto appMgrClient = std::make_unique(); + EXPECT_NE(appMgrClient, nullptr); + + auto result = appMgrClient->ConnectAppMgrService(); + EXPECT_EQ(result, AppMgrResultCode::RESULT_OK); + + int32_t pid = 1234; + appMgrClient->CancelDelayedExitTask(pid); +} + +/** + * @tc.name: CancelDelayedExitTask_002 + * @tc.desc: Cancel delayed exit task without connecting service (null service). + * @tc.type: FUNC + */ +HWTEST_F(AppMgrClientTest, CancelDelayedExitTask_002, TestSize.Level2) +{ + auto appMgrClient = std::make_unique(); + EXPECT_NE(appMgrClient, nullptr); + + int32_t pid = 1234; + appMgrClient->CancelDelayedExitTask(pid); +} } // namespace AppExecFwk } // namespace OHOS diff --git a/test/unittest/app_mgr_proxy_test/app_mgr_proxy_test.cpp b/test/unittest/app_mgr_proxy_test/app_mgr_proxy_test.cpp index 75d9ba41b1..0a415dfdb0 100644 --- a/test/unittest/app_mgr_proxy_test/app_mgr_proxy_test.cpp +++ b/test/unittest/app_mgr_proxy_test/app_mgr_proxy_test.cpp @@ -1564,5 +1564,75 @@ HWTEST_F(AppMgrProxyTest, UpdateFreezeExcludedPid_001, TestSize.Level1) appMgrProxy_->UpdateFreezeExcludedPid(isAdd, pid, profilerPid); EXPECT_EQ(mockAppMgrService_->code_, static_cast(AppMgrInterfaceCode::UPDATE_FREEZE_EXCLUDED_PID)); } + +/** + * @tc.name: EnableDelayedProcessExit_0100 + * @tc.desc: Test EnableDelayedProcessExit with successful SendRequest. + * @tc.type: FUNC + */ +HWTEST_F(AppMgrProxyTest, EnableDelayedProcessExit_0100, TestSize.Level1) +{ + EXPECT_CALL(*mockAppMgrService_, SendRequest(_, _, _, _)) + .Times(1) + .WillOnce([](uint32_t code, MessageParcel& data, MessageParcel& reply, MessageOption& option) { + reply.WriteInt32(ERR_OK); + return NO_ERROR; + }); + + auto ret = appMgrProxy_->EnableDelayedProcessExit(1234, true); + EXPECT_EQ(ret, ERR_OK); +} + +/** + * @tc.name: EnableDelayedProcessExit_0200 + * @tc.desc: Test EnableDelayedProcessExit with disabled flag. + * @tc.type: FUNC + */ +HWTEST_F(AppMgrProxyTest, EnableDelayedProcessExit_0200, TestSize.Level1) +{ + EXPECT_CALL(*mockAppMgrService_, SendRequest(_, _, _, _)) + .Times(1) + .WillOnce([](uint32_t code, MessageParcel& data, MessageParcel& reply, MessageOption& option) { + reply.WriteInt32(ERR_OK); + return NO_ERROR; + }); + + auto ret = appMgrProxy_->EnableDelayedProcessExit(1234, false); + EXPECT_EQ(ret, ERR_OK); +} + +/** + * @tc.name: EnableDelayedProcessExit_0300 + * @tc.desc: Test EnableDelayedProcessExit with failed SendRequest. + * @tc.type: FUNC + */ +HWTEST_F(AppMgrProxyTest, EnableDelayedProcessExit_0300, TestSize.Level1) +{ + EXPECT_CALL(*mockAppMgrService_, SendRequest(_, _, _, _)) + .Times(1) + .WillOnce([](uint32_t code, MessageParcel& data, MessageParcel& reply, MessageOption& option) { + reply.WriteInt32(ERR_INVALID_VALUE); + return NO_ERROR; + }); + + auto ret = appMgrProxy_->EnableDelayedProcessExit(-1, true); + EXPECT_EQ(ret, ERR_INVALID_VALUE); +} + +/** + * @tc.name: CancelDelayedExitTask_0100 + * @tc.desc: Test CancelDelayedExitTask with successful SendRequest. + * @tc.type: FUNC + */ +HWTEST_F(AppMgrProxyTest, CancelDelayedExitTask_0100, TestSize.Level1) +{ + EXPECT_CALL(*mockAppMgrService_, SendRequest(_, _, _, _)) + .Times(1) + .WillOnce([](uint32_t code, MessageParcel& data, MessageParcel& reply, MessageOption& option) { + return NO_ERROR; + }); + + appMgrProxy_->CancelDelayedExitTask(1234); +} } // namespace AppExecFwk } // namespace OHOS \ No newline at end of file diff --git a/test/unittest/app_mgr_service_event_handler_test/BUILD.gn b/test/unittest/app_mgr_service_event_handler_test/BUILD.gn index 0862b3c26c..2f5e6edb05 100644 --- a/test/unittest/app_mgr_service_event_handler_test/BUILD.gn +++ b/test/unittest/app_mgr_service_event_handler_test/BUILD.gn @@ -98,6 +98,7 @@ ohos_unittest("AMSEventHandlerTest") { "init:libbeget_proxy", "init:libbegetutil", "ipc:ipc_core", + "ipc:ipc_single", "kv_store:distributeddata_inner", "kv_store:distributeddata_mgr", "memmgr:memmgrclient", diff --git a/test/unittest/app_mgr_service_fourth_test/mock/src/mock_app_running_record.cpp b/test/unittest/app_mgr_service_fourth_test/mock/src/mock_app_running_record.cpp index 9ba731d178..e43d22c967 100644 --- a/test/unittest/app_mgr_service_fourth_test/mock/src/mock_app_running_record.cpp +++ b/test/unittest/app_mgr_service_fourth_test/mock/src/mock_app_running_record.cpp @@ -1010,6 +1010,16 @@ std::string AppRunningRecord::GetPreloadModuleName() const return preloadModuleName_; } +void AppRunningRecord::SetPreloadAbilityName(const std::string &abilityName) +{ + preloadAbilityName_ = abilityName; +} + +std::string AppRunningRecord::GetPreloadAbilityName() const +{ + return preloadAbilityName_; +} + void AppRunningRecord::SetPreloadState(PreloadState state) { preloadState_ = state; diff --git a/test/unittest/app_mgr_service_inner_eighth_test/BUILD.gn b/test/unittest/app_mgr_service_inner_eighth_test/BUILD.gn index f4b42dd299..16077331c1 100644 --- a/test/unittest/app_mgr_service_inner_eighth_test/BUILD.gn +++ b/test/unittest/app_mgr_service_inner_eighth_test/BUILD.gn @@ -86,6 +86,7 @@ ohos_unittest("app_mgr_service_inner_eighth_test") { "mock/src/mock_ipc_skeleton.cpp", "mock/src/mock_my_status.cpp", "mock/src/mock_os_account_manager_wrapper.cpp", + "mock/src/mock_res_sched_util.cpp", "mock/src/mock_parameters.cpp", "mock/src/mock_permission_verification.cpp", "mock/src/mock_remote_client_manager.cpp", @@ -135,6 +136,7 @@ ohos_unittest("app_mgr_service_inner_eighth_test") { "init:libbeget_proxy", "init:libbegetutil", "ipc:ipc_core", + "ipc:ipc_single", "json:nlohmann_json_static", "kv_store:distributeddata_inner", "kv_store:distributeddata_mgr", diff --git a/test/unittest/app_mgr_service_inner_eighth_test/app_mgr_service_inner_eighth_test.cpp b/test/unittest/app_mgr_service_inner_eighth_test/app_mgr_service_inner_eighth_test.cpp index f78d82b189..0437827ac0 100644 --- a/test/unittest/app_mgr_service_inner_eighth_test/app_mgr_service_inner_eighth_test.cpp +++ b/test/unittest/app_mgr_service_inner_eighth_test/app_mgr_service_inner_eighth_test.cpp @@ -29,6 +29,7 @@ #include "overlay_manager_proxy.h" #include "ability_connect_callback_stub.h" #include "app_scheduler_const.h" +#include "res_sched_util.h" using namespace testing; using namespace testing::ext; using namespace OHOS::AAFwk; @@ -3854,5 +3855,134 @@ HWTEST_F(AppMgrServiceInnerEighthTest, PostChildProcessAttachTimeoutTask_004, Te EXPECT_NE(ret, 0); TAG_LOGI(AAFwkTag::TEST, "PostChildProcessAttachTimeoutTask_004 end"); } + +/** + * @tc.name: ReportAbilityStartInfoForSpecified_001 + * @tc.desc: test appRecord null, launchMode SPECIFIED -> early return, mock not called + * @tc.type: FUNC + */ +HWTEST_F(AppMgrServiceInnerEighthTest, ReportAbilityStartInfoForSpecified_001, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "ReportAbilityStartInfoForSpecified_001 start"); + auto appMgrServiceInner = std::make_shared(); + AbilityInfo abilityInfo; + abilityInfo.launchMode = LaunchMode::SPECIFIED; + EXPECT_CALL(ResSchedUtil::GetInstance(), ReportAbilityStartInfoToRSS(testing::_, testing::_, + testing::_, testing::_, testing::_, testing::_)).Times(0); + appMgrServiceInner->ReportAbilityStartInfoForSpecified(nullptr, abilityInfo); + TAG_LOGI(AAFwkTag::TEST, "ReportAbilityStartInfoForSpecified_001 end"); +} + +/** + * @tc.name: ReportAbilityStartInfoForSpecified_002 + * @tc.desc: test appRecord valid, launchMode STANDARD -> early return, mock not called + * @tc.type: FUNC + */ +HWTEST_F(AppMgrServiceInnerEighthTest, ReportAbilityStartInfoForSpecified_002, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "ReportAbilityStartInfoForSpecified_002 start"); + auto appMgrServiceInner = std::make_shared(); + AAFwk::MyStatus::GetInstance().appRecordGetPid_ = 100; + auto appInfo = std::make_shared(); + auto appRecord = std::make_shared(appInfo, 100, "testProcess"); + AbilityInfo abilityInfo; + abilityInfo.launchMode = LaunchMode::STANDARD; + EXPECT_CALL(ResSchedUtil::GetInstance(), ReportAbilityStartInfoToRSS(testing::_, testing::_, + testing::_, testing::_, testing::_, testing::_)).Times(0); + appMgrServiceInner->ReportAbilityStartInfoForSpecified(appRecord, abilityInfo); + TAG_LOGI(AAFwkTag::TEST, "ReportAbilityStartInfoForSpecified_002 end"); +} + +/** + * @tc.name: ReportAbilityStartInfoForSpecified_003 + * @tc.desc: test SPECIFIED launchMode, pid > 0, PRELOAD_NONE, not cached -> mock called with correct params + * @tc.type: FUNC + */ +HWTEST_F(AppMgrServiceInnerEighthTest, ReportAbilityStartInfoForSpecified_003, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "ReportAbilityStartInfoForSpecified_003 start"); + auto appMgrServiceInner = std::make_shared(); + AAFwk::MyStatus::GetInstance().appRecordGetPid_ = 100; + auto appInfo = std::make_shared(); + auto appRecord = std::make_shared(appInfo, 100, "testProcess"); + appRecord->SetPreloadMode(AppExecFwk::PreloadMode::PRELOAD_NONE); + AbilityInfo abilityInfo; + abilityInfo.launchMode = LaunchMode::SPECIFIED; + abilityInfo.name = "testAbility"; + EXPECT_CALL(ResSchedUtil::GetInstance(), ReportAbilityStartInfoToRSS(testing::_, Eq(100), + Eq(true), Eq(false), Eq(static_cast(AppExecFwk::PreloadMode::PRELOAD_NONE)), + Eq(false))).Times(1); + appMgrServiceInner->ReportAbilityStartInfoForSpecified(appRecord, abilityInfo); + TAG_LOGI(AAFwkTag::TEST, "ReportAbilityStartInfoForSpecified_003 end"); +} + +/** + * @tc.name: ReportAbilityStartInfoForSpecified_004 + * @tc.desc: test SPECIFIED launchMode, preloadMode PRE_MAKE -> supportWarmSmartGC true + * @tc.type: FUNC + */ +HWTEST_F(AppMgrServiceInnerEighthTest, ReportAbilityStartInfoForSpecified_004, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "ReportAbilityStartInfoForSpecified_004 start"); + auto appMgrServiceInner = std::make_shared(); + AAFwk::MyStatus::GetInstance().appRecordGetPid_ = 100; + auto appInfo = std::make_shared(); + auto appRecord = std::make_shared(appInfo, 100, "testProcess"); + appRecord->SetPreloadMode(AppExecFwk::PreloadMode::PRE_MAKE); + AbilityInfo abilityInfo; + abilityInfo.launchMode = LaunchMode::SPECIFIED; + abilityInfo.name = "testAbility"; + EXPECT_CALL(ResSchedUtil::GetInstance(), ReportAbilityStartInfoToRSS(testing::_, Eq(100), + Eq(true), Eq(true), Eq(static_cast(AppExecFwk::PreloadMode::PRE_MAKE)), + Eq(false))).Times(1); + appMgrServiceInner->ReportAbilityStartInfoForSpecified(appRecord, abilityInfo); + TAG_LOGI(AAFwkTag::TEST, "ReportAbilityStartInfoForSpecified_004 end"); +} + +/** + * @tc.name: ReportAbilityStartInfoForSpecified_005 + * @tc.desc: test SPECIFIED launchMode, preloadMode PRELOAD_MODULE -> supportWarmSmartGC true + * @tc.type: FUNC + */ +HWTEST_F(AppMgrServiceInnerEighthTest, ReportAbilityStartInfoForSpecified_005, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "ReportAbilityStartInfoForSpecified_005 start"); + auto appMgrServiceInner = std::make_shared(); + AAFwk::MyStatus::GetInstance().appRecordGetPid_ = 200; + auto appInfo = std::make_shared(); + auto appRecord = std::make_shared(appInfo, 200, "testProcess"); + appRecord->SetPreloadMode(AppExecFwk::PreloadMode::PRELOAD_MODULE); + AbilityInfo abilityInfo; + abilityInfo.launchMode = LaunchMode::SPECIFIED; + abilityInfo.name = "testAbility"; + EXPECT_CALL(ResSchedUtil::GetInstance(), ReportAbilityStartInfoToRSS(testing::_, Eq(200), + Eq(true), Eq(true), Eq(static_cast(AppExecFwk::PreloadMode::PRELOAD_MODULE)), + Eq(false))).Times(1); + appMgrServiceInner->ReportAbilityStartInfoForSpecified(appRecord, abilityInfo); + TAG_LOGI(AAFwkTag::TEST, "ReportAbilityStartInfoForSpecified_005 end"); +} + +/** + * @tc.name: ReportAbilityStartInfoForSpecified_006 + * @tc.desc: test SPECIFIED launchMode, pid = 0 -> isColdStart false + * @tc.type: FUNC + */ +HWTEST_F(AppMgrServiceInnerEighthTest, ReportAbilityStartInfoForSpecified_006, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "ReportAbilityStartInfoForSpecified_006 start"); + auto appMgrServiceInner = std::make_shared(); + AAFwk::MyStatus::GetInstance().appRecordGetPid_ = 0; + auto appInfo = std::make_shared(); + auto appRecord = std::make_shared(appInfo, 0, "testProcess"); + appRecord->SetPreloadMode(AppExecFwk::PreloadMode::PRE_LAUNCH); + AbilityInfo abilityInfo; + abilityInfo.launchMode = LaunchMode::SPECIFIED; + abilityInfo.name = "testAbility"; + EXPECT_CALL(ResSchedUtil::GetInstance(), ReportAbilityStartInfoToRSS(testing::_, Eq(0), + Eq(false), Eq(false), Eq(static_cast(AppExecFwk::PreloadMode::PRE_LAUNCH)), + Eq(false))).Times(1); + appMgrServiceInner->ReportAbilityStartInfoForSpecified(appRecord, abilityInfo); + TAG_LOGI(AAFwkTag::TEST, "ReportAbilityStartInfoForSpecified_006 end"); +} } // namespace AppExecFwk } // namespace OHOS \ No newline at end of file diff --git a/test/unittest/app_mgr_service_inner_eighth_test/mock/include/res_sched_util.h b/test/unittest/app_mgr_service_inner_eighth_test/mock/include/res_sched_util.h new file mode 100644 index 0000000000..d356414bf8 --- /dev/null +++ b/test/unittest/app_mgr_service_inner_eighth_test/mock/include/res_sched_util.h @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2025 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 MOCK_RES_SCHED_UTIL_H +#define MOCK_RES_SCHED_UTIL_H + +#include +#include +#include +#include +#include + +namespace OHOS { +namespace AppExecFwk { +struct AbilityInfo; +} +namespace AAFwk { +using AbilityInfo = AppExecFwk::AbilityInfo; + +enum class LoadingStage : int32_t { + LOAD_BEGIN = 1, + LOAD_END, + FOREGROUND_BEGIN, + FOREGROUND_END, + CONNECT_BEGIN, + CONNECT_END, + DESTROY_BEGIN = 8, + DESTROY_END = 9, + PRELOAD_BEGIN, + PRELOAD_END, + PRE_LAUNCH_BEGIN, +}; + +class ResSchedUtil { +public: + ResSchedUtil() = default; + virtual ~ResSchedUtil() = default; + static ResSchedUtil &GetInstance(); + + MOCK_METHOD(void, ReportAbilityStartInfoToRSS, + (const AbilityInfo &abilityInfo, int32_t pid, bool isColdStart, + bool supportWarmSmartGC, int32_t preloadMode, bool isSuggestCache), ()); + MOCK_METHOD(void, ReportPreloadApplicationToRSS, + (const std::shared_ptr& abilityInfo, int32_t preloadMode), ()); + MOCK_METHOD(void, ReportAbilityAssociatedStartInfoToRSS, + (const AbilityInfo &abilityInfo, int64_t resSchedType, int32_t callerUid, int32_t callerPid), ()); + MOCK_METHOD(void, ReportEventToRSS, + (const int32_t uid, const std::string &bundleName, const std::string &reason, + const int32_t pid, const int32_t callerPid, bool isCreateFromImage), ()); + MOCK_METHOD(void, ReportUIExtensionProcColdStartToRss, + (int32_t extensionAbilityType, int hostPid, const std::string& hostBundleName, const std::string& bundleName, + const std::string& abilityName, const std::string& moduleName, bool isPreloadUIExtension), ()); + MOCK_METHOD(void, PromotePriorityToRSS, + (int32_t callerUid, int32_t callerPid, const std::string &targetBundleName, + int32_t targetUid, int32_t targetPid), ()); + MOCK_METHOD(std::string, GetThawReasonByAbilityType, (const AbilityInfo &abilityInfo), ()); + MOCK_METHOD(void, GetAllFrozenPidsFromRSS, (std::unordered_set &frozenPids), ()); + MOCK_METHOD(bool, CheckShouldForceKillProcess, (int32_t pid, const std::string& bundleName), ()); + inline void ReportLoadingEventToRss(LoadingStage stage, int32_t pid, int32_t uid, + int64_t timeDuration, int64_t abilityRecordId) {} + inline void ReportLoadingEventToRss(LoadingStage stage, int32_t pid, int32_t uid, int64_t timeDuration, + int64_t abilityRecordId, const std::unordered_map &extraParams) {} + MOCK_METHOD(std::unordered_set, GetNWebPreloadSet, (), (const)); + MOCK_METHOD(void, ReportAbilityIntentExemptionInfoToRSS, (int32_t callerUid, int32_t callerPid), ()); + MOCK_METHOD(void, ReportSubHealtyPerfInfoToRSS, (), ()); + MOCK_METHOD(void, ReportForkAllEventToRSS, + (int32_t imagePid, int32_t orginalPid, + std::shared_ptr abilityInfo, int32_t forkAllState), ()); +}; +} // namespace AAFwk +} // namespace OHOS +#endif // MOCK_RES_SCHED_UTIL_H diff --git a/test/unittest/app_mgr_service_inner_eighth_test/mock/src/mock_app_running_record.cpp b/test/unittest/app_mgr_service_inner_eighth_test/mock/src/mock_app_running_record.cpp index de2b13713e..11d9ef746e 100644 --- a/test/unittest/app_mgr_service_inner_eighth_test/mock/src/mock_app_running_record.cpp +++ b/test/unittest/app_mgr_service_inner_eighth_test/mock/src/mock_app_running_record.cpp @@ -1019,6 +1019,16 @@ std::string AppRunningRecord::GetPreloadModuleName() const return preloadModuleName_; } +void AppRunningRecord::SetPreloadAbilityName(const std::string &abilityName) +{ + preloadAbilityName_ = abilityName; +} + +std::string AppRunningRecord::GetPreloadAbilityName() const +{ + return preloadAbilityName_; +} + void AppRunningRecord::SetPreloadState(PreloadState state) { preloadState_ = state; diff --git a/test/unittest/app_mgr_service_inner_eighth_test/mock/src/mock_res_sched_util.cpp b/test/unittest/app_mgr_service_inner_eighth_test/mock/src/mock_res_sched_util.cpp new file mode 100644 index 0000000000..5fa65217d9 --- /dev/null +++ b/test/unittest/app_mgr_service_inner_eighth_test/mock/src/mock_res_sched_util.cpp @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2025 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 "res_sched_util.h" + +namespace OHOS { +namespace AAFwk { + +ResSchedUtil &ResSchedUtil::GetInstance() +{ + static ResSchedUtil instance; + return instance; +} + +} // namespace AAFwk +} // namespace OHOS diff --git a/test/unittest/app_mgr_service_inner_fifth_test/BUILD.gn b/test/unittest/app_mgr_service_inner_fifth_test/BUILD.gn index 4a551e2591..7c3a8fd0be 100644 --- a/test/unittest/app_mgr_service_inner_fifth_test/BUILD.gn +++ b/test/unittest/app_mgr_service_inner_fifth_test/BUILD.gn @@ -42,6 +42,7 @@ ohos_unittest("AppMgrServiceInnerFifthTest") { "${ability_runtime_services_path}/appmgr/src/advanced_security_mode_manager.cpp", "${ability_runtime_test_path}/mock/common/src/mock_native_token.cpp", "${ability_runtime_test_path}/mock/task_handler_wrap_mock/src/mock_task_handler_wrap.cpp", + "${ability_runtime_services_path}/appmgr/src/fork_image_info.cpp", "app_mgr_service_inner_fifth_test.cpp", ] diff --git a/test/unittest/app_mgr_service_inner_fifth_test/app_mgr_service_inner_fifth_test.cpp b/test/unittest/app_mgr_service_inner_fifth_test/app_mgr_service_inner_fifth_test.cpp index ed2b8bda3f..9219319599 100644 --- a/test/unittest/app_mgr_service_inner_fifth_test/app_mgr_service_inner_fifth_test.cpp +++ b/test/unittest/app_mgr_service_inner_fifth_test/app_mgr_service_inner_fifth_test.cpp @@ -901,5 +901,565 @@ HWTEST_F(AppMgrServiceInnerTest, HandleConfigurationChange_001, TestSize.Level2) } } } + +/** + * @tc.name: GetValidUserId_001 + * @tc.desc: GetValidUserId - input userId is not DEFAULT_INVAL_VALUE, return as-is + * @tc.type: FUNC + */ +HWTEST_F(AppMgrServiceInnerTest, GetValidUserId_001, TestSize.Level2) +{ + auto appMgrServiceInner = std::make_shared(); + int32_t result = appMgrServiceInner->GetValidUserId(100); + EXPECT_EQ(result, 100); +} + +/** + * @tc.name: GetValidUserId_002 + * @tc.desc: GetValidUserId - input is DEFAULT_INVAL_VALUE, uid maps to U0/U1, get foreground userId + * @tc.type: FUNC + */ +HWTEST_F(AppMgrServiceInnerTest, GetValidUserId_002, TestSize.Level2) +{ + auto appMgrServiceInner = std::make_shared(); + int32_t result = appMgrServiceInner->GetValidUserId(-1); + EXPECT_GE(result, 0); +} + +/** + * @tc.name: MakeImageInner_ImageExist_001 + * @tc.desc: MakeImageInner - image already exists in imageInfoMap_ + * @tc.type: FUNC + */ +HWTEST_F(AppMgrServiceInnerTest, MakeImageInner_ImageExist_001, TestSize.Level2) +{ + auto appMgrServiceInner = std::make_shared(); + AAFwk::Want want; + want.SetElementName("bundleName", "abilityName"); + AppMgrServiceInner::MakeImageRequest request { + .bundleName = "bundleName", + .abilityName = "abilityName", + .userId = 100, + .appCloneIndex = 0 + }; + auto imageInfo = std::make_shared(); + appMgrServiceInner->imageInfoMap_[request] = imageInfo; + + auto ret = appMgrServiceInner->MakeImageInner(want, 100, + AppExecFwk::PreloadMode::PRELOAD_MODULE, 0, nullptr); + EXPECT_EQ(ret, ImageError::ERR_IMAGE_INFO_EXIST); +} + +/** + * @tc.name: MakeImageInner_PreloadFailed_001 + * @tc.desc: MakeImageInner - PreloadApplication fails + * @tc.type: FUNC + */ +HWTEST_F(AppMgrServiceInnerTest, MakeImageInner_PreloadFailed_001, TestSize.Level2) +{ + auto appMgrServiceInner = std::make_shared(); + AAFwk::Want want; + want.SetElementName("bundleName", "abilityName"); + + auto ret = appMgrServiceInner->MakeImageInner(want, -1, + AppExecFwk::PreloadMode::PRELOAD_MODULE, 0, nullptr); + EXPECT_EQ(ret, ImageError::ERR_PRELOAD_FAILED); +} + +/** + * @tc.name: MakeImageInner_NotPreloadModule_001 + * @tc.desc: MakeImageInner - preloadMode is not PRELOAD_MODULE + * @tc.type: FUNC + */ +HWTEST_F(AppMgrServiceInnerTest, MakeImageInner_NotPreloadModule_001, TestSize.Level2) +{ + auto appMgrServiceInner = std::make_shared(); + AAFwk::Want want; + want.SetElementName("bundleName", "abilityName"); + + auto ret = appMgrServiceInner->MakeImageInner(want, 100, + AppExecFwk::PreloadMode::PRELOAD_NONE, 0, nullptr); + EXPECT_EQ(ret, ImageError::ERR_INVALID_PRELOAD_TYPE); +} + +/** + * @tc.name: DestroyImageByImageInfo_NullImageInfo_001 + * @tc.desc: DestroyImageByImageInfo - imageInfo is nullptr + * @tc.type: FUNC + */ +HWTEST_F(AppMgrServiceInnerTest, DestroyImageByImageInfo_NullImageInfo_001, TestSize.Level2) +{ + auto appMgrServiceInner = std::make_shared(); + auto ret = appMgrServiceInner->DestroyImageByImageInfo(nullptr); + EXPECT_EQ(ret, ImageError::ERR_IMAGE_INFO_NOT_EXIST); +} + +/** + * @tc.name: DestroyImageByImageInfo_ImagePidInvalid_001 + * @tc.desc: DestroyImageByImageInfo - imagePid < 0 + * @tc.type: FUNC + */ +HWTEST_F(AppMgrServiceInnerTest, DestroyImageByImageInfo_ImagePidInvalid_001, TestSize.Level2) +{ + auto appMgrServiceInner = std::make_shared(); + auto imageInfo = std::make_shared(); + imageInfo->imagePid = -1; + auto appRecord = std::make_shared( + std::make_shared(), 1, "processName"); + imageInfo->baseAppRecord = appRecord; + // imagePid < 0 → ERR_IMAGE_INFO_NOT_READY + auto ret = appMgrServiceInner->DestroyImageByImageInfo(imageInfo); + EXPECT_EQ(ret, ImageError::ERR_IMAGE_INFO_NOT_READY); +} + +/** + * @tc.name: DestroyImageByImageInfo_BaseAppRecordNull_001 + * @tc.desc: DestroyImageByImageInfo - baseAppRecord is nullptr + * @tc.type: FUNC + */ +HWTEST_F(AppMgrServiceInnerTest, DestroyImageByImageInfo_BaseAppRecordNull_001, TestSize.Level2) +{ + auto appMgrServiceInner = std::make_shared(); + auto imageInfo = std::make_shared(); + imageInfo->imagePid = 100; + imageInfo->baseAppRecord = nullptr; + auto ret = appMgrServiceInner->DestroyImageByImageInfo(imageInfo); + EXPECT_EQ(ret, ImageError::ERR_IMAGE_INFO_NOT_READY); +} + +/** + * @tc.name: DestroyImageForFault_NullAppRecord_001 + * @tc.desc: DestroyImageForFault - appRecord is nullptr + * @tc.type: FUNC + */ +HWTEST_F(AppMgrServiceInnerTest, DestroyImageForFault_NullAppRecord_001, TestSize.Level2) +{ + auto appMgrServiceInner = std::make_shared(); + auto ret = appMgrServiceInner->DestroyImageForFault(nullptr); + EXPECT_EQ(ret, ImageError::ERR_INNER); +} + +/** + * @tc.name: DestroyImageForFault_ImageInfoNotExist_001 + * @tc.desc: DestroyImageForFault - imageInfo not found in map + * @tc.type: FUNC + */ +HWTEST_F(AppMgrServiceInnerTest, DestroyImageForFault_ImageInfoNotExist_001, TestSize.Level2) +{ + auto appMgrServiceInner = std::make_shared(); + auto appRecord = std::make_shared( + std::make_shared(), 1, "processName"); + auto ret = appMgrServiceInner->DestroyImageForFault(appRecord); + EXPECT_EQ(ret, ImageError::ERR_IMAGE_INFO_NOT_EXIST); +} + +/** + * @tc.name: HandleForkAll_NullAppRecord_001 + * @tc.desc: HandleForkAll - appRecord not found by pid, returns -1 + * @tc.type: FUNC + */ +HWTEST_F(AppMgrServiceInnerTest, HandleForkAll_NullAppRecord_001, TestSize.Level2) +{ + auto appMgrServiceInner = std::make_shared(); + int32_t ret = appMgrServiceInner->HandleForkAll(99999); + EXPECT_EQ(ret, -1); +} + +/** + * @tc.name: HandleForkAllInner_AppRecordNull_001 + * @tc.desc: HandleForkAllInner - appRecord is nullptr + * @tc.type: FUNC + */ +HWTEST_F(AppMgrServiceInnerTest, HandleForkAllInner_AppRecordNull_001, TestSize.Level2) +{ + auto appMgrServiceInner = std::make_shared(); + auto ret = appMgrServiceInner->HandleForkAllInner(nullptr, 100); + EXPECT_EQ(ret, ImageError::ERR_INNER); +} + +/** + * @tc.name: HandleForkAllInner_StateNotReady_001 + * @tc.desc: HandleForkAllInner - makeImageState is not MAKE_PRELOAD_FINISH + * @tc.type: FUNC + */ +HWTEST_F(AppMgrServiceInnerTest, HandleForkAllInner_StateNotReady_001, TestSize.Level2) +{ + auto appMgrServiceInner = std::make_shared(); + auto appRecord = std::make_shared( + std::make_shared(), 1, "processName"); + appRecord->SetMakeImageState(MakeImageState::NONE); + auto ret = appMgrServiceInner->HandleForkAllInner(appRecord, 100); + EXPECT_EQ(ret, ImageError::ERR_TEMPLATE_HAS_BEEN_USED); +} + +/** + * @tc.name: IsImageInfoExist_NullAppRecord_001 + * @tc.desc: IsImageInfoExist - appRecord is nullptr + * @tc.type: FUNC + */ +HWTEST_F(AppMgrServiceInnerTest, IsImageInfoExist_NullAppRecord_001, TestSize.Level2) +{ + auto appMgrServiceInner = std::make_shared(); + bool ret = appMgrServiceInner->IsImageInfoExist(nullptr); + EXPECT_FALSE(ret); +} + +/** + * @tc.name: IsImageInfoExist_NotFound_001 + * @tc.desc: IsImageInfoExist - image not found in map + * @tc.type: FUNC + */ +HWTEST_F(AppMgrServiceInnerTest, IsImageInfoExist_NotFound_001, TestSize.Level2) +{ + auto appMgrServiceInner = std::make_shared(); + bool ret = appMgrServiceInner->IsImageInfoExist("bundle", "ability", 100, 0); + EXPECT_FALSE(ret); +} + +/** + * @tc.name: IsImageInfoExist_Found_001 + * @tc.desc: IsImageInfoExist - image found in map + * @tc.type: FUNC + */ +HWTEST_F(AppMgrServiceInnerTest, IsImageInfoExist_Found_001, TestSize.Level2) +{ + auto appMgrServiceInner = std::make_shared(); + AppMgrServiceInner::MakeImageRequest request { + .bundleName = "bundle", + .abilityName = "ability", + .userId = 100, + .appCloneIndex = 0 + }; + appMgrServiceInner->imageInfoMap_[request] = std::make_shared(); + bool ret = appMgrServiceInner->IsImageInfoExist("bundle", "ability", 100, 0); + EXPECT_TRUE(ret); +} + +/** + * @tc.name: GetImageInfoByRecord_NullAppRecord_001 + * @tc.desc: GetImageInfo - appRecord is nullptr + * @tc.type: FUNC + */ +HWTEST_F(AppMgrServiceInnerTest, GetImageInfoByRecord_NullAppRecord_001, TestSize.Level2) +{ + auto appMgrServiceInner = std::make_shared(); + auto ret = appMgrServiceInner->GetImageInfo(nullptr); + EXPECT_EQ(ret, nullptr); +} + +/** + * @tc.name: GetImageInfoByRecord_Valid_001 + * @tc.desc: GetImageInfo - valid appRecord found in map + * @tc.type: FUNC + */ +HWTEST_F(AppMgrServiceInnerTest, GetImageInfoByRecord_Valid_001, TestSize.Level2) +{ + auto appMgrServiceInner = std::make_shared(); + AppMgrServiceInner::MakeImageRequest request { + .bundleName = "bundleName", + .abilityName = "abilityName", + .userId = 100, + .appCloneIndex = 0 + }; + auto imageInfo = std::make_shared(); + appMgrServiceInner->imageInfoMap_[request] = imageInfo; + + auto appRecord = std::make_shared( + std::make_shared(), 1, "processName"); + auto ret = appMgrServiceInner->GetImageInfo(appRecord); + EXPECT_EQ(ret, nullptr); // preload ability name won't match +} + +/** + * @tc.name: GetImageInfoByRemoteObject_NotFound_001 + * @tc.desc: GetImageInfoByRemoteObject - no matching remote object + * @tc.type: FUNC + */ +HWTEST_F(AppMgrServiceInnerTest, GetImageInfoByRemoteObject_NotFound_001, TestSize.Level2) +{ + auto appMgrServiceInner = std::make_shared(); + auto ret = appMgrServiceInner->GetImageInfoByRemoteObject(nullptr); + EXPECT_EQ(ret, nullptr); +} + +/** + * @tc.name: GetImageInfoByRemoteObject_EmptyMap_001 + * @tc.desc: GetImageInfoByRemoteObject - empty map returns nullptr + * @tc.type: FUNC + */ +HWTEST_F(AppMgrServiceInnerTest, GetImageInfoByRemoteObject_EmptyMap_001, TestSize.Level2) +{ + auto appMgrServiceInner = std::make_shared(); + auto mockObj = sptr(new (std::nothrow) MockAbilityToken()); + auto ret = appMgrServiceInner->GetImageInfoByRemoteObject(mockObj); + EXPECT_EQ(ret, nullptr); +} + +/** + * @tc.name: IsImageInfoMatched_NullImageInfo_001 + * @tc.desc: IsImageInfoMatched - imageInfo is nullptr + * @tc.type: FUNC + */ +HWTEST_F(AppMgrServiceInnerTest, IsImageInfoMatched_NullImageInfo_001, TestSize.Level2) +{ + auto appMgrServiceInner = std::make_shared(); + bool ret = appMgrServiceInner->IsImageInfoMatched(nullptr, 0, "", "", "", ""); + EXPECT_FALSE(ret); +} + +/** + * @tc.name: IsImageInfoMatched_ImagePidInvalid_001 + * @tc.desc: IsImageInfoMatched - imagePid <= 0 + * @tc.type: FUNC + */ +HWTEST_F(AppMgrServiceInnerTest, IsImageInfoMatched_ImagePidInvalid_001, TestSize.Level2) +{ + auto appMgrServiceInner = std::make_shared(); + auto imageInfo = std::make_shared(); + imageInfo->imagePid = 0; + bool ret = appMgrServiceInner->IsImageInfoMatched(imageInfo, 0, "", "", "", ""); + EXPECT_FALSE(ret); +} + +/** + * @tc.name: IsImageInfoMatched_NullAppRecord_001 + * @tc.desc: IsImageInfoMatched - baseAppRecord is nullptr + * @tc.type: FUNC + */ +HWTEST_F(AppMgrServiceInnerTest, IsImageInfoMatched_NullAppRecord_001, TestSize.Level2) +{ + auto appMgrServiceInner = std::make_shared(); + auto imageInfo = std::make_shared(); + imageInfo->imagePid = 100; + imageInfo->baseAppRecord = nullptr; + bool ret = appMgrServiceInner->IsImageInfoMatched(imageInfo, 0, "", "", "", ""); + EXPECT_FALSE(ret); +} + +/** + * @tc.name: IsImageInfoMatched_ProcessNameMismatch_001 + * @tc.desc: IsImageInfoMatched - process name does not match + * @tc.type: FUNC + */ +HWTEST_F(AppMgrServiceInnerTest, IsImageInfoMatched_ProcessNameMismatch_001, TestSize.Level2) +{ + auto appMgrServiceInner = std::make_shared(); + auto imageInfo = std::make_shared(); + imageInfo->imagePid = 100; + auto appRecord = std::make_shared( + std::make_shared(), 1, "processName"); + imageInfo->baseAppRecord = appRecord; + bool ret = appMgrServiceInner->IsImageInfoMatched(imageInfo, 0, "differentProcess", "", "", ""); + EXPECT_FALSE(ret); +} + +/** + * @tc.name: CreateAppRunningRecordFromImageInfo_Null_001 + * @tc.desc: CreateAppRunningRecordFromImageInfo - imageInfo is nullptr + * @tc.type: FUNC + */ +HWTEST_F(AppMgrServiceInnerTest, CreateAppRunningRecordFromImageInfo_Null_001, TestSize.Level2) +{ + auto appMgrServiceInner = std::make_shared(); + auto ret = appMgrServiceInner->CreateAppRunningRecordFromImageInfo(nullptr); + EXPECT_EQ(ret, nullptr); +} + +/** + * @tc.name: CreateAppRunningRecordFromImageInfo_NoBaseRecord_001 + * @tc.desc: CreateAppRunningRecordFromImageInfo - baseAppRecord is nullptr + * @tc.type: FUNC + */ +HWTEST_F(AppMgrServiceInnerTest, CreateAppRunningRecordFromImageInfo_NoBaseRecord_001, TestSize.Level2) +{ + auto appMgrServiceInner = std::make_shared(); + auto imageInfo = std::make_shared(); + imageInfo->baseAppRecord = nullptr; + auto ret = appMgrServiceInner->CreateAppRunningRecordFromImageInfo(imageInfo); + EXPECT_EQ(ret, nullptr); +} + +/** + * @tc.name: TryToUseImageInfo_NullParams_001 + * @tc.desc: TryToUseImageInfo - appRunningManager_ is null (first null check) + * @tc.type: FUNC + */ +HWTEST_F(AppMgrServiceInnerTest, TryToUseImageInfo_NullParams_001, TestSize.Level2) +{ + auto appMgrServiceInner = std::make_shared(); + std::shared_ptr appRecord = nullptr; + auto ret = appMgrServiceInner->TryToUseImageInfo(nullptr, nullptr, nullptr, + "callerKey", 0, "process", "instanceKey", "", "", appRecord); + EXPECT_EQ(ret, ERR_OK); +} + +/** + * @tc.name: TryToUseImageInfo_ImageInfoNotFound_001 + * @tc.desc: TryToUseImageInfo - imageInfo not found in map + * @tc.type: FUNC + */ +HWTEST_F(AppMgrServiceInnerTest, TryToUseImageInfo_ImageInfoNotFound_001, TestSize.Level2) +{ + auto appMgrServiceInner = std::make_shared(); + auto abilityInfo = std::make_shared(); + abilityInfo->name = "testAbility"; + auto appInfo = std::make_shared(); + appInfo->bundleName = "testBundle"; + appInfo->uid = 0; + std::shared_ptr appRecord = nullptr; + auto ret = appMgrServiceInner->TryToUseImageInfo(abilityInfo, appInfo, nullptr, + "callerKey", 0, "process", "instanceKey", "", "", appRecord); + EXPECT_EQ(ret, ERR_OK); +} + +/** + * @tc.name: TryToUseImageInfo_ImageInfoNotMatched_001 + * @tc.desc: TryToUseImageInfo - IsImageInfoMatched returns false + * @tc.type: FUNC + */ +HWTEST_F(AppMgrServiceInnerTest, TryToUseImageInfo_ImageInfoNotMatched_001, TestSize.Level2) +{ + auto appMgrServiceInner = std::make_shared(); + auto abilityInfo = std::make_shared(); + abilityInfo->name = "testAbility"; + auto appInfo = std::make_shared(); + appInfo->bundleName = "testBundle"; + appInfo->uid = 0; + AppMgrServiceInner::MakeImageRequest request { + .bundleName = "testBundle", + .abilityName = "testAbility", + .userId = 0, + .appCloneIndex = 0 + }; + auto imageInfo = std::make_shared(); + imageInfo->imagePid = 100; + appMgrServiceInner->imageInfoMap_[request] = imageInfo; + std::shared_ptr appRecord = nullptr; + auto ret = appMgrServiceInner->TryToUseImageInfo(abilityInfo, appInfo, + sptr(new (std::nothrow) MockAbilityToken()), + "callerKey", 0, "differentProcess", "instanceKey", "", "", appRecord); + EXPECT_EQ(ret, ERR_OK); +} + +/** + * @tc.name: ProcessKia_NotKia_001 + * @tc.desc: ProcessKia - isKia is false, early return ERR_OK + * @tc.type: FUNC + */ +HWTEST_F(AppMgrServiceInnerTest, ProcessKia_NotKia_001, TestSize.Level2) +{ + auto appMgrServiceInner = std::make_shared(); + int32_t ret = appMgrServiceInner->ProcessKia(false, nullptr, "", false); + EXPECT_EQ(ret, ERR_OK); +} + +/** + * @tc.name: ProcessKia_NullAppRecord_001 + * @tc.desc: ProcessKia - isKia false regardless of appRecord + * @tc.type: FUNC + */ +HWTEST_F(AppMgrServiceInnerTest, ProcessKia_NullAppRecord_001, TestSize.Level2) +{ + auto appMgrServiceInner = std::make_shared(); + // isKia=false always returns ERR_OK regardless of AppUtils::IsStartOptionsWithAnimation + int32_t ret = appMgrServiceInner->ProcessKia(false, nullptr, "watermark", true); + EXPECT_EQ(ret, ERR_OK); +} + +/** + * @tc.name: ProcessKia_Valid_001 + * @tc.desc: ProcessKia - isKia true with valid appRecord + * @tc.type: FUNC + */ +HWTEST_F(AppMgrServiceInnerTest, ProcessKia_Valid_001, TestSize.Level2) +{ + auto appMgrServiceInner = std::make_shared(); + auto appRecord = std::make_shared( + std::make_shared(), 1, "processName"); + int32_t ret = appMgrServiceInner->ProcessKia(true, appRecord, "watermark", true); + EXPECT_EQ(ret, ERR_OK); +} + +/** + * @tc.name: GetBackgroundAppInfo_NullSession_001 + * @tc.desc: GetBackgroundAppInfo - SessionManager returns null + * @tc.type: FUNC + */ +HWTEST_F(AppMgrServiceInnerTest, GetBackgroundAppInfo_NullSession_001, TestSize.Level2) +{ + auto appMgrServiceInner = std::make_shared(); + std::vector allowList; + auto ret = appMgrServiceInner->GetBackgroundAppInfo(allowList); + EXPECT_TRUE(ret.empty()); +} + +/** + * @tc.name: GetRenderProcessTerminationStatus_NoAppMgr_001 + * @tc.desc: GetRenderProcessTerminationStatus - appRunningManager_ is null + * @tc.type: FUNC + */ +HWTEST_F(AppMgrServiceInnerTest, GetRenderProcessTerminationStatus_NoAppMgr_001, TestSize.Level2) +{ + auto appMgrServiceInner = std::make_shared(); + int status = 0; + int32_t ret = appMgrServiceInner->GetRenderProcessTerminationStatus(100, status); + EXPECT_EQ(ret, ERR_INVALID_VALUE); +} + +/** + * @tc.name: GetRenderProcessTerminationStatus_HostRecordNull_001 + * @tc.desc: GetRenderProcessTerminationStatus - hostRecord not found + * @tc.type: FUNC + */ +HWTEST_F(AppMgrServiceInnerTest, GetRenderProcessTerminationStatus_HostRecordNull_001, TestSize.Level2) +{ + auto appMgrServiceInner = std::make_shared(); + auto appRunningManager = std::make_shared(); + appMgrServiceInner->appRunningManager_ = appRunningManager; + int status = 0; + int32_t ret = appMgrServiceInner->GetRenderProcessTerminationStatus(99999, status); + EXPECT_EQ(ret, ERR_INVALID_VALUE); +} + +/** + * @tc.name: IsAppRunning_InvalidCloneIndex_001 + * @tc.desc: IsAppRunning - appCloneIndex out of valid range, clamped to -1 + * @tc.type: FUNC + */ +HWTEST_F(AppMgrServiceInnerTest, IsAppRunning_InvalidCloneIndex_001, TestSize.Level2) +{ + auto appMgrServiceInner = std::make_shared(); + bool isRunning = false; + // callingUid likely != FOUNDATION_UID in test → first check fails + int32_t ret = appMgrServiceInner->IsAppRunning("testBundle", -2, 100, isRunning); + EXPECT_EQ(ret, ERR_PERMISSION_DENIED); +} + +/** + * @tc.name: CreateAbilityInfo_NullBundleMgr_001 + * @tc.desc: CreateAbilityInfo - bundleMgrHelper is null, early return false + * @tc.type: FUNC + */ +HWTEST_F(AppMgrServiceInnerTest, CreateAbilityInfo_NullBundleMgr_001, TestSize.Level2) +{ + auto appMgrServiceInner = std::make_shared(); + appMgrServiceInner->remoteClientManager_ = std::make_shared(); + AAFwk::Want want; + AbilityInfo abilityInfo; + bool ret = appMgrServiceInner->CreateAbilityInfo(want, abilityInfo); + EXPECT_FALSE(ret); +} + +/** + * @tc.name: AllowChildProcessInMultiProcessFeatureApp_NullAppRecord_001 + * @tc.desc: AllowChildProcessInMultiProcessFeatureApp - appRecord is nullptr + * @tc.type: FUNC + */ +HWTEST_F(AppMgrServiceInnerTest, AllowChildProcessInMultiProcessFeatureApp_NullAppRecord_001, TestSize.Level2) +{ + auto appMgrServiceInner = std::make_shared(); + bool ret = appMgrServiceInner->AllowChildProcessInMultiProcessFeatureApp(nullptr); + EXPECT_FALSE(ret); +} } // namespace AppExecFwk } // namespace OHOS diff --git a/test/unittest/app_mgr_service_inner_fourth_test/BUILD.gn b/test/unittest/app_mgr_service_inner_fourth_test/BUILD.gn index 53b7850edc..131383cc95 100644 --- a/test/unittest/app_mgr_service_inner_fourth_test/BUILD.gn +++ b/test/unittest/app_mgr_service_inner_fourth_test/BUILD.gn @@ -42,6 +42,7 @@ ohos_unittest("AppMgrServiceInnerFourthTest") { "mock/src/mock_ipc_skeleton.cpp", "mock/src/mock_my_flag.cpp", "mock/src/mock_permission_verification.cpp", + "${ability_runtime_services_path}/appmgr/src/fork_image_info.cpp", ] configs = [ "${ability_runtime_services_path}/appmgr:appmgr_config" ] diff --git a/test/unittest/app_mgr_service_inner_fourth_test/app_mgr_service_inner_fourth_test.cpp b/test/unittest/app_mgr_service_inner_fourth_test/app_mgr_service_inner_fourth_test.cpp index bb0e9400f6..dcccfe90dd 100644 --- a/test/unittest/app_mgr_service_inner_fourth_test/app_mgr_service_inner_fourth_test.cpp +++ b/test/unittest/app_mgr_service_inner_fourth_test/app_mgr_service_inner_fourth_test.cpp @@ -679,7 +679,7 @@ HWTEST_F(AppMgrServiceInnerFourthTest, IsImageInfoExist_ShouldReturnFalseWhenIma std::string bundleName = "com.acts.imagetest"; int32_t userId = 1; int32_t appIndex = 0; - bool exist = appMgrServiceInner->IsImageInfoExist(bundleName, userId, appIndex); + bool exist = appMgrServiceInner->IsImageInfoExist(bundleName, "", userId, appIndex); EXPECT_FALSE(exist); TAG_LOGI(AAFwkTag::TEST, "IsImageInfoExist_ShouldReturnFalseWhenImageInfoNotExist end"); } @@ -699,7 +699,7 @@ HWTEST_F(AppMgrServiceInnerFourthTest, IsImageInfoExist_ShouldReturnTrueWhenImag int32_t appIndex = 0; PreloadRequest preloadRequest; appMgrServiceInner->PreAddImageInfo(bundleName, userId, appIndex, nullptr, preloadRequest); - bool exist = appMgrServiceInner->IsImageInfoExist(bundleName, userId, appIndex); + bool exist = appMgrServiceInner->IsImageInfoExist(bundleName, "", userId, appIndex); EXPECT_TRUE(exist); TAG_LOGI(AAFwkTag::TEST, "IsImageInfoExist_ShouldReturnTrueWhenImageInfoExist end"); } @@ -718,13 +718,13 @@ HWTEST_F(AppMgrServiceInnerFourthTest, RemoveImageInfo_ShouldRemoveImageWhenKeyI int32_t userId = 1; int32_t appIndex = 0; PreloadRequest preloadRequest; - appMgrServiceInner->RemoveImageInfo(bundleName, userId, appIndex); - EXPECT_FALSE(appMgrServiceInner->IsImageInfoExist(bundleName, userId, appIndex)); + appMgrServiceInner->RemoveImageInfo(bundleName, "", userId, appIndex); + EXPECT_FALSE(appMgrServiceInner->IsImageInfoExist(bundleName, "", userId, appIndex)); appMgrServiceInner->PreAddImageInfo(bundleName, userId, appIndex, nullptr, preloadRequest); - bool exist = appMgrServiceInner->IsImageInfoExist(bundleName, userId, appIndex); + bool exist = appMgrServiceInner->IsImageInfoExist(bundleName, "", userId, appIndex); EXPECT_TRUE(exist); - appMgrServiceInner->RemoveImageInfo(bundleName, userId, appIndex); - EXPECT_FALSE(appMgrServiceInner->IsImageInfoExist(bundleName, userId, appIndex)); + appMgrServiceInner->RemoveImageInfo(bundleName, "", userId, appIndex); + EXPECT_FALSE(appMgrServiceInner->IsImageInfoExist(bundleName, "", userId, appIndex)); TAG_LOGI(AAFwkTag::TEST, "RemoveImageInfo_ShouldRemoveImageWhenKeyIsExist end"); } @@ -774,7 +774,7 @@ HWTEST_F(AppMgrServiceInnerFourthTest, GetImageInfoByUid_ShouldReturnNullptrWhen }; appMgrServiceInner->imageInfoMap_.emplace(request, nullptr); int32_t uid = 1; - EXPECT_EQ(appMgrServiceInner->GetImageInfoByUid(uid), nullptr); + EXPECT_EQ(appMgrServiceInner->GetImageInfosByUid(uid).size(), 0); TAG_LOGI(AAFwkTag::TEST, "GetImageInfoByUid_ShouldReturnNullptrWhenForkImageInfoIsNullptr end"); } @@ -802,7 +802,7 @@ HWTEST_F(AppMgrServiceInnerFourthTest, UpdateImageInfo_ShouldDoNothingWhenReques appRecord->SetAppIndex(appIndex); int32_t imagePid = 100; appMgrServiceInner->UpdateImageInfo(imagePid, 0, appRecord); - auto imageInfo = appMgrServiceInner->GetImageInfo(bundleName, userId, appIndex); + auto imageInfo = appMgrServiceInner->GetImageInfo(bundleName, "", userId, appIndex); EXPECT_NE(imageInfo, nullptr); EXPECT_NE(imageInfo->baseAppRecord, appRecord); TAG_LOGI(AAFwkTag::TEST, "UpdateImageInfo_ShouldDoNothingWhenRequestNotMatch end"); @@ -832,7 +832,7 @@ HWTEST_F(AppMgrServiceInnerFourthTest, UpdateImageInfo_ShouldUpdateWhenAppRecord appRecord->SetAppIndex(appIndex); int32_t imagePid = 100; appMgrServiceInner->UpdateImageInfo(imagePid, 0, appRecord); - auto imageInfo = appMgrServiceInner->GetImageInfo(bundleName, userId, appIndex); + auto imageInfo = appMgrServiceInner->GetImageInfo(bundleName, "", userId, appIndex); EXPECT_NE(imageInfo, nullptr); EXPECT_EQ(imageInfo->baseAppRecord, appRecord); EXPECT_EQ(imageInfo->imagePid, imagePid); @@ -920,8 +920,8 @@ HWTEST_F(AppMgrServiceInnerFourthTest, HandleMakeImageFailed_ShouldDonothingWhen int32_t userId = 100; int32_t appIndex = 0; ImageError imageErr = ImageError::ERR_OK; - appMgrServiceInner->HandleMakeImageFailed(bundleName, userId, appIndex, imageErr); - EXPECT_EQ(appMgrServiceInner->GetImageInfo(bundleName, userId, appIndex), nullptr); + appMgrServiceInner->HandleMakeImageFailed(bundleName, "", userId, appIndex, imageErr); + EXPECT_EQ(appMgrServiceInner->GetImageInfo(bundleName, "", userId, appIndex), nullptr); TAG_LOGI(AAFwkTag::TEST, "HandleMakeImageFailed_ShouldDonothingWhenImageInfoNotExist end"); } @@ -1027,5 +1027,410 @@ HWTEST_F(AppMgrServiceInnerFourthTest, NotifyImageOperationFailed_ShouldReturnEr EXPECT_EQ(appMgrServiceInner->NotifyImageOperationFailed(errHandler, errCode), ERR_OK); TAG_LOGI(AAFwkTag::TEST, "NotifyImageOperationFailed_ShouldReturnErrOkWhenErrorHandleIsNullptr end"); } + +/** + * @tc.name: FindImageInfo_ShouldReturnNullptrWhenImageInfoNotExist + * @tc.desc: Test FindImageInfo + * @tc.type: FUNC + */ +HWTEST_F(AppMgrServiceInnerFourthTest, FindImageInfo_ShouldReturnNullptrWhenImageInfoNotExist, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "FindImageInfo_ShouldReturnNullptrWhenImageInfoNotExist start"); + auto appMgrServiceInner = std::make_shared(); + EXPECT_NE(appMgrServiceInner, nullptr); + std::string bundleName = "com.acts.imagetest"; + std::string abilityName = "TestAbility"; + int32_t userId = 100; + int32_t appIndex = 0; + auto imageInfo = appMgrServiceInner->FindImageInfo(bundleName, abilityName, userId, appIndex); + EXPECT_EQ(imageInfo, nullptr); + TAG_LOGI(AAFwkTag::TEST, "FindImageInfo_ShouldReturnNullptrWhenImageInfoNotExist end"); +} + +/** + * @tc.name: FindImageInfo_ShouldReturnImageInfoWhenExist + * @tc.desc: Test FindImageInfo + * @tc.type: FUNC + */ +HWTEST_F(AppMgrServiceInnerFourthTest, FindImageInfo_ShouldReturnImageInfoWhenExist, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "FindImageInfo_ShouldReturnImageInfoWhenExist start"); + auto appMgrServiceInner = std::make_shared(); + EXPECT_NE(appMgrServiceInner, nullptr); + std::string bundleName = "com.acts.imagetest"; + std::string abilityName = "TestAbility"; + int32_t userId = 100; + int32_t appIndex = 0; + PreloadRequest preloadRequest; + appMgrServiceInner->PreAddImageInfo(bundleName, userId, appIndex, nullptr, preloadRequest); + auto imageInfo = appMgrServiceInner->FindImageInfo(bundleName, "", userId, appIndex); + EXPECT_NE(imageInfo, nullptr); + TAG_LOGI(AAFwkTag::TEST, "FindImageInfo_ShouldReturnImageInfoWhenExist end"); +} + +/** + * @tc.name: GetImageInfoByAppRecord_ShouldReturnNullptrWhenAppRecordIsNullptr + * @tc.desc: Test GetImageInfo with AppRunningRecord parameter + * @tc.type: FUNC + */ +HWTEST_F(AppMgrServiceInnerFourthTest, GetImageInfoByAppRecord_ShouldReturnNullptrWhenAppRecordIsNullptr, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "GetImageInfoByAppRecord_ShouldReturnNullptrWhenAppRecordIsNullptr start"); + auto appMgrServiceInner = std::make_shared(); + EXPECT_NE(appMgrServiceInner, nullptr); + auto imageInfo = appMgrServiceInner->GetImageInfo(nullptr); + EXPECT_EQ(imageInfo, nullptr); + TAG_LOGI(AAFwkTag::TEST, "GetImageInfoByAppRecord_ShouldReturnNullptrWhenAppRecordIsNullptr end"); +} + +/** + * @tc.name: GetImageInfoByAppRecord_ShouldReturnNullptrWhenImageInfoNotExist + * @tc.desc: Test GetImageInfo with AppRunningRecord parameter + * @tc.type: FUNC + */ +HWTEST_F(AppMgrServiceInnerFourthTest, GetImageInfoByAppRecord_ShouldReturnNullptrWhenImageInfoNotExist, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "GetImageInfoByAppRecord_ShouldReturnNullptrWhenImageInfoNotExist start"); + auto appMgrServiceInner = std::make_shared(); + EXPECT_NE(appMgrServiceInner, nullptr); + auto appRecord = std::make_shared(applicationInfo_, APP_DEBUG_INFO_UID, "PROCESS_NAME"); + auto imageInfo = appMgrServiceInner->GetImageInfo(appRecord); + EXPECT_EQ(imageInfo, nullptr); + TAG_LOGI(AAFwkTag::TEST, "GetImageInfoByAppRecord_ShouldReturnNullptrWhenImageInfoNotExist end"); +} + +/** + * @tc.name: GetImageInfoByAppRecord_ShouldReturnImageInfoWhenExist + * @tc.desc: Test GetImageInfo with AppRunningRecord parameter + * @tc.type: FUNC + */ +HWTEST_F(AppMgrServiceInnerFourthTest, GetImageInfoByAppRecord_ShouldReturnImageInfoWhenExist, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "GetImageInfoByAppRecord_ShouldReturnImageInfoWhenExist start"); + auto appMgrServiceInner = std::make_shared(); + EXPECT_NE(appMgrServiceInner, nullptr); + std::string bundleName = "com.acts.imagetest"; + int32_t userId = 100; + int32_t appIndex = 0; + PreloadRequest preloadRequest; + appMgrServiceInner->PreAddImageInfo(bundleName, userId, appIndex, nullptr, preloadRequest); + + auto appInfo = std::make_shared(); + appInfo->bundleName = bundleName; + auto appRecord = std::make_shared(appInfo, APP_DEBUG_INFO_UID, "PROCESS_NAME"); + appRecord->SetUid(userId * BASE_USER_RANGE); + appRecord->SetAppIndex(appIndex); + + auto imageInfo = appMgrServiceInner->GetImageInfo(appRecord); + EXPECT_NE(imageInfo, nullptr); + TAG_LOGI(AAFwkTag::TEST, "GetImageInfoByAppRecord_ShouldReturnImageInfoWhenExist end"); +} + +/** + * @tc.name: IsImageMakeSuccess_ShouldReturnFalseWhenImageInfoNotExist + * @tc.desc: Test IsImageMakeSuccess + * @tc.type: FUNC + */ +HWTEST_F(AppMgrServiceInnerFourthTest, IsImageMakeSuccess_ShouldReturnFalseWhenImageInfoNotExist, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "IsImageMakeSuccess_ShouldReturnFalseWhenImageInfoNotExist start"); + auto appMgrServiceInner = std::make_shared(); + EXPECT_NE(appMgrServiceInner, nullptr); + std::string bundleName = "com.acts.imagetest"; + std::string abilityName = "TestAbility"; + int32_t userId = 100; + int32_t appIndex = 0; + bool isSuccess = appMgrServiceInner->IsImageMakeSuccess(bundleName, abilityName, userId, appIndex); + EXPECT_FALSE(isSuccess); + TAG_LOGI(AAFwkTag::TEST, "IsImageMakeSuccess_ShouldReturnFalseWhenImageInfoNotExist end"); +} + +/** + * @tc.name: IsImageMakeSuccess_ShouldReturnTrueWhenImageInfoExistAndPidValid + * @tc.desc: Test IsImageMakeSuccess + * @tc.type: FUNC + */ +HWTEST_F(AppMgrServiceInnerFourthTest, IsImageMakeSuccess_ShouldReturnTrueWhenImageInfoExistAndPidValid, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "IsImageMakeSuccess_ShouldReturnTrueWhenImageInfoExistAndPidValid start"); + auto appMgrServiceInner = std::make_shared(); + EXPECT_NE(appMgrServiceInner, nullptr); + std::string bundleName = "com.acts.imagetest"; + std::string abilityName = "TestAbility"; + int32_t userId = 100; + int32_t appIndex = 0; + PreloadRequest preloadRequest; + preloadRequest.abilityName = abilityName; + appMgrServiceInner->PreAddImageInfo(bundleName, userId, appIndex, nullptr, preloadRequest); + + auto imageInfo = appMgrServiceInner->GetImageInfo(bundleName, abilityName, userId, appIndex); + if (imageInfo != nullptr) { + imageInfo->imagePid = 100; + } + + bool isSuccess = appMgrServiceInner->IsImageMakeSuccess(bundleName, abilityName, userId, appIndex); + EXPECT_TRUE(isSuccess); + TAG_LOGI(AAFwkTag::TEST, "IsImageMakeSuccess_ShouldReturnTrueWhenImageInfoExistAndPidValid end"); +} + +/** + * @tc.name: RemoveImageInfoByAppRecord_ShouldNotCrashWhenAppRecordIsNullptr + * @tc.desc: Test RemoveImageInfo with AppRunningRecord parameter + * @tc.type: FUNC + */ +HWTEST_F(AppMgrServiceInnerFourthTest, RemoveImageInfoByAppRecord_ShouldNotCrashWhenAppRecordIsNullptr, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "RemoveImageInfoByAppRecord_ShouldNotCrashWhenAppRecordIsNullptr start"); + auto appMgrServiceInner = std::make_shared(); + EXPECT_NE(appMgrServiceInner, nullptr); + appMgrServiceInner->RemoveImageInfo(nullptr); + TAG_LOGI(AAFwkTag::TEST, "RemoveImageInfoByAppRecord_ShouldNotCrashWhenAppRecordIsNullptr end"); +} + +/** + * @tc.name: RemoveImageInfoByAppRecord_ShouldRemoveWhenMatch + * @tc.desc: Test RemoveImageInfo with AppRunningRecord parameter + * @tc.type: FUNC + */ +HWTEST_F(AppMgrServiceInnerFourthTest, RemoveImageInfoByAppRecord_ShouldRemoveWhenMatch, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "RemoveImageInfoByAppRecord_ShouldRemoveWhenMatch start"); + auto appMgrServiceInner = std::make_shared(); + EXPECT_NE(appMgrServiceInner, nullptr); + std::string bundleName = "com.acts.imagetest"; + int32_t userId = 100; + int32_t appIndex = 0; + PreloadRequest preloadRequest; + appMgrServiceInner->PreAddImageInfo(bundleName, userId, appIndex, nullptr, preloadRequest); + + auto appInfo = std::make_shared(); + appInfo->bundleName = bundleName; + auto appRecord = std::make_shared(appInfo, APP_DEBUG_INFO_UID, "PROCESS_NAME"); + appRecord->SetUid(userId * BASE_USER_RANGE); + appRecord->SetAppIndex(appIndex); + + EXPECT_TRUE(appMgrServiceInner->IsImageInfoExist(bundleName, "", userId, appIndex)); + appMgrServiceInner->RemoveImageInfo(appRecord); + EXPECT_FALSE(appMgrServiceInner->IsImageInfoExist(bundleName, "", userId, appIndex)); + TAG_LOGI(AAFwkTag::TEST, "RemoveImageInfoByAppRecord_ShouldRemoveWhenMatch end"); +} + +/** + * @tc.name: HandleMakeImageTimeout_ShouldWorkCorrectly + * @tc.desc: Test HandleMakeImageTimeout with abilityName parameter + * @tc.type: FUNC + */ +HWTEST_F(AppMgrServiceInnerFourthTest, HandleMakeImageTimeout_ShouldWorkCorrectly, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "HandleMakeImageTimeout_ShouldWorkCorrectly start"); + auto appMgrServiceInner = std::make_shared(); + EXPECT_NE(appMgrServiceInner, nullptr); + std::string bundleName = "com.acts.imagetest"; + std::string abilityName = "TestAbility"; + int32_t userId = 100; + int32_t appIndex = 0; + + appMgrServiceInner->HandleMakeImageTimeout(bundleName, abilityName, userId, appIndex); + auto imageInfo = appMgrServiceInner->GetImageInfo(bundleName, abilityName, userId, appIndex); + EXPECT_EQ(imageInfo, nullptr); + TAG_LOGI(AAFwkTag::TEST, "HandleMakeImageTimeout_ShouldWorkCorrectly end"); +} + +/** + * @tc.name: HandleMakeImageFailedByAppRecord_ShouldNotCrashWhenAppRecordIsNullptr + * @tc.desc: Test HandleMakeImageFailed with AppRunningRecord parameter + * @tc.type: FUNC + */ +HWTEST_F(AppMgrServiceInnerFourthTest, HandleMakeImageFailedByAppRecord_ShouldNotCrashWhenAppRecordIsNullptr, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "HandleMakeImageFailedByAppRecord_ShouldNotCrashWhenAppRecordIsNullptr start"); + auto appMgrServiceInner = std::make_shared(); + EXPECT_NE(appMgrServiceInner, nullptr); + ImageError error = ImageError::ERR_PRELOAD_FAILED; + appMgrServiceInner->HandleMakeImageFailed(nullptr, error); + TAG_LOGI(AAFwkTag::TEST, "HandleMakeImageFailedByAppRecord_ShouldNotCrashWhenAppRecordIsNullptr end"); +} + +/** + * @tc.name: HandleMakeImageFailedByAppRecord_ShouldRemoveImageInfo + * @tc.desc: Test HandleMakeImageFailed with AppRunningRecord parameter + * @tc.type: FUNC + */ +HWTEST_F(AppMgrServiceInnerFourthTest, HandleMakeImageFailedByAppRecord_ShouldRemoveImageInfo, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "HandleMakeImageFailedByAppRecord_ShouldRemoveImageInfo start"); + auto appMgrServiceInner = std::make_shared(); + EXPECT_NE(appMgrServiceInner, nullptr); + std::string bundleName = "com.acts.imagetest"; + int32_t userId = 100; + int32_t appIndex = 0; + PreloadRequest preloadRequest; + appMgrServiceInner->PreAddImageInfo(bundleName, userId, appIndex, nullptr, preloadRequest); + + auto appInfo = std::make_shared(); + appInfo->bundleName = bundleName; + auto appRecord = std::make_shared(appInfo, APP_DEBUG_INFO_UID, "PROCESS_NAME"); + appRecord->SetUid(userId * BASE_USER_RANGE); + appRecord->SetAppIndex(appIndex); + + EXPECT_TRUE(appMgrServiceInner->IsImageInfoExist(bundleName, "", userId, appIndex)); + ImageError error = ImageError::ERR_PRELOAD_FAILED; + appMgrServiceInner->HandleMakeImageFailed(appRecord, error); + EXPECT_FALSE(appMgrServiceInner->IsImageInfoExist(bundleName, "", userId, appIndex)); + TAG_LOGI(AAFwkTag::TEST, "HandleMakeImageFailedByAppRecord_ShouldRemoveImageInfo end"); +} + +/** + * @tc.name: DestroyImageByImageInfo_ShouldReturnNotReadyWhenImagePidInvalid + * @tc.desc: Test DestroyImageByImageInfo + * @tc.type: FUNC + */ +HWTEST_F(AppMgrServiceInnerFourthTest, DestroyImageByImageInfo_ShouldReturnNotReadyWhenImagePidInvalid, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "DestroyImageByImageInfo_ShouldReturnNotReadyWhenImagePidInvalid start"); + auto appMgrServiceInner = std::make_shared(); + EXPECT_NE(appMgrServiceInner, nullptr); + auto imageInfo = std::make_shared(); + imageInfo->imagePid = -1; + auto ret = appMgrServiceInner->DestroyImageByImageInfo(imageInfo); + EXPECT_EQ(ret, ImageError::ERR_IMAGE_INFO_NOT_READY); + TAG_LOGI(AAFwkTag::TEST, "DestroyImageByImageInfo_ShouldReturnNotReadyWhenImagePidInvalid end"); +} + +/** + * @tc.name: DestroyImageByImageInfo_ShouldReturnNotExistWhenImageInfoIsNullptr + * @tc.desc: Test DestroyImageByImageInfo + * @tc.type: FUNC + */ +HWTEST_F(AppMgrServiceInnerFourthTest, DestroyImageByImageInfo_ShouldReturnNotExistWhenImageInfoIsNullptr, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "DestroyImageByImageInfo_ShouldReturnNotExistWhenImageInfoIsNullptr start"); + auto appMgrServiceInner = std::make_shared(); + EXPECT_NE(appMgrServiceInner, nullptr); + auto ret = appMgrServiceInner->DestroyImageByImageInfo(nullptr); + EXPECT_EQ(ret, ImageError::ERR_IMAGE_INFO_NOT_EXIST); + TAG_LOGI(AAFwkTag::TEST, "DestroyImageByImageInfo_ShouldReturnNotExistWhenImageInfoIsNullptr end"); +} + +/** + * @tc.name: IsImageInfoMatched_ShouldReturnFalseWhenProcessNameNotMatch + * @tc.desc: Test IsImageInfoMatched + * @tc.type: FUNC + */ +HWTEST_F(AppMgrServiceInnerFourthTest, IsImageInfoMatched_ShouldReturnFalseWhenProcessNameNotMatch, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "IsImageInfoMatched_ShouldReturnFalseWhenProcessNameNotMatch start"); + auto appMgrServiceInner = std::make_shared(); + EXPECT_NE(appMgrServiceInner, nullptr); + + auto appInfo = std::make_shared(); + appInfo->bundleName = "com.acts.imagetest"; + auto appRecord = std::make_shared(appInfo, APP_DEBUG_INFO_UID, "com.acts.imagetest"); + + auto imageInfo = std::make_shared(); + imageInfo->imagePid = 100; + imageInfo->baseAppRecord = appRecord; + + int32_t appIndex = 0; + std::string processName = "wrong_process_name"; + std::string instanceKey = ""; + std::string specifiedProcessFlag = ""; + std::string customProcessFlag = ""; + + bool isMatched = appMgrServiceInner->IsImageInfoMatched(imageInfo, appIndex, processName, + instanceKey, specifiedProcessFlag, customProcessFlag); + EXPECT_FALSE(isMatched); + TAG_LOGI(AAFwkTag::TEST, "IsImageInfoMatched_ShouldReturnFalseWhenProcessNameNotMatch end"); +} + +/** + * @tc.name: IsImageInfoMatched_ShouldReturnTrueWhenAllMatch + * @tc.desc: Test IsImageInfoMatched + * @tc.type: FUNC + */ +HWTEST_F(AppMgrServiceInnerFourthTest, IsImageInfoMatched_ShouldReturnTrueWhenAllMatch, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "IsImageInfoMatched_ShouldReturnTrueWhenAllMatch start"); + auto appMgrServiceInner = std::make_shared(); + EXPECT_NE(appMgrServiceInner, nullptr); + + auto appInfo = std::make_shared(); + appInfo->bundleName = "com.acts.imagetest"; + auto appRecord = std::make_shared(appInfo, APP_DEBUG_INFO_UID, "com.acts.imagetest"); + + auto imageInfo = std::make_shared(); + imageInfo->imagePid = 100; + imageInfo->baseAppRecord = appRecord; + + int32_t appIndex = 0; + std::string processName = "com.acts.imagetest"; + std::string instanceKey = ""; + std::string specifiedProcessFlag = ""; + std::string customProcessFlag = ""; + + bool isMatched = appMgrServiceInner->IsImageInfoMatched(imageInfo, appIndex, processName, + instanceKey, specifiedProcessFlag, customProcessFlag); + EXPECT_TRUE(isMatched); + TAG_LOGI(AAFwkTag::TEST, "IsImageInfoMatched_ShouldReturnTrueWhenAllMatch end"); +} + +/** + * @tc.name: CreateAppRunningRecordFromImageInfo_ShouldReturnNullptrWhenBaseAppRecordIsNullptr + * @tc.desc: Test CreateAppRunningRecordFromImageInfo + * @tc.type: FUNC + */ +HWTEST_F(AppMgrServiceInnerFourthTest, CreateAppRunningRecordFromImageInfo_ShouldReturnNullptrWhenBaseAppRecordIsNullptr, + TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "CreateAppRunningRecordFromImageInfo_ShouldReturnNullptrWhenBaseAppRecordIsNullptr start"); + auto appMgrServiceInner = std::make_shared(); + EXPECT_NE(appMgrServiceInner, nullptr); + auto imageInfo = std::make_shared(); + imageInfo->baseAppRecord = nullptr; + auto appRecord = appMgrServiceInner->CreateAppRunningRecordFromImageInfo(imageInfo); + EXPECT_EQ(appRecord, nullptr); + TAG_LOGI(AAFwkTag::TEST, "CreateAppRunningRecordFromImageInfo_ShouldReturnNullptrWhenBaseAppRecordIsNullptr end"); +} + +/** + * @tc.name: GetImageInfoByBundleAndAbility_ShouldReturnNullptrWhenNotExist + * @tc.desc: Test GetImageInfo with bundleName and abilityName parameters + * @tc.type: FUNC + */ +HWTEST_F(AppMgrServiceInnerFourthTest, GetImageInfoByBundleAndAbility_ShouldReturnNullptrWhenNotExist, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "GetImageInfoByBundleAndAbility_ShouldReturnNullptrWhenNotExist start"); + auto appMgrServiceInner = std::make_shared(); + EXPECT_NE(appMgrServiceInner, nullptr); + std::string bundleName = "com.acts.imagetest"; + std::string abilityName = "TestAbility"; + int32_t userId = 100; + int32_t appIndex = 0; + auto imageInfo = appMgrServiceInner->GetImageInfo(bundleName, abilityName, userId, appIndex); + EXPECT_EQ(imageInfo, nullptr); + TAG_LOGI(AAFwkTag::TEST, "GetImageInfoByBundleAndAbility_ShouldReturnNullptrWhenNotExist end"); +} + +/** + * @tc.name: GetImageInfoByBundleAndAbility_ShouldReturnImageInfoWhenExist + * @tc.desc: Test GetImageInfo with bundleName and abilityName parameters + * @tc.type: FUNC + */ +HWTEST_F(AppMgrServiceInnerFourthTest, GetImageInfoByBundleAndAbility_ShouldReturnImageInfoWhenExist, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "GetImageInfoByBundleAndAbility_ShouldReturnImageInfoWhenExist start"); + auto appMgrServiceInner = std::make_shared(); + EXPECT_NE(appMgrServiceInner, nullptr); + std::string bundleName = "com.acts.imagetest"; + std::string abilityName = "TestAbility"; + int32_t userId = 100; + int32_t appIndex = 0; + PreloadRequest preloadRequest; + preloadRequest.abilityName = abilityName; + appMgrServiceInner->PreAddImageInfo(bundleName, userId, appIndex, nullptr, preloadRequest); + auto imageInfo = appMgrServiceInner->GetImageInfo(bundleName, abilityName, userId, appIndex); + EXPECT_NE(imageInfo, nullptr); + TAG_LOGI(AAFwkTag::TEST, "GetImageInfoByBundleAndAbility_ShouldReturnImageInfoWhenExist end"); +} } // namespace AppExecFwk } // namespace OHOS diff --git a/test/unittest/app_mgr_service_inner_ninth_test/BUILD.gn b/test/unittest/app_mgr_service_inner_ninth_test/BUILD.gn index 1a6ccc0cb1..fc97d1cc51 100644 --- a/test/unittest/app_mgr_service_inner_ninth_test/BUILD.gn +++ b/test/unittest/app_mgr_service_inner_ninth_test/BUILD.gn @@ -136,6 +136,7 @@ ohos_unittest("app_mgr_service_inner_ninth_test") { "init:libbeget_proxy", "init:libbegetutil", "ipc:ipc_core", + "ipc:ipc_single", "json:nlohmann_json_static", "kv_store:distributeddata_inner", "kv_store:distributeddata_mgr", diff --git a/test/unittest/app_mgr_service_inner_ninth_test/app_mgr_service_inner_ninth_test.cpp b/test/unittest/app_mgr_service_inner_ninth_test/app_mgr_service_inner_ninth_test.cpp index 1f6ec41f26..ee4f3d5fa1 100644 --- a/test/unittest/app_mgr_service_inner_ninth_test/app_mgr_service_inner_ninth_test.cpp +++ b/test/unittest/app_mgr_service_inner_ninth_test/app_mgr_service_inner_ninth_test.cpp @@ -2452,5 +2452,290 @@ HWTEST_F(AppMgrServiceInnerNinthTest, KillChildProcessByPid_0300, TestSize.Level AAFwk::MyStatus::GetInstance().getChildProcessRecordByPid_ = nullptr; TAG_LOGI(AAFwkTag::TEST, "KillChildProcessByPid_0300 end"); } + +/** + * @tc.name: EnableDelayedProcessExit_0100 + * @tc.desc: Test EnableDelayedProcessExit when device not supported + * @tc.type: FUNC + */ +HWTEST_F(AppMgrServiceInnerNinthTest, EnableDelayedProcessExit_0100, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "EnableDelayedProcessExit_0100 start"); + auto appMgrServiceInner = std::make_shared(); + ASSERT_NE(appMgrServiceInner, nullptr); + + int32_t pid = 1234; + auto ret = appMgrServiceInner->EnableDelayedProcessExit(pid, true); + EXPECT_EQ(ret, ERR_INVALID_VALUE); + TAG_LOGI(AAFwkTag::TEST, "EnableDelayedProcessExit_0100 end"); +} + +/** + * @tc.name: EnableDelayedProcessExit_0200 + * @tc.desc: Test EnableDelayedProcessExit with null appRecord + * @tc.type: FUNC + */ +HWTEST_F(AppMgrServiceInnerNinthTest, EnableDelayedProcessExit_0200, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "EnableDelayedProcessExit_0200 start"); + auto appMgrServiceInner = std::make_shared(); + ASSERT_NE(appMgrServiceInner, nullptr); + + // Force IsSupportDelayedProcessExit to return true + AAFwk::AppUtils::GetInstance().isSupportDelayedProcessExit_.isLoaded = true; + AAFwk::AppUtils::GetInstance().isSupportDelayedProcessExit_.value = true; + + AAFwk::MyStatus::GetInstance().getAppRunningRecordByPid_ = nullptr; + int32_t pid = 1234; + auto ret = appMgrServiceInner->EnableDelayedProcessExit(pid, true); + EXPECT_EQ(ret, ERR_INVALID_VALUE); + + // Restore + AAFwk::AppUtils::GetInstance().isSupportDelayedProcessExit_.isLoaded = false; + TAG_LOGI(AAFwkTag::TEST, "EnableDelayedProcessExit_0200 end"); +} + +/** + * @tc.name: EnableDelayedProcessExit_0300 + * @tc.desc: Test EnableDelayedProcessExit with appRecord but no UIAbility + * @tc.type: FUNC + */ +HWTEST_F(AppMgrServiceInnerNinthTest, EnableDelayedProcessExit_0300, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "EnableDelayedProcessExit_0300 start"); + auto appMgrServiceInner = std::make_shared(); + ASSERT_NE(appMgrServiceInner, nullptr); + + AAFwk::AppUtils::GetInstance().isSupportDelayedProcessExit_.isLoaded = true; + AAFwk::AppUtils::GetInstance().isSupportDelayedProcessExit_.value = true; + + auto applicationInfo = std::make_shared(); + applicationInfo->name = "test.app.name"; + applicationInfo->bundleName = "test.bundle.name"; + auto appRecord = std::make_shared(applicationInfo, 1, "test_process"); + ASSERT_NE(appRecord, nullptr); + AAFwk::MyStatus::GetInstance().getAppRunningRecordByPid_ = appRecord; + + // No UIAbility added, so should return ERR_DELAYED_PROCESS_EXIT_NO_UIABILITY + int32_t pid = 1234; + auto ret = appMgrServiceInner->EnableDelayedProcessExit(pid, true); + EXPECT_EQ(ret, AAFwk::ERR_DELAYED_PROCESS_EXIT_NO_UIABILITY); + + AAFwk::MyStatus::GetInstance().getAppRunningRecordByPid_ = nullptr; + AAFwk::AppUtils::GetInstance().isSupportDelayedProcessExit_.isLoaded = false; + TAG_LOGI(AAFwkTag::TEST, "EnableDelayedProcessExit_0300 end"); +} + +/** + * @tc.name: EnableDelayedProcessExit_0400 + * @tc.desc: Test EnableDelayedProcessExit with UIAbility present and enabled=true (success path) + * @tc.type: FUNC + */ +HWTEST_F(AppMgrServiceInnerNinthTest, EnableDelayedProcessExit_0400, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "EnableDelayedProcessExit_0400 start"); + auto appMgrServiceInner = std::make_shared(); + ASSERT_NE(appMgrServiceInner, nullptr); + + AAFwk::AppUtils::GetInstance().isSupportDelayedProcessExit_.isLoaded = true; + AAFwk::AppUtils::GetInstance().isSupportDelayedProcessExit_.value = true; + + auto applicationInfo = std::make_shared(); + applicationInfo->name = "test.app.name"; + applicationInfo->bundleName = "test.bundle.name"; + auto appRecord = std::make_shared(applicationInfo, 1, "test_process"); + ASSERT_NE(appRecord, nullptr); + AAFwk::MyStatus::GetInstance().getAppRunningRecordByPid_ = appRecord; + + // Add a UIAbility (PAGE type) to the abilitiesMap + auto abilityInfo = std::make_shared(); + abilityInfo->type = AbilityType::PAGE; + sptr token = new MockAppScheduler(); + auto abilityRecord = std::make_shared(abilityInfo, token, 1); + AAFwk::MyStatus::GetInstance().abilitiesMap_[token] = abilityRecord; + + int32_t pid = 1234; + auto ret = appMgrServiceInner->EnableDelayedProcessExit(pid, true); + EXPECT_EQ(ret, ERR_OK); + EXPECT_TRUE(appRecord->IsDelayedProcessExitEnabled()); + + AAFwk::MyStatus::GetInstance().abilitiesMap_.clear(); + AAFwk::MyStatus::GetInstance().getAppRunningRecordByPid_ = nullptr; + AAFwk::AppUtils::GetInstance().isSupportDelayedProcessExit_.isLoaded = false; + TAG_LOGI(AAFwkTag::TEST, "EnableDelayedProcessExit_0400 end"); +} + +/** + * @tc.name: EnableDelayedProcessExit_0500 + * @tc.desc: Test EnableDelayedProcessExit with UIAbility present and enabled=false (disable + cancel task) + * @tc.type: FUNC + */ +HWTEST_F(AppMgrServiceInnerNinthTest, EnableDelayedProcessExit_0500, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "EnableDelayedProcessExit_0500 start"); + auto appMgrServiceInner = std::make_shared(); + ASSERT_NE(appMgrServiceInner, nullptr); + + AAFwk::AppUtils::GetInstance().isSupportDelayedProcessExit_.isLoaded = true; + AAFwk::AppUtils::GetInstance().isSupportDelayedProcessExit_.value = true; + + auto applicationInfo = std::make_shared(); + applicationInfo->name = "test.app.name"; + applicationInfo->bundleName = "test.bundle.name"; + auto appRecord = std::make_shared(applicationInfo, 1, "test_process"); + ASSERT_NE(appRecord, nullptr); + AAFwk::MyStatus::GetInstance().getAppRunningRecordByPid_ = appRecord; + + // Add a UIAbility (PAGE type) to the abilitiesMap + auto abilityInfo = std::make_shared(); + abilityInfo->type = AbilityType::PAGE; + sptr token = new MockAppScheduler(); + auto abilityRecord = std::make_shared(abilityInfo, token, 1); + AAFwk::MyStatus::GetInstance().abilitiesMap_[token] = abilityRecord; + + appMgrServiceInner->taskHandler_ = + std::make_shared("EnableDelayedProcessExitTest"); + + int32_t pid = 1234; + auto ret = appMgrServiceInner->EnableDelayedProcessExit(pid, false); + EXPECT_EQ(ret, ERR_OK); + EXPECT_FALSE(appRecord->IsDelayedProcessExitEnabled()); + + AAFwk::MyStatus::GetInstance().abilitiesMap_.clear(); + AAFwk::MyStatus::GetInstance().getAppRunningRecordByPid_ = nullptr; + AAFwk::AppUtils::GetInstance().isSupportDelayedProcessExit_.isLoaded = false; + TAG_LOGI(AAFwkTag::TEST, "EnableDelayedProcessExit_0500 end"); +} + +/** + * @tc.name: EnableDelayedProcessExit_0600 + * @tc.desc: Test EnableDelayedProcessExit with null AbilityRunningRecord item in abilitiesMap + * @tc.type: FUNC + */ +HWTEST_F(AppMgrServiceInnerNinthTest, EnableDelayedProcessExit_0600, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "EnableDelayedProcessExit_0600 start"); + auto appMgrServiceInner = std::make_shared(); + ASSERT_NE(appMgrServiceInner, nullptr); + + AAFwk::AppUtils::GetInstance().isSupportDelayedProcessExit_.isLoaded = true; + AAFwk::AppUtils::GetInstance().isSupportDelayedProcessExit_.value = true; + + auto applicationInfo = std::make_shared(); + applicationInfo->name = "test.app.name"; + applicationInfo->bundleName = "test.bundle.name"; + auto appRecord = std::make_shared(applicationInfo, 1, "test_process"); + ASSERT_NE(appRecord, nullptr); + AAFwk::MyStatus::GetInstance().getAppRunningRecordByPid_ = appRecord; + + // Add a null AbilityRunningRecord to the abilitiesMap + sptr token = new MockAppScheduler(); + AAFwk::MyStatus::GetInstance().abilitiesMap_[token] = nullptr; + + int32_t pid = 1234; + auto ret = appMgrServiceInner->EnableDelayedProcessExit(pid, true); + // null item.second -> continue, then no UIAbility found + EXPECT_EQ(ret, AAFwk::ERR_DELAYED_PROCESS_EXIT_NO_UIABILITY); + + AAFwk::MyStatus::GetInstance().abilitiesMap_.clear(); + AAFwk::MyStatus::GetInstance().getAppRunningRecordByPid_ = nullptr; + AAFwk::AppUtils::GetInstance().isSupportDelayedProcessExit_.isLoaded = false; + TAG_LOGI(AAFwkTag::TEST, "EnableDelayedProcessExit_0600 end"); +} + +/** + * @tc.name: EnableDelayedProcessExit_0700 + * @tc.desc: Test EnableDelayedProcessExit with non-PAGE ability type (abilityInfo not null but not PAGE) + * @tc.type: FUNC + */ +HWTEST_F(AppMgrServiceInnerNinthTest, EnableDelayedProcessExit_0700, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "EnableDelayedProcessExit_0700 start"); + auto appMgrServiceInner = std::make_shared(); + ASSERT_NE(appMgrServiceInner, nullptr); + + AAFwk::AppUtils::GetInstance().isSupportDelayedProcessExit_.isLoaded = true; + AAFwk::AppUtils::GetInstance().isSupportDelayedProcessExit_.value = true; + + auto applicationInfo = std::make_shared(); + applicationInfo->name = "test.app.name"; + applicationInfo->bundleName = "test.bundle.name"; + auto appRecord = std::make_shared(applicationInfo, 1, "test_process"); + ASSERT_NE(appRecord, nullptr); + AAFwk::MyStatus::GetInstance().getAppRunningRecordByPid_ = appRecord; + + // Add a SERVICE type ability (not PAGE) + auto abilityInfo = std::make_shared(); + abilityInfo->type = AbilityType::SERVICE; + sptr token = new MockAppScheduler(); + auto abilityRecord = std::make_shared(abilityInfo, token, 1); + AAFwk::MyStatus::GetInstance().abilitiesMap_[token] = abilityRecord; + + int32_t pid = 1234; + auto ret = appMgrServiceInner->EnableDelayedProcessExit(pid, true); + // abilityInfo != nullptr but type != PAGE, so no UIAbility found + EXPECT_EQ(ret, AAFwk::ERR_DELAYED_PROCESS_EXIT_NO_UIABILITY); + + AAFwk::MyStatus::GetInstance().abilitiesMap_.clear(); + AAFwk::MyStatus::GetInstance().getAppRunningRecordByPid_ = nullptr; + AAFwk::AppUtils::GetInstance().isSupportDelayedProcessExit_.isLoaded = false; + TAG_LOGI(AAFwkTag::TEST, "EnableDelayedProcessExit_0700 end"); +} + +/** + * @tc.name: CancelDelayedExitTask_0300 + * @tc.desc: Test CancelDelayedExitTask with appRecord and delayed exit enabled + * @tc.type: FUNC + */ +HWTEST_F(AppMgrServiceInnerNinthTest, CancelDelayedExitTask_0300, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "CancelDelayedExitTask_0300 start"); + auto appMgrServiceInner = std::make_shared(); + ASSERT_NE(appMgrServiceInner, nullptr); + + auto applicationInfo = std::make_shared(); + applicationInfo->name = "test.app.name"; + applicationInfo->bundleName = "test.bundle.name"; + auto appRecord = std::make_shared(applicationInfo, 1, "test_process"); + ASSERT_NE(appRecord, nullptr); + appRecord->EnableDelayedProcessExit(true); + AAFwk::MyStatus::GetInstance().getAppRunningRecordByPid_ = appRecord; + + appMgrServiceInner->taskHandler_ = + std::make_shared("CancelDelayedExitTaskTest"); + // Should cancel the task + appMgrServiceInner->CancelDelayedExitTask(1234); + EXPECT_TRUE(appRecord->IsDelayedProcessExitEnabled()); + + AAFwk::MyStatus::GetInstance().getAppRunningRecordByPid_ = nullptr; + TAG_LOGI(AAFwkTag::TEST, "CancelDelayedExitTask_0300 end"); +} + +/** + * @tc.name: CancelDelayedExitTask_0400 + * @tc.desc: Test CancelDelayedExitTask with delayed exit enabled but null taskHandler + * @tc.type: FUNC + */ +HWTEST_F(AppMgrServiceInnerNinthTest, CancelDelayedExitTask_0400, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "CancelDelayedExitTask_0400 start"); + auto appMgrServiceInner = std::make_shared(); + ASSERT_NE(appMgrServiceInner, nullptr); + + auto applicationInfo = std::make_shared(); + applicationInfo->name = "test.app.name"; + applicationInfo->bundleName = "test.bundle.name"; + auto appRecord = std::make_shared(applicationInfo, 1, "test_process"); + ASSERT_NE(appRecord, nullptr); + appRecord->EnableDelayedProcessExit(true); + AAFwk::MyStatus::GetInstance().getAppRunningRecordByPid_ = appRecord; + + appMgrServiceInner->taskHandler_ = nullptr; + // Should not crash even with null taskHandler + appMgrServiceInner->CancelDelayedExitTask(1234); + + AAFwk::MyStatus::GetInstance().getAppRunningRecordByPid_ = nullptr; + TAG_LOGI(AAFwkTag::TEST, "CancelDelayedExitTask_0400 end"); +} } // namespace AppExecFwk } // namespace OHOS diff --git a/test/unittest/app_mgr_service_inner_ninth_test/mock/include/mock_my_status.h b/test/unittest/app_mgr_service_inner_ninth_test/mock/include/mock_my_status.h index a108412df2..1f6930c046 100644 --- a/test/unittest/app_mgr_service_inner_ninth_test/mock/include/mock_my_status.h +++ b/test/unittest/app_mgr_service_inner_ninth_test/mock/include/mock_my_status.h @@ -362,6 +362,7 @@ public: runningRecord_ = nullptr; } std::shared_ptr masterProcessRunningRecord_ = nullptr; + std::shared_ptr mainProcessRunningRecord_ = nullptr; std::shared_ptr appRecordForSpecifiedProcess_ = nullptr; bool exitByPidstatus_ = false; @@ -370,6 +371,7 @@ private: MyStatus() = default; bool isLogoutUserCalled_ = false; bool isFindMasterProcessAppRunningRecordCalled_ = false; + bool isFindMainProcessAppRunningRecordCalled_ = false; bool isCheckAppRunningRecordForSpecifiedProcessCalled_ = false; }; } // namespace AAFwk diff --git a/test/unittest/app_mgr_service_inner_ninth_test/mock/src/mock_app_preloader.cpp b/test/unittest/app_mgr_service_inner_ninth_test/mock/src/mock_app_preloader.cpp index 81e2a0987d..cdffe94c3b 100644 --- a/test/unittest/app_mgr_service_inner_ninth_test/mock/src/mock_app_preloader.cpp +++ b/test/unittest/app_mgr_service_inner_ninth_test/mock/src/mock_app_preloader.cpp @@ -101,7 +101,7 @@ std::shared_ptr AppPreloader::GetBundleManagerHelper() return nullptr; } -int32_t AppPreloader::GeneratePreloadExtensionRequest(const AAFwk::Want &want, const AbilityInfo &abilityInfo, +int32_t AppPreloader::GeneratePreloadExtensionRequest(const AAFwk::Want &want, int32_t userId, int32_t appIndex, PreloadRequest &request) { return ERR_OK; diff --git a/test/unittest/app_mgr_service_inner_ninth_test/mock/src/mock_app_running_manager.cpp b/test/unittest/app_mgr_service_inner_ninth_test/mock/src/mock_app_running_manager.cpp index b2fff24e39..428da21fdd 100644 --- a/test/unittest/app_mgr_service_inner_ninth_test/mock/src/mock_app_running_manager.cpp +++ b/test/unittest/app_mgr_service_inner_ninth_test/mock/src/mock_app_running_manager.cpp @@ -531,6 +531,12 @@ std::shared_ptr AppRunningManager::FindMasterProcessAppRunning return AAFwk::MyStatus::GetInstance().masterProcessRunningRecord_; } +std::shared_ptr AppRunningManager::FindMainProcessAppRunningRecord(const int uid) +{ + AAFwk::MyStatus::GetInstance().isFindMainProcessAppRunningRecordCalled_ = true; + return AAFwk::MyStatus::GetInstance().mainProcessRunningRecord_; +} + std::shared_ptr AppRunningManager::CheckAppRunningRecordForSpecifiedProcess( int32_t uid, const std::string &instanceKey, const std::string &customProcessFlag) { diff --git a/test/unittest/app_mgr_service_inner_ninth_test/mock/src/mock_app_running_record.cpp b/test/unittest/app_mgr_service_inner_ninth_test/mock/src/mock_app_running_record.cpp index 7fe29b6785..28b56be33f 100644 --- a/test/unittest/app_mgr_service_inner_ninth_test/mock/src/mock_app_running_record.cpp +++ b/test/unittest/app_mgr_service_inner_ninth_test/mock/src/mock_app_running_record.cpp @@ -1098,6 +1098,16 @@ std::string AppRunningRecord::GetPreloadModuleName() const return preloadModuleName_; } +void AppRunningRecord::SetPreloadAbilityName(const std::string &abilityName) +{ + preloadAbilityName_ = abilityName; +} + +std::string AppRunningRecord::GetPreloadAbilityName() const +{ + return preloadAbilityName_; +} + void AppRunningRecord::SetPreloadState(PreloadState state) { preloadState_ = state; diff --git a/test/unittest/app_mgr_service_inner_second_test/app_mgr_service_inner_second_test.cpp b/test/unittest/app_mgr_service_inner_second_test/app_mgr_service_inner_second_test.cpp index 024ad6ed14..80dd981201 100644 --- a/test/unittest/app_mgr_service_inner_second_test/app_mgr_service_inner_second_test.cpp +++ b/test/unittest/app_mgr_service_inner_second_test/app_mgr_service_inner_second_test.cpp @@ -1506,6 +1506,8 @@ HWTEST_F(AppMgrServiceInnerSecondTest, AppMgrServiceInnerSecondTest_TransformedN appRecord->isDebugApp_ = true; ret = appMgrServiceInner->TransformedNotifyAppFault(faultData); EXPECT_EQ(ret, ERR_OK); + faultData.errorObject.message = "PreloadUIExtension test"; + appMgrServiceInner->TransformedNotifyAppFault(faultData); EXPECT_NE(appMgrServiceInner, nullptr); TAG_LOGI(AAFwkTag::TEST, "AppMgrServiceInnerSecondTest_TransformedNotifyAppFault_0200 end"); } diff --git a/test/unittest/app_mgr_service_inner_seventh_test/BUILD.gn b/test/unittest/app_mgr_service_inner_seventh_test/BUILD.gn index f41ecc4c2f..f118f79fdc 100644 --- a/test/unittest/app_mgr_service_inner_seventh_test/BUILD.gn +++ b/test/unittest/app_mgr_service_inner_seventh_test/BUILD.gn @@ -134,6 +134,7 @@ ohos_unittest("app_mgr_service_inner_seventh_test") { "init:libbeget_proxy", "init:libbegetutil", "ipc:ipc_core", + "ipc:ipc_single", "json:nlohmann_json_static", "kv_store:distributeddata_inner", "kv_store:distributeddata_mgr", diff --git a/test/unittest/app_mgr_service_inner_seventh_test/mock/src/mock_app_running_record.cpp b/test/unittest/app_mgr_service_inner_seventh_test/mock/src/mock_app_running_record.cpp index b5a5b2f496..3b749acc7c 100644 --- a/test/unittest/app_mgr_service_inner_seventh_test/mock/src/mock_app_running_record.cpp +++ b/test/unittest/app_mgr_service_inner_seventh_test/mock/src/mock_app_running_record.cpp @@ -1019,6 +1019,16 @@ std::string AppRunningRecord::GetPreloadModuleName() const return preloadModuleName_; } +void AppRunningRecord::SetPreloadAbilityName(const std::string &abilityName) +{ + preloadAbilityName_ = abilityName; +} + +std::string AppRunningRecord::GetPreloadAbilityName() const +{ + return preloadAbilityName_; +} + void AppRunningRecord::SetPreloadState(PreloadState state) { preloadState_ = state; diff --git a/test/unittest/app_mgr_service_inner_sixth_test/app_mgr_service_inner_sixth_test.cpp b/test/unittest/app_mgr_service_inner_sixth_test/app_mgr_service_inner_sixth_test.cpp index bd572fe9af..7673f7a6b3 100644 --- a/test/unittest/app_mgr_service_inner_sixth_test/app_mgr_service_inner_sixth_test.cpp +++ b/test/unittest/app_mgr_service_inner_sixth_test/app_mgr_service_inner_sixth_test.cpp @@ -125,7 +125,7 @@ HWTEST_F(AppMgrServiceInnerSixthTest, CreateAbilityInfo_001, TestSize.Level0) MyFlag::flag_ = FALSE_VALUE; auto ret4 = appMgrServiceInner->CreateAbilityInfo(want, abilityInfo); - EXPECT_EQ(ret4, true); + EXPECT_EQ(ret4, false); TAG_LOGI(AAFwkTag::TEST, "CreateAbilityInfo_001 end"); } @@ -140,7 +140,7 @@ HWTEST_F(AppMgrServiceInnerSixthTest, CreateAbilityInfo_002, TestSize.Level0) auto appMgrServiceInner = std::make_shared(); EXPECT_NE(appMgrServiceInner, nullptr); MyFlag::flag1_ = FALSE_VALUE; - MyFlag::flag2_ = TRUE_VALUE; + MyFlag::flag2_ = FALSE_VALUE; MyFlag::flag_ = TRUE_VALUE; AAFwk::Want want; want.SetParam(DLP_PARAMS_INDEX, DLP_PARAMS_INDEX_VALUE_ONE); @@ -148,7 +148,7 @@ HWTEST_F(AppMgrServiceInnerSixthTest, CreateAbilityInfo_002, TestSize.Level0) auto ret1 = appMgrServiceInner->CreateAbilityInfo(want, abilityInfo); EXPECT_EQ(ret1, true); - MyFlag::flag2_ = FALSE_VALUE; + MyFlag::flag2_ = TRUE_VALUE; MyFlag::flag_ = FALSE_VALUE; auto ret2 = appMgrServiceInner->CreateAbilityInfo(want, abilityInfo); EXPECT_EQ(ret2, false); @@ -441,48 +441,6 @@ HWTEST_F(AppMgrServiceInnerSixthTest, KillFaultApp_003, TestSize.Level1) GTEST_LOG_(INFO) << "KillFaultApp_003 end"; } -/** - * @tc.name: KillFaultApp_004 - * @tc.type: FUNC - * @tc.Function: KillFaultApp - * @tc.SubFunction: NA - * @tc.EnvConditions: NA - */ -HWTEST_F(AppMgrServiceInnerSixthTest, KillFaultApp_004, TestSize.Level1) -{ - GTEST_LOG_(INFO) << "KillFaultApp_004 start"; - pid_t childPid = fork(); - if (childPid < 0) { - printf("failed to fork process.\n"); - } else if (childPid == 0) { - auto appMgrServiceInner = std::make_shared(); - EXPECT_NE(appMgrServiceInner, nullptr); - auto taskHandlerWrapTest = std::make_shared(""); - EXPECT_NE(taskHandlerWrapTest, nullptr); - appMgrServiceInner->taskHandler_ = taskHandlerWrapTest; - EXPECT_NE(appMgrServiceInner->taskHandler_, nullptr); - - int pid = 0; - std::string bundleName = "KillFaultAppTest"; - bool isNeedExit = true; - FaultData faultData; - faultData.forceExit = true; - faultData.waitSaveState = false; - int32_t result = appMgrServiceInner->KillFaultApp(pid, bundleName, faultData, isNeedExit); - EXPECT_EQ(result, 0); - int sleepCount = 10; - while (sleepCount > 0) { - sleepCount = sleep(sleepCount); - } - } else { - if (waitpid(childPid, nullptr, 0) != childPid) { - printf("failed to wait process.\n"); - } - printf("waitpid process success.\n"); - } - GTEST_LOG_(INFO) << "KillFaultApp_004 end"; -} - /** * @tc.name: SetAppFreezeFilter_004 * @tc.type: FUNC diff --git a/test/unittest/app_mgr_service_inner_tdd_test/app_mgr_service_inner_tdd_test.cpp b/test/unittest/app_mgr_service_inner_tdd_test/app_mgr_service_inner_tdd_test.cpp index 4d96e7def7..072e81c54f 100644 --- a/test/unittest/app_mgr_service_inner_tdd_test/app_mgr_service_inner_tdd_test.cpp +++ b/test/unittest/app_mgr_service_inner_tdd_test/app_mgr_service_inner_tdd_test.cpp @@ -1002,6 +1002,8 @@ HWTEST_F(AppMgrServiceInnerTest, SubmitDfxFaultTask_001, TestSize.Level1) appMgrServiceInner->Init(); EXPECT_NE(appMgrServiceInner, nullptr); appMgrServiceInner->SubmitDfxFaultTask(faultData, bundleName, appRecord, pid); + faultData.errorObject.message = "PreloadUIExtension test"; + appMgrServiceInner->SubmitDfxFaultTask(faultData, bundleName, appRecord, pid); TAG_LOGI(AAFwkTag::TEST, "SubmitDfxFaultTask end"); } diff --git a/test/unittest/app_mgr_service_inner_tenth_test/BUILD.gn b/test/unittest/app_mgr_service_inner_tenth_test/BUILD.gn index ed3171d117..105749c01c 100644 --- a/test/unittest/app_mgr_service_inner_tenth_test/BUILD.gn +++ b/test/unittest/app_mgr_service_inner_tenth_test/BUILD.gn @@ -139,6 +139,7 @@ ohos_unittest("app_mgr_service_inner_tenth_test") { "init:libbeget_proxy", "init:libbegetutil", "ipc:ipc_core", + "ipc:ipc_single", "json:nlohmann_json_static", "kv_store:distributeddata_inner", "kv_store:distributeddata_mgr", diff --git a/test/unittest/app_mgr_service_inner_test/app_mgr_service_inner_test.cpp b/test/unittest/app_mgr_service_inner_test/app_mgr_service_inner_test.cpp index 4ed77e2f24..37e5adae94 100644 --- a/test/unittest/app_mgr_service_inner_test/app_mgr_service_inner_test.cpp +++ b/test/unittest/app_mgr_service_inner_test/app_mgr_service_inner_test.cpp @@ -6800,5 +6800,22 @@ HWTEST_F(AppMgrServiceInnerTest, GetAllAbilityInfos_0003, TestSize.Level1) EXPECT_EQ(ret, ERR_INVALID_VALUE); TAG_LOGI(AAFwkTag::TEST, "GetAllAbilityInfos_0003 end"); } + +/** + * @tc.name: IsChildProcessSupported_001 + * @tc.desc: IsChildProcessSupported when no app record. + * @tc.type: FUNC + */ +HWTEST_F(AppMgrServiceInnerTest, IsChildProcessSupported_001, TestSize.Level2) +{ + auto appMgrServiceInner = std::make_shared(); + EXPECT_NE(appMgrServiceInner, nullptr); + + bool isSupported = false; + EXPECT_EQ(appMgrServiceInner->IsChildProcessSupported(false, isSupported), AAFwk::ERR_NO_APP_RECORD); + + isSupported = false; + EXPECT_EQ(appMgrServiceInner->IsChildProcessSupported(true, isSupported), AAFwk::ERR_NO_APP_RECORD); +} } // namespace AppExecFwk } // namespace OHOS diff --git a/test/unittest/app_mgr_service_second_test/app_mgr_service_second_test.cpp b/test/unittest/app_mgr_service_second_test/app_mgr_service_second_test.cpp index c6981b9cee..b9894da05d 100644 --- a/test/unittest/app_mgr_service_second_test/app_mgr_service_second_test.cpp +++ b/test/unittest/app_mgr_service_second_test/app_mgr_service_second_test.cpp @@ -19,6 +19,10 @@ #define private public #define protected public #include "app_mgr_service.h" +#include "app_running_manager.h" +#include "app_running_record.h" +#include "app_utils.h" +#include "priority_object.h" #undef protected #undef private #include "mock_ipc_skeleton.h" @@ -572,5 +576,91 @@ HWTEST_F(AppMgrServiceSecondTest, UpdateFreezeExcludedPid_0100, TestSize.Level2) IPCSkeleton::uid_ = HIPROFILER_UID; appMgrService->UpdateFreezeExcludedPid(true, pid, profilerPid); } + +/** + * @tc.name: IsChildProcessSupported_0100 + * @tc.desc: IsChildProcessSupported when ready but no app record for ark. + * @tc.type: FUNC + */ +HWTEST_F(AppMgrServiceSecondTest, IsChildProcessSupported_0100, TestSize.Level1) +{ + auto appMgrService = std::make_shared(); + ASSERT_NE(appMgrService, nullptr); + ReadyToRun(appMgrService); + IPCSkeleton::pid_ = PID; + bool isSupported = false; + auto ret = appMgrService->IsChildProcessSupported(false, isSupported); + EXPECT_EQ(ret, AAFwk::ERR_NO_APP_RECORD); +} + +/** + * @tc.name: IsChildProcessSupported_0200 + * @tc.desc: IsChildProcessSupported when ready but no app record for native. + * @tc.type: FUNC + */ +HWTEST_F(AppMgrServiceSecondTest, IsChildProcessSupported_0200, TestSize.Level1) +{ + auto appMgrService = std::make_shared(); + ASSERT_NE(appMgrService, nullptr); + ReadyToRun(appMgrService); + IPCSkeleton::pid_ = PID; + bool isSupported = false; + auto ret = appMgrService->IsChildProcessSupported(true, isSupported); + EXPECT_EQ(ret, AAFwk::ERR_NO_APP_RECORD); +} + +/** + * @tc.name: IsChildProcessSupported_0300 + * @tc.desc: IsChildProcessSupported when ready with app record for ark and multiProcessModel is true. + * @tc.type: FUNC + */ +HWTEST_F(AppMgrServiceSecondTest, IsChildProcessSupported_0300, TestSize.Level1) +{ + auto appMgrService = std::make_shared(); + ASSERT_NE(appMgrService, nullptr); + ReadyToRun(appMgrService); + + auto appInfo = std::make_shared(); + appInfo->bundleName = BUNDLE_NAME; + auto appRecord = std::make_shared(appInfo, 1, "test_process"); + appRecord->GetPriorityObject()->SetPid(PID); + appMgrService->appMgrServiceInner_->appRunningManager_->appRunningRecordMap_.emplace(1, appRecord); + + AAFwk::AppUtils::GetInstance().isMultiProcessModel_.isLoaded = true; + AAFwk::AppUtils::GetInstance().isMultiProcessModel_.value = true; + IPCSkeleton::pid_ = PID; + + bool isSupported = false; + auto ret = appMgrService->IsChildProcessSupported(false, isSupported); + EXPECT_EQ(ret, ERR_OK); + EXPECT_TRUE(isSupported); +} + +/** + * @tc.name: IsChildProcessSupported_0400 + * @tc.desc: IsChildProcessSupported when ready with app record for native and multiProcessModel is true. + * @tc.type: FUNC + */ +HWTEST_F(AppMgrServiceSecondTest, IsChildProcessSupported_0400, TestSize.Level1) +{ + auto appMgrService = std::make_shared(); + ASSERT_NE(appMgrService, nullptr); + ReadyToRun(appMgrService); + + auto appInfo = std::make_shared(); + appInfo->bundleName = BUNDLE_NAME; + auto appRecord = std::make_shared(appInfo, 2, "test_process"); + appRecord->GetPriorityObject()->SetPid(PID); + appMgrService->appMgrServiceInner_->appRunningManager_->appRunningRecordMap_.emplace(2, appRecord); + + AAFwk::AppUtils::GetInstance().isMultiProcessModel_.isLoaded = true; + AAFwk::AppUtils::GetInstance().isMultiProcessModel_.value = true; + IPCSkeleton::pid_ = PID; + + bool isSupported = false; + auto ret = appMgrService->IsChildProcessSupported(true, isSupported); + EXPECT_EQ(ret, ERR_OK); + EXPECT_TRUE(isSupported); +} } // namespace AppExecFwk } // namespace OHOS \ No newline at end of file diff --git a/test/unittest/app_mgr_service_test/app_mgr_service_test.cpp b/test/unittest/app_mgr_service_test/app_mgr_service_test.cpp index 3940c9bdf7..63977f8635 100644 --- a/test/unittest/app_mgr_service_test/app_mgr_service_test.cpp +++ b/test/unittest/app_mgr_service_test/app_mgr_service_test.cpp @@ -2622,5 +2622,19 @@ HWTEST_F(AppMgrServiceTest, UpdateConfigurationByUserIds_001, TestSize.Level2) int32_t res = appMgrService->UpdateConfigurationByUserIds(config, userIds); EXPECT_EQ(res, ERR_INVALID_OPERATION); } + +/** + * @tc.name: IsChildProcessSupported_0100 + * @tc.desc: IsChildProcessSupported when service is not ready. + * @tc.type: FUNC + */ +HWTEST_F(AppMgrServiceTest, IsChildProcessSupported_0100, TestSize.Level1) +{ + auto appMgrService = std::make_shared(); + ASSERT_NE(appMgrService, nullptr); + bool isSupported = false; + auto ret = appMgrService->IsChildProcessSupported(false, isSupported); + EXPECT_EQ(ret, ERR_INVALID_OPERATION); +} } // namespace AppExecFwk } // namespace OHOS \ No newline at end of file diff --git a/test/unittest/app_mgr_stub_test/app_mgr_stub_test.cpp b/test/unittest/app_mgr_stub_test/app_mgr_stub_test.cpp index 2f61df1e60..280e755723 100644 --- a/test/unittest/app_mgr_stub_test/app_mgr_stub_test.cpp +++ b/test/unittest/app_mgr_stub_test/app_mgr_stub_test.cpp @@ -1430,5 +1430,80 @@ HWTEST_F(AppMgrStubTest, HandleUpdateFreezeExcludedPid_001, TestSize.Level1) static_cast(AppMgrInterfaceCode::UPDATE_FREEZE_EXCLUDED_PID), data, reply, option); EXPECT_EQ(result, NO_ERROR); } + +/** + * @tc.name: HandleEnableDelayedProcessExit_0100 + * @tc.desc: Test HandleEnableDelayedProcessExit with enable flag. + * @tc.type: FUNC + */ +HWTEST_F(AppMgrStubTest, HandleEnableDelayedProcessExit_0100, TestSize.Level1) +{ + MessageParcel data; + MessageParcel reply; + MessageOption option; + + WriteInterfaceToken(data); + int32_t pid = 1234; + data.WriteInt32(pid); + data.WriteBool(true); + + EXPECT_CALL(*mockAppMgrService_, EnableDelayedProcessExit(_, _)) + .Times(1) + .WillOnce(Return(ERR_OK)); + + auto ret = mockAppMgrService_->OnRemoteRequest( + static_cast(AppMgrInterfaceCode::ENABLE_DELAYED_PROCESS_EXIT), data, reply, option); + EXPECT_EQ(ret, NO_ERROR); + EXPECT_EQ(reply.ReadInt32(), ERR_OK); +} + +/** + * @tc.name: HandleEnableDelayedProcessExit_0200 + * @tc.desc: Test HandleEnableDelayedProcessExit with disable flag. + * @tc.type: FUNC + */ +HWTEST_F(AppMgrStubTest, HandleEnableDelayedProcessExit_0200, TestSize.Level1) +{ + MessageParcel data; + MessageParcel reply; + MessageOption option; + + WriteInterfaceToken(data); + int32_t pid = 1234; + data.WriteInt32(pid); + data.WriteBool(false); + + EXPECT_CALL(*mockAppMgrService_, EnableDelayedProcessExit(_, _)) + .Times(1) + .WillOnce(Return(ERR_INVALID_VALUE)); + + auto ret = mockAppMgrService_->OnRemoteRequest( + static_cast(AppMgrInterfaceCode::ENABLE_DELAYED_PROCESS_EXIT), data, reply, option); + EXPECT_EQ(ret, NO_ERROR); + EXPECT_EQ(reply.ReadInt32(), ERR_INVALID_VALUE); +} + +/** + * @tc.name: HandleCancelDelayedExitTask_0100 + * @tc.desc: Test HandleCancelDelayedExitTask. + * @tc.type: FUNC + */ +HWTEST_F(AppMgrStubTest, HandleCancelDelayedExitTask_0100, TestSize.Level1) +{ + MessageParcel data; + MessageParcel reply; + MessageOption option; + + WriteInterfaceToken(data); + int32_t pid = 1234; + data.WriteInt32(pid); + + EXPECT_CALL(*mockAppMgrService_, CancelDelayedExitTask(_)) + .Times(1); + + auto ret = mockAppMgrService_->OnRemoteRequest( + static_cast(AppMgrInterfaceCode::CANCEL_DELAYED_EXIT_TASK), data, reply, option); + EXPECT_EQ(ret, NO_ERROR); +} } // namespace AppExecFwk } // namespace OHOS diff --git a/test/unittest/app_preloader_test/app_preloader_test.cpp b/test/unittest/app_preloader_test/app_preloader_test.cpp index f46df9c759..69e3f4baa3 100755 --- a/test/unittest/app_preloader_test/app_preloader_test.cpp +++ b/test/unittest/app_preloader_test/app_preloader_test.cpp @@ -629,16 +629,43 @@ HWTEST_F(AppPreloaderTest, AppPreloaderTest_GeneratePreloadExtensionRequest_0100 std::string abilityName = "InputService"; AAFwk::Want want; want.SetElementName(bundleName, abilityName); - AbilityInfo abilityInfo; int32_t userId = -9; // failed GetBundleAndHapInfo int32_t appIndex = 0; PreloadRequest request; - auto ret = manager->GeneratePreloadExtensionRequest(want, abilityInfo, userId, appIndex, request); + auto ret = manager->GeneratePreloadExtensionRequest(want, userId, appIndex, request); EXPECT_EQ(ret, AAFwk::GET_BUNDLE_INFO_FAILED); } +/** + * @tc.number: AppPreloaderTest_GeneratePreloadExtensionRequest_0200 + * @tc.desc: Test GeneratePreloadExtensionRequest with appIndex != 0 + * @tc.type: FUNC + * @tc.Function: GeneratePreloadExtensionRequest + * @tc.SubFunction: NA + * @tc.EnvConditions: NA + */ +HWTEST_F(AppPreloaderTest, AppPreloaderTest_GeneratePreloadExtensionRequest_0200, TestSize.Level2) +{ + TAG_LOGI(AAFwkTag::TEST, "AppPreloaderTest_GeneratePreloadExtensionRequest_0200 start."); + auto manager = std::make_shared(remoteClientManager_); + EXPECT_NE(manager, nullptr); + + std::string bundleName = "com.example.hmos.inputmethod"; + std::string abilityName = "InputService"; + AAFwk::Want want; + want.SetElementName(bundleName, abilityName); + + int32_t userId = 1; + int32_t appIndex = 1; + PreloadRequest request; + + auto ret = manager->GeneratePreloadExtensionRequest(want, userId, appIndex, request); + EXPECT_EQ(ret, ERR_OK); + TAG_LOGI(AAFwkTag::TEST, "AppPreloaderTest_GeneratePreloadExtensionRequest_0200 end."); +} + /** * @tc.number: AppPreloaderTest_GeneratePreloadExtensionRequest_0300 * @tc.desc: Test GeneratePreloadExtensionRequest works @@ -649,7 +676,7 @@ HWTEST_F(AppPreloaderTest, AppPreloaderTest_GeneratePreloadExtensionRequest_0100 */ HWTEST_F(AppPreloaderTest, AppPreloaderTest_GeneratePreloadExtensionRequest_0300, TestSize.Level2) { - TAG_LOGD(AAFwkTag::TEST, "AppPreloaderTest_GeneratePreloadExtensionRequest_0300 start."); + TAG_LOGI(AAFwkTag::TEST, "AppPreloaderTest_GeneratePreloadExtensionRequest_0300 start."); auto manager = std::make_shared(remoteClientManager_); EXPECT_NE(manager, nullptr); @@ -657,14 +684,74 @@ HWTEST_F(AppPreloaderTest, AppPreloaderTest_GeneratePreloadExtensionRequest_0300 std::string abilityName = "InputService"; AAFwk::Want want; want.SetElementName(bundleName, abilityName); - AbilityInfo abilityInfo; int32_t userId = 1; int32_t appIndex = 0; PreloadRequest request; - auto ret = manager->GeneratePreloadExtensionRequest(want, abilityInfo, userId, appIndex, request); + auto ret = manager->GeneratePreloadExtensionRequest(want, userId, appIndex, request); EXPECT_EQ(ret, ERR_OK); + TAG_LOGI(AAFwkTag::TEST, "AppPreloaderTest_GeneratePreloadExtensionRequest_0300 end."); +} + + +/** + * @tc.number: AppPreloaderTest_GeneratePreloadExtensionRequest_0400 + * @tc.desc: Test GeneratePreloadExtensionRequest with null remoteClientManager + * @tc.type: FUNC + * @tc.Function: GeneratePreloadExtensionRequest + * @tc.SubFunction: NA + * @tc.EnvConditions: NA + */ +HWTEST_F(AppPreloaderTest, AppPreloaderTest_GeneratePreloadExtensionRequest_0400, TestSize.Level2) +{ + TAG_LOGI(AAFwkTag::TEST, "AppPreloaderTest_GeneratePreloadExtensionRequest_0400 start."); + auto manager = std::make_shared(remoteClientManager_); + EXPECT_NE(manager, nullptr); + + std::string bundleName = "com.example.hmos.inputmethod"; + std::string abilityName = "InputService"; + AAFwk::Want want; + want.SetElementName(bundleName, abilityName); + + manager->remoteClientManager_ = nullptr; + int32_t userId = 1; + int32_t appIndex = 0; + PreloadRequest request; + + auto ret = manager->GeneratePreloadExtensionRequest(want, userId, appIndex, request); + manager->remoteClientManager_ = remoteClientManager_; + EXPECT_EQ(ret, ERR_INVALID_OPERATION); + TAG_LOGI(AAFwkTag::TEST, "AppPreloaderTest_GeneratePreloadExtensionRequest_0400 end."); +} + +/** + * @tc.number: AppPreloaderTest_GetAbilityInfo_0100 + * @tc.desc: Test GetAbilityInfo works correctly + * @tc.type: FUNC + * @tc.Function: GetAbilityInfo + * @tc.SubFunction: NA + * @tc.EnvConditions: NA + */ +HWTEST_F(AppPreloaderTest, AppPreloaderTest_GetAbilityInfo_0100, TestSize.Level2) +{ + TAG_LOGI(AAFwkTag::TEST, "AppPreloaderTest_GetAbilityInfo_0100 start."); + auto manager = std::make_shared(remoteClientManager_); + EXPECT_NE(manager, nullptr); + + std::string bundleName = "com.example.hmos.inputmethod"; + std::string abilityName = "InputService"; + AAFwk::Want want; + want.SetElementName(bundleName, abilityName); + + int32_t userId = 1; + int32_t appIndex = 0; + PreloadRequest request; + AbilityInfo abilityInfo; + + auto ret = manager->GetAbilityInfo(want, userId, appIndex, request, abilityInfo); + EXPECT_TRUE(ret); + TAG_LOGI(AAFwkTag::TEST, "AppPreloaderTest_GetAbilityInfo_0100 end."); } } // namespace AppExecFwk } // namespace OHOS diff --git a/test/unittest/app_preloader_test/include/bundle_mgr_helper.h b/test/unittest/app_preloader_test/include/bundle_mgr_helper.h index 61ef7526b3..4c7bcd9c19 100644 --- a/test/unittest/app_preloader_test/include/bundle_mgr_helper.h +++ b/test/unittest/app_preloader_test/include/bundle_mgr_helper.h @@ -111,6 +111,22 @@ public: } return true; } + + bool QueryExtensionAbilityInfos(const Want &want, const int32_t &flag, const int32_t &userId, + std::vector &extensionInfos) + { + ExtensionAbilityInfo extensionInfo; + extensionInfos.emplace_back(extensionInfo); + return true; + } + + ErrCode GetSandboxExtAbilityInfos(const Want &want, int32_t appIndex, int32_t flags, + int32_t userId, std::vector &extensionInfos) + { + ExtensionAbilityInfo extensionInfo; + extensionInfos.emplace_back(extensionInfo); + return ERR_OK; + } }; } // namespace AppExecFwk } // namespace OHOS diff --git a/test/unittest/app_running_manager_fourth_test/app_running_manager_fourth_test.cpp b/test/unittest/app_running_manager_fourth_test/app_running_manager_fourth_test.cpp index 2ddcc8f55f..6cbdf801ba 100644 --- a/test/unittest/app_running_manager_fourth_test/app_running_manager_fourth_test.cpp +++ b/test/unittest/app_running_manager_fourth_test/app_running_manager_fourth_test.cpp @@ -1554,6 +1554,137 @@ HWTEST_F(AppRunningManagerFourthTest, AppRunningManager_FindMasterProcessAppRunn EXPECT_EQ(ret, nullptr); } +/** + * @tc.name: AppRunningManager_FindMainProcessAppRunningRecord_0100 + * @tc.desc: Find main process record successfully when one record is main process + * @tc.type: FUNC + */ +HWTEST_F(AppRunningManagerFourthTest, AppRunningManager_FindMainProcessAppRunningRecord_0100, TestSize.Level1) +{ + int uid = 100; + BundleInfo bundleInfo; + std::shared_ptr recordOne = + appRunningManager_->CreateAppRunningRecord(appInfo_, PROCESS_NAME, bundleInfo, ""); + ASSERT_NE(recordOne, nullptr); + std::shared_ptr recordTwo = + appRunningManager_->CreateAppRunningRecord(appInfo_, PROCESS_NAME, bundleInfo, ""); + ASSERT_NE(recordTwo, nullptr); + std::shared_ptr recordThree = + appRunningManager_->CreateAppRunningRecord(appInfo_, PROCESS_NAME, bundleInfo, ""); + ASSERT_NE(recordThree, nullptr); + + recordOne->processType_ = ProcessType::NORMAL; + recordOne->SetMainProcess(true); + recordOne->SetUid(uid); + recordTwo->processType_ = ProcessType::NORMAL; + recordTwo->SetMainProcess(false); + recordTwo->SetUid(uid); + recordThree->processType_ = ProcessType::EXTENSION; + recordThree->SetMainProcess(false); + recordThree->SetUid(uid); + appRunningManager_->appRunningRecordMap_.clear(); + appRunningManager_->appRunningRecordMap_.insert(std::make_pair(ONE, recordOne)); + appRunningManager_->appRunningRecordMap_.insert(std::make_pair(TWO, recordTwo)); + appRunningManager_->appRunningRecordMap_.insert(std::make_pair(THREE, recordThree)); + auto ret = appRunningManager_->FindMainProcessAppRunningRecord(uid); + EXPECT_EQ(ret, recordOne); +} + +/** + * @tc.name: AppRunningManager_FindMainProcessAppRunningRecord_0200 + * @tc.desc: Return nullptr when no record is main process + * @tc.type: FUNC + */ +HWTEST_F(AppRunningManagerFourthTest, AppRunningManager_FindMainProcessAppRunningRecord_0200, TestSize.Level1) +{ + int uid = 100; + BundleInfo bundleInfo; + std::shared_ptr recordOne = + appRunningManager_->CreateAppRunningRecord(appInfo_, PROCESS_NAME, bundleInfo, ""); + ASSERT_NE(recordOne, nullptr); + std::shared_ptr recordTwo = + appRunningManager_->CreateAppRunningRecord(appInfo_, PROCESS_NAME, bundleInfo, ""); + ASSERT_NE(recordTwo, nullptr); + + recordOne->processType_ = ProcessType::NORMAL; + recordOne->SetMainProcess(false); + recordOne->SetUid(uid); + recordTwo->processType_ = ProcessType::EXTENSION; + recordTwo->SetMainProcess(false); + recordTwo->SetUid(uid); + appRunningManager_->appRunningRecordMap_.clear(); + appRunningManager_->appRunningRecordMap_.insert(std::make_pair(ONE, recordOne)); + appRunningManager_->appRunningRecordMap_.insert(std::make_pair(TWO, recordTwo)); + auto ret = appRunningManager_->FindMainProcessAppRunningRecord(uid); + EXPECT_EQ(ret, nullptr); +} + +/** + * @tc.name: AppRunningManager_FindMainProcessAppRunningRecord_0300 + * @tc.desc: Return nullptr when map is empty + * @tc.type: FUNC + */ +HWTEST_F(AppRunningManagerFourthTest, AppRunningManager_FindMainProcessAppRunningRecord_0300, TestSize.Level1) +{ + int uid = 100; + appRunningManager_->appRunningRecordMap_.clear(); + auto ret = appRunningManager_->FindMainProcessAppRunningRecord(uid); + EXPECT_EQ(ret, nullptr); +} + +/** + * @tc.name: AppRunningManager_FindMainProcessAppRunningRecord_0400 + * @tc.desc: Return nullptr when uid does not match any record + * @tc.type: FUNC + */ +HWTEST_F(AppRunningManagerFourthTest, AppRunningManager_FindMainProcessAppRunningRecord_0400, TestSize.Level1) +{ + int uid = 100; + int otherUid = 200; + BundleInfo bundleInfo; + std::shared_ptr recordOne = + appRunningManager_->CreateAppRunningRecord(appInfo_, PROCESS_NAME, bundleInfo, ""); + ASSERT_NE(recordOne, nullptr); + + recordOne->processType_ = ProcessType::NORMAL; + recordOne->SetMainProcess(true); + recordOne->SetUid(otherUid); + appRunningManager_->appRunningRecordMap_.clear(); + appRunningManager_->appRunningRecordMap_.insert(std::make_pair(ONE, recordOne)); + auto ret = appRunningManager_->FindMainProcessAppRunningRecord(uid); + EXPECT_EQ(ret, nullptr); +} + +/** + * @tc.name: AppRunningManager_FindMainProcessAppRunningRecord_0500 + * @tc.desc: Skip terminating records and find the next valid main process + * @tc.type: FUNC + */ +HWTEST_F(AppRunningManagerFourthTest, AppRunningManager_FindMainProcessAppRunningRecord_0500, TestSize.Level1) +{ + int uid = 100; + BundleInfo bundleInfo; + std::shared_ptr recordOne = + appRunningManager_->CreateAppRunningRecord(appInfo_, PROCESS_NAME, bundleInfo, ""); + ASSERT_NE(recordOne, nullptr); + std::shared_ptr recordTwo = + appRunningManager_->CreateAppRunningRecord(appInfo_, PROCESS_NAME, bundleInfo, ""); + ASSERT_NE(recordTwo, nullptr); + + recordOne->processType_ = ProcessType::NORMAL; + recordOne->SetMainProcess(true); + recordOne->SetUid(uid); + recordOne->SetTerminating(); + recordTwo->processType_ = ProcessType::NORMAL; + recordTwo->SetMainProcess(true); + recordTwo->SetUid(uid); + appRunningManager_->appRunningRecordMap_.clear(); + appRunningManager_->appRunningRecordMap_.insert(std::make_pair(ONE, recordOne)); + appRunningManager_->appRunningRecordMap_.insert(std::make_pair(TWO, recordTwo)); + auto ret = appRunningManager_->FindMainProcessAppRunningRecord(uid); + EXPECT_EQ(ret, recordTwo); +} + /** * @tc.name: AppRunningManager_IsAppRunningRecordValid_0100 * @tc.desc: NA diff --git a/test/unittest/app_service_extension_context_test/ability_manager_stub_mock.h b/test/unittest/app_service_extension_context_test/ability_manager_stub_mock.h index 3d63747e52..3281cd8331 100644 --- a/test/unittest/app_service_extension_context_test/ability_manager_stub_mock.h +++ b/test/unittest/app_service_extension_context_test/ability_manager_stub_mock.h @@ -330,8 +330,8 @@ public: } int StartAbilityByCall(const Want& want, const sptr& connect, - const sptr& callerToken, int32_t userId = DEFAULT_INVAL_VALUE, bool isSilent = false, - bool promotePriority = false, bool isVisible = false) override + const sptr& callerToken, int32_t accountId = DEFAULT_INVAL_VALUE, bool isSilent = false, + bool promotePriority = false, bool isVisible = false, uint64_t specifiedFullTokenId = 0) override { return 0; } diff --git a/test/unittest/app_utils_test/app_utils_test.cpp b/test/unittest/app_utils_test/app_utils_test.cpp index 6121331c97..12599b19c6 100644 --- a/test/unittest/app_utils_test/app_utils_test.cpp +++ b/test/unittest/app_utils_test/app_utils_test.cpp @@ -1094,5 +1094,47 @@ HWTEST_F(AppUtilsTest, IsSupportNativeUIAbility_0200, TestSize.Level2) appUtils.isSupportNativeUIAbility_.value = true; EXPECT_TRUE(appUtils.IsSupportNativeUIAbility()); } + +/** + * @tc.number: IsSupportDelayedProcessExit_0100 + * @tc.desc: Test IsSupportDelayedProcessExit when not loaded + * @tc.type: FUNC + */ +HWTEST_F(AppUtilsTest, IsSupportDelayedProcessExit_0100, TestSize.Level2) +{ + TAG_LOGI(AAFwkTag::TEST, "IsSupportDelayedProcessExit_0100 called."); + auto &appUtils = AAFwk::AppUtils::GetInstance(); + appUtils.isSupportDelayedProcessExit_.isLoaded = false; + appUtils.IsSupportDelayedProcessExit(); + EXPECT_TRUE(appUtils.isSupportDelayedProcessExit_.isLoaded); +} + +/** + * @tc.number: IsSupportDelayedProcessExit_0200 + * @tc.desc: Test IsSupportDelayedProcessExit when already loaded with true + * @tc.type: FUNC + */ +HWTEST_F(AppUtilsTest, IsSupportDelayedProcessExit_0200, TestSize.Level2) +{ + TAG_LOGI(AAFwkTag::TEST, "IsSupportDelayedProcessExit_0200 called."); + auto &appUtils = AAFwk::AppUtils::GetInstance(); + appUtils.isSupportDelayedProcessExit_.isLoaded = true; + appUtils.isSupportDelayedProcessExit_.value = true; + EXPECT_TRUE(appUtils.IsSupportDelayedProcessExit()); +} + +/** + * @tc.number: IsSupportDelayedProcessExit_0300 + * @tc.desc: Test IsSupportDelayedProcessExit when already loaded with false + * @tc.type: FUNC + */ +HWTEST_F(AppUtilsTest, IsSupportDelayedProcessExit_0300, TestSize.Level2) +{ + TAG_LOGI(AAFwkTag::TEST, "IsSupportDelayedProcessExit_0300 called."); + auto &appUtils = AAFwk::AppUtils::GetInstance(); + appUtils.isSupportDelayedProcessExit_.isLoaded = true; + appUtils.isSupportDelayedProcessExit_.value = false; + EXPECT_FALSE(appUtils.IsSupportDelayedProcessExit()); +} } // namespace AbilityRuntime } // namespace OHOS diff --git a/test/unittest/appkit/child_main_thread_first_test/child_main_thread_first_test.cpp b/test/unittest/appkit/child_main_thread_first_test/child_main_thread_first_test.cpp index 65b3220caf..88387066a3 100644 --- a/test/unittest/appkit/child_main_thread_first_test/child_main_thread_first_test.cpp +++ b/test/unittest/appkit/child_main_thread_first_test/child_main_thread_first_test.cpp @@ -174,6 +174,29 @@ HWTEST_F(ChildMainThreadFirstTest, HandleLoadNative_0200, TestSize.Level1) TAG_LOGI(AAFwkTag::TEST, "HandleLoadNative_0200 end."); } +/** + * @tc.number: HandleLoadNative_0300 + * @tc.desc: Test HandleLoadNative works with valid process info and args + * @tc.type: FUNC + */ +HWTEST_F(ChildMainThreadFirstTest, HandleLoadNative_0300, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "HandleLoadNative_0300 start."); + ChildMainThread childMainThread; + childMainThread.processInfo_ = std::make_shared(); + childMainThread.processInfo_->srcEntry = "libchildproc.so"; + childMainThread.processInfo_->entryFunc = "ChildProcessMain"; + childMainThread.processInfo_->bundleName = "com.ohos.test"; + childMainThread.processArgs_ = std::make_shared(); + childMainThread.appMgr_ = sptr(new (std::nothrow) MockAppMgrService()); + + childMainThread.HandleLoadNative(); + + EXPECT_NE(childMainThread.processInfo_, nullptr); + EXPECT_NE(childMainThread.processArgs_, nullptr); + TAG_LOGI(AAFwkTag::TEST, "HandleLoadNative_0300 end."); +} + /** * @tc.number: HandleRunNativeProc_0100 * @tc.desc: Test HandleLoadNative works @@ -191,6 +214,27 @@ HWTEST_F(ChildMainThreadFirstTest, HandleRunNativeProc_0100, TestSize.Level1) TAG_LOGI(AAFwkTag::TEST, "HandleRunNativeProc_0100 end."); } +/** + * @tc.number: HandleRunNativeProc_0200 + * @tc.desc: Test HandleRunNativeProc works with valid process info + * @tc.type: FUNC + */ +HWTEST_F(ChildMainThreadFirstTest, HandleRunNativeProc_0200, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "HandleRunNativeProc_0200 start."); + ChildMainThread childMainThread; + childMainThread.processInfo_ = std::make_shared(); + childMainThread.processInfo_->srcEntry = "libchildproc.so"; + childMainThread.processInfo_->bundleName = "com.ohos.test"; + childMainThread.appMgr_ = sptr(new (std::nothrow) MockAppMgrService()); + sptr mainProcessCb = sptr(new (std::nothrow) MockAbilityToken()); + + childMainThread.HandleRunNativeProc(mainProcessCb); + + EXPECT_NE(childMainThread.processInfo_, nullptr); + TAG_LOGI(AAFwkTag::TEST, "HandleRunNativeProc_0200 end."); +} + /** * @tc.number: OnLoadAbilityFinished_0100 * @tc.desc: Test HandleLoadNative works diff --git a/test/unittest/appkit/ohos_application_test/ohos_application_test.cpp b/test/unittest/appkit/ohos_application_test/ohos_application_test.cpp index fbfd516c24..71e4fba9b4 100644 --- a/test/unittest/appkit/ohos_application_test/ohos_application_test.cpp +++ b/test/unittest/appkit/ohos_application_test/ohos_application_test.cpp @@ -2263,5 +2263,31 @@ HWTEST_F(OHOSApplicationTest, AppExecFwk_OHOSApplicationTest_InitJSLeakWatcher_0 GTEST_LOG_(INFO) << "AppExecFwk_OHOSApplicationTest_InitJSLeakWatcher_0400 end."; } + +/** +* @tc.number: AppExecFwk_OHOSApplicationTest_OnTerminate_0100 +* @tc.name: OnTerminate +* @tc.desc: Verify OnTerminate with abilityRuntimeContext_ nullptr +*/ +HWTEST_F(OHOSApplicationTest, AppExecFwk_OHOSApplicationTest_OnTerminate_0100, TestSize.Level1) +{ + EXPECT_EQ(ohosApplication_->abilityRuntimeContext_, nullptr); + ohosApplication_->OnTerminate(); + EXPECT_EQ(ohosApplication_->abilityRuntimeContext_, nullptr); +} + +/** +* @tc.number: AppExecFwk_OHOSApplicationTest_OnTerminate_0200 +* @tc.name: OnTerminate +* @tc.desc: Verify OnTerminate with abilityRuntimeContext_ not nullptr +*/ +HWTEST_F(OHOSApplicationTest, AppExecFwk_OHOSApplicationTest_OnTerminate_0200, TestSize.Level1) +{ + auto abilityRuntimeContext = + std::make_shared(); + ohosApplication_->SetApplicationContext(abilityRuntimeContext); + ohosApplication_->OnTerminate(); + EXPECT_NE(ohosApplication_->abilityRuntimeContext_, nullptr); +} } // namespace AppExecFwk } // namespace OHOS diff --git a/test/unittest/application_context_test/application_context_test.cpp b/test/unittest/application_context_test/application_context_test.cpp index 1d86bd6929..23c47f4b22 100644 --- a/test/unittest/application_context_test/application_context_test.cpp +++ b/test/unittest/application_context_test/application_context_test.cpp @@ -2349,7 +2349,7 @@ HWTEST_F(ApplicationContextTest, AddNativeAbility_0100, TestSize.Level1) GTEST_LOG_(INFO) << "AddNativeAbility_0100 start"; ASSERT_NE(context_, nullptr); context_->nativeAbilities_.clear(); - auto wrapper = std::make_shared(); + auto wrapper = std::make_shared(); wrapper->instanceId = "100"; wrapper->abilityName = "TestAbility"; context_->AddNativeAbility("100", wrapper); @@ -2370,10 +2370,10 @@ HWTEST_F(ApplicationContextTest, AddNativeAbility_0200, TestSize.Level1) GTEST_LOG_(INFO) << "AddNativeAbility_0200 start"; ASSERT_NE(context_, nullptr); context_->nativeAbilities_.clear(); - auto wrapper1 = std::make_shared(); + auto wrapper1 = std::make_shared(); wrapper1->instanceId = "1"; wrapper1->abilityName = "Ability1"; - auto wrapper2 = std::make_shared(); + auto wrapper2 = std::make_shared(); wrapper2->instanceId = "2"; wrapper2->abilityName = "Ability2"; context_->AddNativeAbility("1", wrapper1); @@ -2394,10 +2394,10 @@ HWTEST_F(ApplicationContextTest, AddNativeAbility_0300, TestSize.Level1) GTEST_LOG_(INFO) << "AddNativeAbility_0300 start"; ASSERT_NE(context_, nullptr); context_->nativeAbilities_.clear(); - auto wrapper1 = std::make_shared(); + auto wrapper1 = std::make_shared(); wrapper1->instanceId = "1"; wrapper1->abilityName = "Ability1"; - auto wrapper2 = std::make_shared(); + auto wrapper2 = std::make_shared(); wrapper2->instanceId = "1"; wrapper2->abilityName = "AbilityOverwritten"; context_->AddNativeAbility("1", wrapper1); @@ -2434,7 +2434,7 @@ HWTEST_F(ApplicationContextTest, GetNativeAbility_0200, TestSize.Level1) GTEST_LOG_(INFO) << "GetNativeAbility_0200 start"; ASSERT_NE(context_, nullptr); context_->nativeAbilities_.clear(); - auto wrapper = std::make_shared(); + auto wrapper = std::make_shared(); wrapper->instanceId = "42"; wrapper->abilityName = "TestAbility"; context_->nativeAbilities_["42"] = wrapper; @@ -2457,7 +2457,7 @@ HWTEST_F(ApplicationContextTest, GetNativeAbility_0300, TestSize.Level1) GTEST_LOG_(INFO) << "GetNativeAbility_0300 start"; ASSERT_NE(context_, nullptr); context_->nativeAbilities_.clear(); - auto wrapper = std::make_shared(); + auto wrapper = std::make_shared(); wrapper->instanceId = "1"; context_->nativeAbilities_["1"] = wrapper; auto ret = context_->GetNativeAbility("999"); @@ -2477,7 +2477,7 @@ HWTEST_F(ApplicationContextTest, RemoveNativeAbility_0100, TestSize.Level1) GTEST_LOG_(INFO) << "RemoveNativeAbility_0100 start"; ASSERT_NE(context_, nullptr); context_->nativeAbilities_.clear(); - auto wrapper = std::make_shared(); + auto wrapper = std::make_shared(); wrapper->instanceId = "42"; context_->nativeAbilities_["42"] = wrapper; EXPECT_EQ(context_->nativeAbilities_.size(), 1u); @@ -2517,7 +2517,7 @@ HWTEST_F(ApplicationContextTest, AddGetRemoveNativeAbility_0100, TestSize.Level1 context_->nativeAbilities_.clear(); // Add - auto wrapper = std::make_shared(); + auto wrapper = std::make_shared(); wrapper->instanceId = "100"; wrapper->abilityName = "IntegrationTestAbility"; wrapper->env = reinterpret_cast(0x1234); @@ -2540,8 +2540,8 @@ HWTEST_F(ApplicationContextTest, AddGetRemoveNativeAbility_0100, TestSize.Level1 } namespace { -const NativeAbilityWrapper* receivedWrapper = nullptr; -void MockPostAbility(const NativeAbilityWrapper* wrapper) +const AbilityRuntime_NativeAbilityWrapper* receivedWrapper = nullptr; +void MockPostAbility(const AbilityRuntime_NativeAbilityWrapper* wrapper) { receivedWrapper = wrapper; } @@ -2575,7 +2575,7 @@ HWTEST_F(ApplicationContextTest, ApplicationContext_PostAbility_0200, TestSize.L ASSERT_NE(context_, nullptr); context_->nativeAbilities_.clear(); - auto wrapper = std::make_shared(); + auto wrapper = std::make_shared(); wrapper->instanceId = "42"; wrapper->abilityName = "TestAbility"; @@ -2600,7 +2600,7 @@ HWTEST_F(ApplicationContextTest, ApplicationContext_PostAbility_0300, TestSize.L ASSERT_NE(context_, nullptr); context_->nativeAbilities_.clear(); - auto wrapper = std::make_shared(); + auto wrapper = std::make_shared(); wrapper->instanceId = "100"; wrapper->abilityName = "TestPostAbilityIntegration"; @@ -2635,7 +2635,7 @@ HWTEST_F(ApplicationContextTest, ApplicationContext_DestroyAbility_0100, TestSiz ASSERT_NE(context_, nullptr); context_->nativeAbilities_.clear(); - auto wrapper = std::make_shared(); + auto wrapper = std::make_shared(); wrapper->instanceId = "42"; wrapper->abilityName = "TestAbility"; context_->AddNativeAbility("42", wrapper); @@ -2661,14 +2661,15 @@ HWTEST_F(ApplicationContextTest, ApplicationContext_DestroyAbility_0200, TestSiz ASSERT_NE(context_, nullptr); context_->nativeAbilities_.clear(); - auto wrapper = std::make_shared(); + auto wrapper = std::make_shared(); wrapper->instanceId = "100"; wrapper->abilityName = "TestDestroyAbility"; context_->AddNativeAbility("100", wrapper); auto thread = std::make_shared(); std::string receivedInstanceId; - thread->destroyAbilityFunc_ = [&receivedInstanceId](const NativeAbilityWrapper* nativeAbilityWrapper) { + thread->destroyAbilityFunc_ = [&receivedInstanceId]( + const AbilityRuntime_NativeAbilityWrapper *nativeAbilityWrapper) { receivedInstanceId = nativeAbilityWrapper->instanceId; }; context_->abilityNativeThread_ = thread; @@ -2941,7 +2942,7 @@ HWTEST_F(ApplicationContextTest, AbilityNativeThread_PostAbility_0200, TestSize. GTEST_LOG_(INFO) << "AbilityNativeThread_PostAbility_0200 start"; auto thread = std::make_shared(); thread->postAbilityFunc_ = nullptr; - NativeAbilityWrapper wrapper; + AbilityRuntime_NativeAbilityWrapper wrapper; wrapper.instanceId = "1"; wrapper.abilityName = "Test"; thread->PostAbility(&wrapper); @@ -2961,7 +2962,7 @@ HWTEST_F(ApplicationContextTest, AbilityNativeThread_PostAbility_0300, TestSize. thread->postAbilityFunc_ = MockPostAbility; - NativeAbilityWrapper wrapper; + AbilityRuntime_NativeAbilityWrapper wrapper; wrapper.instanceId = "42"; wrapper.abilityName = "TestPostAbility"; wrapper.env = reinterpret_cast(0x1234); @@ -2987,7 +2988,7 @@ HWTEST_F(ApplicationContextTest, AbilityNativeThread_DestroyAbility_0100, TestSi { GTEST_LOG_(INFO) << "AbilityNativeThread_DestroyAbility_0100 start"; auto thread = std::make_shared(); - NativeAbilityWrapper wrapper; + AbilityRuntime_NativeAbilityWrapper wrapper; wrapper.instanceId = "1"; wrapper.abilityName = "Test"; thread->DestroyAbility(&wrapper); @@ -3005,7 +3006,8 @@ HWTEST_F(ApplicationContextTest, AbilityNativeThread_DestroyAbility_0200, TestSi GTEST_LOG_(INFO) << "AbilityNativeThread_DestroyAbility_0200 start"; auto thread = std::make_shared(); std::string receivedInstanceId; - thread->destroyAbilityFunc_ = [&receivedInstanceId](const NativeAbilityWrapper* nativeAbilityWrapper) { + thread->destroyAbilityFunc_ = [&receivedInstanceId]( + const AbilityRuntime_NativeAbilityWrapper *nativeAbilityWrapper) { receivedInstanceId = nativeAbilityWrapper->instanceId; }; thread->DestroyAbility(nullptr); @@ -3024,12 +3026,12 @@ HWTEST_F(ApplicationContextTest, AbilityNativeThread_DestroyAbility_0300, TestSi GTEST_LOG_(INFO) << "AbilityNativeThread_DestroyAbility_0300 start"; auto thread = std::make_shared(); - const NativeAbilityWrapper* receivedWrapper = nullptr; - thread->destroyAbilityFunc_ = [&receivedWrapper](const NativeAbilityWrapper* nativeAbilityWrapper) { + const AbilityRuntime_NativeAbilityWrapper* receivedWrapper = nullptr; + thread->destroyAbilityFunc_ = [&receivedWrapper](const AbilityRuntime_NativeAbilityWrapper* nativeAbilityWrapper) { receivedWrapper = nativeAbilityWrapper; }; - NativeAbilityWrapper wrapper; + AbilityRuntime_NativeAbilityWrapper wrapper; wrapper.instanceId = "42"; wrapper.abilityName = "TestDestroyAbility"; @@ -3125,5 +3127,94 @@ HWTEST_F(ApplicationContextTest, AbilityNativeThread_Destructor_0200, TestSize.L } GTEST_LOG_(INFO) << "AbilityNativeThread_Destructor_0200 end"; } + +/** + * @tc.number: IsDelayedProcessExitPending_0100 + * @tc.name: IsDelayedProcessExitPending + * @tc.desc: Test IsDelayedProcessExitPending returns false by default + */ +HWTEST_F(ApplicationContextTest, IsDelayedProcessExitPending_0100, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "IsDelayedProcessExitPending_0100 start"; + context_->delayedProcessExitEnabled_ = false; + EXPECT_FALSE(context_->IsDelayedProcessExitPending()); + GTEST_LOG_(INFO) << "IsDelayedProcessExitPending_0100 end"; +} + +/** + * @tc.number: IsDelayedProcessExitPending_0200 + * @tc.name: IsDelayedProcessExitPending + * @tc.desc: Test IsDelayedProcessExitPending returns true when enabled + */ +HWTEST_F(ApplicationContextTest, IsDelayedProcessExitPending_0200, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "IsDelayedProcessExitPending_0200 start"; + context_->delayedProcessExitEnabled_ = true; + EXPECT_TRUE(context_->IsDelayedProcessExitPending()); + GTEST_LOG_(INFO) << "IsDelayedProcessExitPending_0200 end"; +} + +/** + * @tc.number: StartSelfUIAbility_0100 + * @tc.name: StartSelfUIAbility + * @tc.desc: Test StartSelfUIAbility when delayed process exit is not pending + */ +HWTEST_F(ApplicationContextTest, StartSelfUIAbility_0100, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "StartSelfUIAbility_0100 start"; + context_->delayedProcessExitEnabled_ = false; + AAFwk::Want want; + auto ret = context_->StartSelfUIAbility(want); + EXPECT_EQ(ret, AAFwk::ERR_DELAYED_PROCESS_EXIT_NOT_PENDING); + GTEST_LOG_(INFO) << "StartSelfUIAbility_0100 end"; +} + +/** + * @tc.number: StartSelfUIAbility_0200 + * @tc.name: StartSelfUIAbility + * @tc.desc: Test StartSelfUIAbility when delayed process exit is pending + */ +HWTEST_F(ApplicationContextTest, StartSelfUIAbility_0200, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "StartSelfUIAbility_0200 start"; + context_->delayedProcessExitEnabled_ = true; + AAFwk::Want want; + want.SetElementName("com.example.bundle", "com.example.ability"); + auto ret = context_->StartSelfUIAbility(want); + EXPECT_NE(ret, AAFwk::ERR_DELAYED_PROCESS_EXIT_NOT_PENDING); + GTEST_LOG_(INFO) << "StartSelfUIAbility_0200 end"; +} + +/** + * @tc.number: EnableDelayedProcessExit_0100 + * @tc.name: EnableDelayedProcessExit + * @tc.desc: Test EnableDelayedProcessExit calls service (service returns non-OK in test env) + */ +HWTEST_F(ApplicationContextTest, EnableDelayedProcessExit_0100, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "EnableDelayedProcessExit_0100 start"; + context_->delayedProcessExitEnabled_ = false; + auto ret = context_->EnableDelayedProcessExit(); + EXPECT_NE(ret, ERR_OK); + + EXPECT_FALSE(context_->delayedProcessExitEnabled_); + GTEST_LOG_(INFO) << "EnableDelayedProcessExit_0100 end"; +} + +/** + * @tc.number: DisableDelayedProcessExit_0100 + * @tc.name: DisableDelayedProcessExit + * @tc.desc: Test DisableDelayedProcessExit calls service (service returns non-OK in test env) + */ +HWTEST_F(ApplicationContextTest, DisableDelayedProcessExit_0100, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "DisableDelayedProcessExit_0100 start"; + context_->delayedProcessExitEnabled_ = true; + auto ret = context_->DisableDelayedProcessExit(); + EXPECT_NE(ret, ERR_OK); + + EXPECT_TRUE(context_->delayedProcessExitEnabled_); + GTEST_LOG_(INFO) << "DisableDelayedProcessExit_0100 end"; +} } // namespace AbilityRuntime } // namespace OHOS diff --git a/test/unittest/arkts_script_test/BUILD.gn b/test/unittest/arkts_script_test/BUILD.gn new file mode 100644 index 0000000000..6584482b5b --- /dev/null +++ b/test/unittest/arkts_script_test/BUILD.gn @@ -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. + +import("//build/ohos.gni") +import("//build/test.gni") +import("//foundation/ability/ability_runtime/ability_runtime.gni") + +module_output_path = "ability_runtime/ability_runtime/ohos-arkts" + +ohos_unittest("ohos-arkts-test") { + module_out_path = module_output_path + + include_dirs = [ + "${ability_runtime_path}/tools/ohos-arktsScript/include", + "${ability_runtime_innerkits_path}/runtime/include", + ] + + sources = [ + "arkts_script_test.cpp", + ] + + deps = [ + "${ability_runtime_path}/tools/ohos-arktsScript:arkts_script", + ] + + external_deps = [ + "ability_base:base", + "ability_base:configuration", + "bundle_framework:appexecfwk_base", + "bundle_framework:appexecfwk_core", + "c_utils:utils", + "eventhandler:libeventhandler", + "googletest:gmock_main", + "googletest:gtest_main", + "hilog:libhilog", + "ipc:ipc_core", + "json:nlohmann_json_static", + "napi:ace_napi", + ] +} + +group("unittest") { + testonly = true + deps = [ ":ohos-arkts-test" ] +} diff --git a/test/unittest/arkts_script_test/arkts_script_test.cpp b/test/unittest/arkts_script_test/arkts_script_test.cpp new file mode 100644 index 0000000000..d40ea2c022 --- /dev/null +++ b/test/unittest/arkts_script_test/arkts_script_test.cpp @@ -0,0 +1,457 @@ +/* + * 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 + +#include +#include +#include +#include +#include +#include +#include + +#define private public +#include "arkts_script.h" +#undef private + +using namespace testing; +using namespace testing::ext; + +namespace OHOS { +namespace ArktsScript { +namespace { + +std::vector BuildArgv(std::vector& args) +{ + std::vector argv; + argv.reserve(args.size()); + for (auto& arg : args) { + argv.push_back(arg.data()); + } + return argv; +} + +class ScopedCoutRedirect { +public: + ScopedCoutRedirect() : oldBuffer_(std::cout.rdbuf(stream_.rdbuf())) {} + ~ScopedCoutRedirect() + { + std::cout.rdbuf(oldBuffer_); + } + + std::string GetOutput() const + { + return stream_.str(); + } + +private: + std::ostringstream stream_; + std::streambuf* oldBuffer_ = nullptr; +}; + +class ScopedCerrRedirect { +public: + ScopedCerrRedirect() : oldBuffer_(std::cerr.rdbuf(stream_.rdbuf())) {} + ~ScopedCerrRedirect() + { + std::cerr.rdbuf(oldBuffer_); + } + + std::string GetOutput() const + { + return stream_.str(); + } + +private: + std::ostringstream stream_; + std::streambuf* oldBuffer_ = nullptr; +}; + +} // namespace + +class ArktsScriptTest : public testing::Test { +public: + void TearDown() override + { + ArktsScript::CloseCompletionChannel(channel_); + } + +protected: + CompletionChannel channel_; +}; + +/** + * @tc.name: ParseArguments_0100 + * @tc.desc: Parse required options and json object arguments. + * @tc.type: FUNC + */ +HWTEST_F(ArktsScriptTest, ParseArguments_0100, TestSize.Level1) +{ + std::vector args = { + "ohos-arktsScript", + "--abcPath", + "/data/test/module.abc", + "--scriptPath", + "entry/src/main/ets/TestScript.ets", + "--functionName", + "calculate", + "--args", + R"({"arg0":"text","arg1":true,"arg2":2147483648,"arg4":{"name":"tool"},"arg5":["a","b"]})", + }; + std::vector argv = BuildArgv(args); + ScriptArgs parsedArgs; + + ASSERT_TRUE(ArktsScript::ParseArguments(static_cast(argv.size()), argv.data(), parsedArgs)); + EXPECT_EQ(parsedArgs.abcPath, "/data/test/module.abc"); + EXPECT_EQ(parsedArgs.scriptName, "entry/src/main/ets/TestScript.ets"); + EXPECT_EQ(parsedArgs.funName, "calculate"); + ASSERT_EQ(parsedArgs.arguments.size(), 6U); + EXPECT_EQ(parsedArgs.arguments[0].type, ScriptArgType::STRING); + EXPECT_EQ(parsedArgs.arguments[0].value, "text"); + EXPECT_EQ(parsedArgs.arguments[1].type, ScriptArgType::BOOLEAN); + EXPECT_EQ(parsedArgs.arguments[1].value, "1"); + EXPECT_EQ(parsedArgs.arguments[2].type, ScriptArgType::DOUBLE); + EXPECT_EQ(parsedArgs.arguments[2].value, "2147483648"); + EXPECT_EQ(parsedArgs.arguments[3].type, ScriptArgType::UNDEFINED); + EXPECT_EQ(parsedArgs.arguments[4].type, ScriptArgType::JSON_VALUE); + EXPECT_EQ(parsedArgs.arguments[4].value, R"({"name":"tool"})"); + EXPECT_EQ(parsedArgs.arguments[5].type, ScriptArgType::JSON_VALUE); + EXPECT_EQ(parsedArgs.arguments[5].value, R"(["a","b"])"); +} + +/** + * @tc.name: ParseArguments_0200 + * @tc.desc: Parse help option. + * @tc.type: FUNC + */ +HWTEST_F(ArktsScriptTest, ParseArguments_0200, TestSize.Level1) +{ + std::vector args = { + "ohos-arktsScript", + "--help", + }; + std::vector argv = BuildArgv(args); + ScriptArgs parsedArgs; + + EXPECT_TRUE(ArktsScript::ParseArguments(static_cast(argv.size()), argv.data(), parsedArgs)); + EXPECT_TRUE(parsedArgs.showHelp); +} + +/** + * @tc.name: ParseArguments_0300 + * @tc.desc: Reject invalid argument forms. + * @tc.type: FUNC + */ +HWTEST_F(ArktsScriptTest, ParseArguments_0300, TestSize.Level1) +{ + ScriptArgs parsedArgs; + EXPECT_FALSE(ArktsScript::ParseArguments(0, nullptr, parsedArgs)); + + std::vector noOption = { "ohos-arktsScript" }; + std::vector noOptionArgv = BuildArgv(noOption); + EXPECT_FALSE(ArktsScript::ParseArguments(static_cast(noOptionArgv.size()), noOptionArgv.data(), parsedArgs)); + + std::vector missingFunction = { "ohos-arktsScript", "--abcPath", "/data/test/module.abc" }; + std::vector missingFunctionArgv = BuildArgv(missingFunction); + EXPECT_FALSE(ArktsScript::ParseArguments(static_cast(missingFunctionArgv.size()), + missingFunctionArgv.data(), parsedArgs)); + + std::vector unknownOption = { + "ohos-arktsScript", "--abcPath", "/data/test/module.abc", "--functionName", "run", "--unknown" + }; + std::vector unknownOptionArgv = BuildArgv(unknownOption); + EXPECT_FALSE(ArktsScript::ParseArguments(static_cast(unknownOptionArgv.size()), + unknownOptionArgv.data(), parsedArgs)); + + std::vector badArgsJson = { + "ohos-arktsScript", "--abcPath", "/data/test/module.abc", "--functionName", "run", "--args", "[]" + }; + std::vector badArgsJsonArgv = BuildArgv(badArgsJson); + EXPECT_FALSE(ArktsScript::ParseArguments(static_cast(badArgsJsonArgv.size()), + badArgsJsonArgv.data(), parsedArgs)); + + std::vector badArgName = { + "ohos-arktsScript", "--abcPath", "/data/test/module.abc", "--functionName", "run", "--args", + R"({"argv0":1})" + }; + std::vector badArgNameArgv = BuildArgv(badArgName); + EXPECT_FALSE(ArktsScript::ParseArguments(static_cast(badArgNameArgv.size()), + badArgNameArgv.data(), parsedArgs)); + + std::vector badArgValue = { + "ohos-arktsScript", "--abcPath", "/data/test/module.abc", "--functionName", "run", "--args", + R"({"arg0":null})" + }; + std::vector badArgValueArgv = BuildArgv(badArgValue); + EXPECT_FALSE(ArktsScript::ParseArguments(static_cast(badArgValueArgv.size()), + badArgValueArgv.data(), parsedArgs)); +} + +/** + * @tc.name: ParseFunctionAndArguments_0100 + * @tc.desc: Reject missing option value and duplicated args json. + * @tc.type: FUNC + */ +HWTEST_F(ArktsScriptTest, ParseFunctionAndArguments_0100, TestSize.Level1) +{ + std::vector missingValue = { "ohos-arktsScript", "--abcPath" }; + std::vector missingValueArgv = BuildArgv(missingValue); + ScriptArgs args; + int index = 1; + EXPECT_FALSE(ArktsScript::ParseFunctionAndArguments(static_cast(missingValueArgv.size()), + missingValueArgv.data(), index, args)); + + std::vector duplicatedArgs = { + "ohos-arktsScript", "--abcPath", "/data/test/module.abc", "--functionName", "run", "--args", + R"({"arg0":1})", "--args", R"({"arg1":2})" + }; + std::vector duplicatedArgsArgv = BuildArgv(duplicatedArgs); + index = 1; + EXPECT_FALSE(ArktsScript::ParseFunctionAndArguments(static_cast(duplicatedArgsArgv.size()), + duplicatedArgsArgv.data(), index, args)); +} + +/** + * @tc.name: OutputResult_0100 + * @tc.desc: Output result to stdout. + * @tc.type: FUNC + */ +HWTEST_F(ArktsScriptTest, OutputResult_0100, TestSize.Level1) +{ + ScopedCoutRedirect coutRedirect; + ArktsScript::OutputResult("ok"); + EXPECT_EQ(coutRedirect.GetOutput(), "ok\n"); +} + +/** + * @tc.name: OutputError_0100 + * @tc.desc: Output error to stderr in JSON format. + * @tc.type: FUNC + */ +HWTEST_F(ArktsScriptTest, OutputError_0100, TestSize.Level1) +{ + ScopedCerrRedirect cerrRedirect; + ArktsScript::OutputError({ "failed", "" }); + EXPECT_EQ(cerrRedirect.GetOutput(), R"({"error":"failed","errorType":"EXECUTION_ERROR","success":false})" "\n"); +} + +/** + * @tc.name: CompletionChannel_Create_0100 + * @tc.desc: Create completion channel. + * @tc.type: FUNC + */ +HWTEST_F(ArktsScriptTest, CompletionChannel_Create_0100, TestSize.Level1) +{ + ASSERT_TRUE(ArktsScript::CreateCompletionChannel(channel_)); + EXPECT_GE(channel_.eventFd, 0); + EXPECT_GE(channel_.epollFd, 0); +} + +/** + * @tc.name: CompletionChannel_Signal_0100 + * @tc.desc: Signal and read completion event. + * @tc.type: FUNC + */ +HWTEST_F(ArktsScriptTest, CompletionChannel_Signal_0100, TestSize.Level1) +{ + ASSERT_TRUE(ArktsScript::CreateCompletionChannel(channel_)); + EXPECT_TRUE(ArktsScript::SignalCompletion(channel_.eventFd)); + ArktsScript::ReadCompletionSignal(channel_); +} + +/** + * @tc.name: CompletionChannel_Close_0100 + * @tc.desc: Close completion channel and verify cleanup. + * @tc.type: FUNC + */ +HWTEST_F(ArktsScriptTest, CompletionChannel_Close_0100, TestSize.Level1) +{ + ASSERT_TRUE(ArktsScript::CreateCompletionChannel(channel_)); + ArktsScript::CloseCompletionChannel(channel_); + EXPECT_EQ(channel_.eventFd, -1); + EXPECT_EQ(channel_.epollFd, -1); + EXPECT_FALSE(ArktsScript::SignalCompletion(channel_.eventFd)); +} + +/** + * @tc.name: ExecutionContext_PublishResult_0100 + * @tc.desc: Publish result to execution context. + * @tc.type: FUNC + */ +HWTEST_F(ArktsScriptTest, ExecutionContext_PublishResult_0100, TestSize.Level1) +{ + auto execContext = std::make_shared(); + + EXPECT_FALSE(ArktsScript::PublishResult(nullptr, -1, "result")); + EXPECT_TRUE(ArktsScript::PublishResult(execContext, -1, "result")); + EXPECT_FALSE(ArktsScript::PublishResult(execContext, -1, "again")); + + ExecutionSnapshot snapshot = ArktsScript::TakeExecutionSnapshot(execContext); + EXPECT_TRUE(snapshot.resultReady); + EXPECT_EQ(snapshot.result, "result"); +} + +/** + * @tc.name: ExecutionContext_PublishFailure_0100 + * @tc.desc: Publish failure to execution context. + * @tc.type: FUNC + */ +HWTEST_F(ArktsScriptTest, ExecutionContext_PublishFailure_0100, TestSize.Level1) +{ + auto execContext = std::make_shared(); + + EXPECT_TRUE(ArktsScript::PublishFailure(execContext, -1, { "failed", "TEST_ERROR" })); + EXPECT_FALSE(ArktsScript::PublishFailure(execContext, -1, { "again", "TEST_ERROR" })); + + ExecutionSnapshot snapshot = ArktsScript::TakeExecutionSnapshot(execContext); + EXPECT_TRUE(snapshot.scriptDone); + EXPECT_EQ(snapshot.error.message, "failed"); + EXPECT_EQ(snapshot.error.type, "TEST_ERROR"); +} + +/** + * @tc.name: ExecutionContext_MarkScriptDone_0100 + * @tc.desc: Mark script as done in execution context. + * @tc.type: FUNC + */ +HWTEST_F(ArktsScriptTest, ExecutionContext_MarkScriptDone_0100, TestSize.Level1) +{ + auto execContext = std::make_shared(); + + EXPECT_FALSE(ArktsScript::MarkScriptDone(nullptr, -1)); + EXPECT_TRUE(ArktsScript::MarkScriptDone(execContext, -1)); + + ExecutionSnapshot snapshot = ArktsScript::TakeExecutionSnapshot(execContext); + EXPECT_TRUE(snapshot.scriptDone); +} + +/** + * @tc.name: ExecutionContext_TakeSnapshot_0100 + * @tc.desc: Take execution snapshot from context. + * @tc.type: FUNC + */ +HWTEST_F(ArktsScriptTest, ExecutionContext_TakeSnapshot_0100, TestSize.Level1) +{ + auto execContext = std::make_shared(); + ArktsScript::PublishResult(execContext, -1, "test_result"); + + ExecutionSnapshot snapshot = ArktsScript::TakeExecutionSnapshot(execContext); + EXPECT_TRUE(snapshot.resultReady); + EXPECT_FALSE(snapshot.scriptDone); + EXPECT_EQ(snapshot.result, "test_result"); + EXPECT_TRUE(snapshot.error.message.empty()); + + EXPECT_FALSE(ArktsScript::TakeExecutionSnapshot(nullptr).resultReady); +} + +/** + * @tc.name: Callback_Success_0100 + * @tc.desc: Complete callback records success result. + * @tc.type: FUNC + */ +HWTEST_F(ArktsScriptTest, Callback_Success_0100, TestSize.Level1) +{ + auto execContext = std::make_shared(); + CompletionChannel channel; + ResultCallback callback = ArktsScript::CreateCompleteCallback(execContext, channel); + + callback(true, "done", {}); + ExecutionSnapshot snapshot = ArktsScript::TakeExecutionSnapshot(execContext); + EXPECT_TRUE(snapshot.resultReady); + EXPECT_EQ(snapshot.result, "done"); +} + +/** + * @tc.name: Callback_Failure_0100 + * @tc.desc: Complete callback records failure and ignores duplicated failure. + * @tc.type: FUNC + */ +HWTEST_F(ArktsScriptTest, Callback_Failure_0100, TestSize.Level1) +{ + auto execContext = std::make_shared(); + CompletionChannel channel; + ResultCallback callback = ArktsScript::CreateCompleteCallback(execContext, channel); + + callback(false, "", { "failed", "CALLBACK_ERROR" }); + ExecutionSnapshot snapshot = ArktsScript::TakeExecutionSnapshot(execContext); + EXPECT_EQ(snapshot.error.message, "failed"); + EXPECT_EQ(snapshot.error.type, "CALLBACK_ERROR"); +} + +/** + * @tc.name: RuntimeFacade_0100 + * @tc.desc: Cover public runtime facade failure paths that do not require a JS engine. + * @tc.type: FUNC + */ +HWTEST_F(ArktsScriptTest, RuntimeFacade_0100, TestSize.Level1) +{ + EXPECT_FALSE(ArktsScript::LoadAbcFile(nullptr, "/data/test/module.abc")); + + ScriptError error; + ScriptArgs args; + args.abcPath = "/data/test/module.abc"; + EXPECT_FALSE(ArktsScript::LoadScriptFile(args, nullptr, error)); + EXPECT_EQ(error.type, "LOAD_ERROR"); + + std::vector invalidArgs = { "ohos-arktsScript", "--abcPath" }; + std::vector argv = BuildArgv(invalidArgs); + EXPECT_EQ(ArktsScript::RunArkTsScript(static_cast(argv.size()), argv.data()), EXIT_FAILURE); +} + +/** + * @tc.name: ExecuteFlowHelpers_0100 + * @tc.desc: Cover safe failure paths for execution flow helper interfaces. + * @tc.type: FUNC + */ +HWTEST_F(ArktsScriptTest, ExecuteFlowHelpers_0100, TestSize.Level1) +{ + ScriptArgs args; + args.abcPath = "/data/test/module.abc"; + args.funName = "run"; + napi_value receiver = nullptr; + napi_value func = nullptr; + ScriptError error; + + EXPECT_FALSE(ArktsScript::ResolveScriptFunction(args, nullptr, receiver, func, error)); + EXPECT_EQ(error.type, "FUNCTION_ERROR"); + + error = {}; + EXPECT_FALSE(ArktsScript::CallResolvedFunction(args, nullptr, receiver, func, error)); + EXPECT_EQ(error.type, "ARGUMENT_ERROR"); + + CompletionChannel channel; + std::thread monitorThread; + EXPECT_EQ(ArktsScript::FinalizeAndJoin(nullptr, channel, monitorThread, { "failed", "TEST_ERROR" }), + EXIT_FAILURE); +} + +/** + * @tc.name: SnapshotHandler_0100 + * @tc.desc: Handle incomplete snapshots without exiting monitor process. + * @tc.type: FUNC + */ +HWTEST_F(ArktsScriptTest, SnapshotHandler_0100, TestSize.Level1) +{ + ExecutionSnapshot snapshot; + snapshot.resultReady = true; + snapshot.scriptDone = false; + CompletionChannel channel; + + EXPECT_NO_FATAL_FAILURE(ArktsScript::HandleExecutionSnapshot(snapshot, channel)); +} + +} // namespace ArktsScript +} // namespace OHOS diff --git a/test/unittest/c_modular_object_utils_test/c_modular_object_utils_test.cpp b/test/unittest/c_modular_object_utils_test/c_modular_object_utils_test.cpp index 073d22a224..dff1821786 100644 --- a/test/unittest/c_modular_object_utils_test/c_modular_object_utils_test.cpp +++ b/test/unittest/c_modular_object_utils_test/c_modular_object_utils_test.cpp @@ -134,15 +134,6 @@ HWTEST_F(CModularObjectUtilsTest, ConvertConnectBusinessErrorCode_003, TestSize. GTEST_LOG_(INFO) << "ConvertConnectBusinessErrorCode_003 end"; } -HWTEST_F(CModularObjectUtilsTest, ConvertConnectBusinessErrorCode_004, TestSize.Level1) -{ - GTEST_LOG_(INFO) << "ConvertConnectBusinessErrorCode_004 start"; - // ERR_INVALID_DISTRIBUTION_TYPE -> INVALID_DISTRIBUTION_TYPE - auto ret = CModularObjectUtils::ConvertConnectBusinessErrorCode(ERR_INVALID_DISTRIBUTION_TYPE); - EXPECT_EQ(ret, ABILITY_RUNTIME_ERROR_CODE_INVALID_DISTRIBUTION_TYPE); - GTEST_LOG_(INFO) << "ConvertConnectBusinessErrorCode_004 end"; -} - HWTEST_F(CModularObjectUtilsTest, ConvertConnectBusinessErrorCode_005, TestSize.Level1) { GTEST_LOG_(INFO) << "ConvertConnectBusinessErrorCode_005 start"; @@ -416,10 +407,10 @@ HWTEST_F(CModularObjectUtilsTest, NotifyFailed_005, TestSize.Level1) state->owner = nullptr; state->onFailedCallback = MockOnFailedCallback; - int32_t businessCode = ABILITY_RUNTIME_ERROR_CODE_INVALID_DISTRIBUTION_TYPE; + int32_t businessCode = ABILITY_RUNTIME_ERROR_CODE_CROSS_APP_IN_PROCESS; CModularObjectUtils::NotifyFailed(state, businessCode); EXPECT_EQ(g_callbackCallCount, 1); - EXPECT_EQ(g_capturedErrorCode, ABILITY_RUNTIME_ERROR_CODE_INVALID_DISTRIBUTION_TYPE); + EXPECT_EQ(g_capturedErrorCode, ABILITY_RUNTIME_ERROR_CODE_CROSS_APP_IN_PROCESS); GTEST_LOG_(INFO) << "NotifyFailed_005 end"; } diff --git a/test/unittest/cache_process_manager_second_test/BUILD.gn b/test/unittest/cache_process_manager_second_test/BUILD.gn index ca87eb8084..da368737c4 100644 --- a/test/unittest/cache_process_manager_second_test/BUILD.gn +++ b/test/unittest/cache_process_manager_second_test/BUILD.gn @@ -147,6 +147,7 @@ ohos_unittest("cache_process_manager_second_test") { "init:libbeget_proxy", "init:libbegetutil", "ipc:ipc_core", + "ipc:ipc_single", "json:nlohmann_json_static", "kv_store:distributeddata_inner", "memmgr:memmgrclient", diff --git a/test/unittest/cache_process_manager_second_test/mock/src/mock_app_running_record.cpp b/test/unittest/cache_process_manager_second_test/mock/src/mock_app_running_record.cpp index b644837195..f076e3018c 100644 --- a/test/unittest/cache_process_manager_second_test/mock/src/mock_app_running_record.cpp +++ b/test/unittest/cache_process_manager_second_test/mock/src/mock_app_running_record.cpp @@ -1042,6 +1042,16 @@ std::string AppRunningRecord::GetPreloadModuleName() const return preloadModuleName_; } +void AppRunningRecord::SetPreloadAbilityName(const std::string &abilityName) +{ + preloadAbilityName_ = abilityName; +} + +std::string AppRunningRecord::GetPreloadAbilityName() const +{ + return preloadAbilityName_; +} + void AppRunningRecord::SetPreloadState(PreloadState state) { preloadState_ = state; diff --git a/test/unittest/child_process_capi_test/BUILD.gn b/test/unittest/child_process_capi_test/BUILD.gn index e1582b388b..d9571aeb13 100644 --- a/test/unittest/child_process_capi_test/BUILD.gn +++ b/test/unittest/child_process_capi_test/BUILD.gn @@ -17,6 +17,7 @@ import("//foundation/ability/ability_runtime/ability_runtime.gni") module_output_path = "ability_runtime/ability_runtime/child_process_capi" ohos_unittest("child_process_capi_test") { + use_exceptions = true module_out_path = module_output_path configs = [ "${ability_runtime_services_path}/common:common_config" ] @@ -31,9 +32,19 @@ ohos_unittest("child_process_capi_test") { "${ability_runtime_native_path}/child_process/include", ] - sources = [ "child_process_capi_test.cpp" ] + sources = [ + "child_process_capi_test.cpp", + "${ability_runtime_native_path}/child_process/src/child_callback_manager.cpp", + "${ability_runtime_native_path}/child_process/src/native_child_callback.cpp", + "${ability_runtime_native_path}/child_process/src/native_child_process.cpp", + ] - deps = [ "${ability_runtime_services_path}/common:app_util_static" ] + deps = [ + "${ability_runtime_services_path}/common:app_util_static", + "${ability_runtime_innerkits_path}/app_manager:app_manager", + "${ability_runtime_innerkits_path}/runtime:runtime", + "${ability_runtime_native_path}/ability/native:ability_business_error", + ] external_deps = [ "c_utils:utils", @@ -45,8 +56,6 @@ ohos_unittest("child_process_capi_test") { if (ability_runtime_child_process) { deps += [ - "${ability_runtime_native_path}/child_process:child_process", - "${ability_runtime_innerkits_path}/app_manager:app_manager", "${ability_runtime_path}/interfaces/inner_api/child_process_manager:child_process_manager", ] } diff --git a/test/unittest/child_process_manager_test/BUILD.gn b/test/unittest/child_process_manager_test/BUILD.gn index 7509e59770..9e72ee1b7b 100644 --- a/test/unittest/child_process_manager_test/BUILD.gn +++ b/test/unittest/child_process_manager_test/BUILD.gn @@ -28,6 +28,7 @@ ohos_unittest("child_process_manager_test") { include_dirs = [ "include", "${ability_runtime_test_path}/mock/services_appmgr_test/include", + "${ability_runtime_test_path}/mock/common/include", ] sources = [ diff --git a/test/unittest/child_process_manager_test/child_process_manager_test.cpp b/test/unittest/child_process_manager_test/child_process_manager_test.cpp index 9f60c675cf..15d2e0d1e8 100644 --- a/test/unittest/child_process_manager_test/child_process_manager_test.cpp +++ b/test/unittest/child_process_manager_test/child_process_manager_test.cpp @@ -18,14 +18,17 @@ #define private public #include "app_utils.h" #include "child_process_manager.h" +#include "iservice_registry.h" #undef private #include "child_process_manager_error_utils.h" #include "js_runtime.h" #include "mock_app_mgr_service.h" #include "mock_bundle_manager.h" +#include "mock_system_ability_manager.h" #include "parameters.h" #include "sys_mgr_client.h" #include "system_ability_definition.h" +#include "ability_manager_errors.h" #include "hilog_tag_wrapper.h" using namespace testing; @@ -57,6 +60,14 @@ void ChildProcessManagerTest::SetUpTestCase() } sysMgr->RegisterSystemAbility(BUNDLE_MGR_SERVICE_SYS_ABILITY_ID, bundleMgrService); sysMgr->RegisterSystemAbility(APP_MGR_SERVICE_ID, mockAppMgrService); + + sptr mockSamgr = + sptr(new (std::nothrow) AppExecFwk::MockSystemAbilityManager()); + if (mockSamgr != nullptr) { + ON_CALL(*mockSamgr, GetSystemAbility(APP_MGR_SERVICE_ID)) + .WillByDefault(testing::Return(mockAppMgrService)); + SystemAbilityManagerClient::GetInstance().systemAbilityManager_ = mockSamgr; + } } void ChildProcessManagerTest::TearDownTestCase() @@ -65,6 +76,7 @@ void ChildProcessManagerTest::TearDownTestCase() AAFwk::AppUtils::GetInstance().isMultiProcessModel_.value = false; AAFwk::AppUtils::GetInstance().isSupportNativeChildProcess_.isLoaded = false; AAFwk::AppUtils::GetInstance().isSupportNativeChildProcess_.value = false; + SystemAbilityManagerClient::GetInstance().systemAbilityManager_ = nullptr; } void ChildProcessManagerTest::SetUp() @@ -685,59 +697,133 @@ HWTEST_F(ChildProcessManagerTest, LoadFromAppRuntime_0300, TestSize.Level2) /** * @tc.number: IsArkChildProcessSupported_0100 - * @tc.desc: Test IsArkChildProcessSupported when supported is set to true + * @tc.desc: Test IsArkChildProcessSupported when IPC returns true * @tc.type: FUNC */ HWTEST_F(ChildProcessManagerTest, IsArkChildProcessSupported_0100, TestSize.Level1) { TAG_LOGD(AAFwkTag::TEST, "IsArkChildProcessSupported_0100 called."); - auto &cpm = ChildProcessManager::GetInstance(); - cpm.SetArkChildProcessSupported(true); - bool result = cpm.IsArkChildProcessSupported(); + auto sysMgr = DelayedSingleton::GetInstance(); + ASSERT_NE(sysMgr, nullptr); + sptr remoteObj = sysMgr->GetSystemAbility(APP_MGR_SERVICE_ID); + ASSERT_NE(remoteObj, nullptr); + auto mockService = static_cast(remoteObj.GetRefPtr()); + ASSERT_NE(mockService, nullptr); + + EXPECT_CALL(*mockService, IsChildProcessSupported(false, _)) + .WillOnce(DoAll(SetArgReferee<1>(true), Return(ERR_OK))); + + bool result = ChildProcessManager::GetInstance().IsArkChildProcessSupported(); EXPECT_TRUE(result); - cpm.SetArkChildProcessSupported(false); } /** * @tc.number: IsArkChildProcessSupported_0200 - * @tc.desc: Test IsArkChildProcessSupported when supported is set to false + * @tc.desc: Test IsArkChildProcessSupported when IPC returns false * @tc.type: FUNC */ HWTEST_F(ChildProcessManagerTest, IsArkChildProcessSupported_0200, TestSize.Level1) { TAG_LOGD(AAFwkTag::TEST, "IsArkChildProcessSupported_0200 called."); - auto &cpm = ChildProcessManager::GetInstance(); - cpm.SetArkChildProcessSupported(false); - bool result = cpm.IsArkChildProcessSupported(); + auto sysMgr = DelayedSingleton::GetInstance(); + ASSERT_NE(sysMgr, nullptr); + sptr remoteObj = sysMgr->GetSystemAbility(APP_MGR_SERVICE_ID); + ASSERT_NE(remoteObj, nullptr); + auto mockService = static_cast(remoteObj.GetRefPtr()); + ASSERT_NE(mockService, nullptr); + + EXPECT_CALL(*mockService, IsChildProcessSupported(false, _)) + .WillOnce(DoAll(SetArgReferee<1>(false), Return(ERR_OK))); + + bool result = ChildProcessManager::GetInstance().IsArkChildProcessSupported(); + EXPECT_FALSE(result); +} + +/** + * @tc.number: IsArkChildProcessSupported_0300 + * @tc.desc: Test IsArkChildProcessSupported when IPC fails + * @tc.type: FUNC + */ +HWTEST_F(ChildProcessManagerTest, IsArkChildProcessSupported_0300, TestSize.Level1) +{ + TAG_LOGD(AAFwkTag::TEST, "IsArkChildProcessSupported_0300 called."); + auto sysMgr = DelayedSingleton::GetInstance(); + ASSERT_NE(sysMgr, nullptr); + sptr remoteObj = sysMgr->GetSystemAbility(APP_MGR_SERVICE_ID); + ASSERT_NE(remoteObj, nullptr); + auto mockService = static_cast(remoteObj.GetRefPtr()); + ASSERT_NE(mockService, nullptr); + + EXPECT_CALL(*mockService, IsChildProcessSupported(false, _)) + .WillOnce(Return(AAFwk::ERR_NO_APP_RECORD)); + + bool result = ChildProcessManager::GetInstance().IsArkChildProcessSupported(); EXPECT_FALSE(result); } /** * @tc.number: IsNativeChildProcessSupported_0100 - * @tc.desc: Test IsNativeChildProcessSupported when supported is set to true + * @tc.desc: Test IsNativeChildProcessSupported when IPC returns true * @tc.type: FUNC */ HWTEST_F(ChildProcessManagerTest, IsNativeChildProcessSupported_0100, TestSize.Level1) { TAG_LOGD(AAFwkTag::TEST, "IsNativeChildProcessSupported_0100 called."); - auto &cpm = ChildProcessManager::GetInstance(); - cpm.SetNativeChildProcessSupported(true); - bool result = cpm.IsNativeChildProcessSupported(); + auto sysMgr = DelayedSingleton::GetInstance(); + ASSERT_NE(sysMgr, nullptr); + sptr remoteObj = sysMgr->GetSystemAbility(APP_MGR_SERVICE_ID); + ASSERT_NE(remoteObj, nullptr); + auto mockService = static_cast(remoteObj.GetRefPtr()); + ASSERT_NE(mockService, nullptr); + + EXPECT_CALL(*mockService, IsChildProcessSupported(true, _)) + .WillOnce(DoAll(SetArgReferee<1>(true), Return(ERR_OK))); + + bool result = ChildProcessManager::GetInstance().IsNativeChildProcessSupported(); EXPECT_TRUE(result); - cpm.SetNativeChildProcessSupported(false); } /** * @tc.number: IsNativeChildProcessSupported_0200 - * @tc.desc: Test IsNativeChildProcessSupported when supported is set to false + * @tc.desc: Test IsNativeChildProcessSupported when IPC returns false * @tc.type: FUNC */ HWTEST_F(ChildProcessManagerTest, IsNativeChildProcessSupported_0200, TestSize.Level1) { TAG_LOGD(AAFwkTag::TEST, "IsNativeChildProcessSupported_0200 called."); - auto &cpm = ChildProcessManager::GetInstance(); - cpm.SetNativeChildProcessSupported(false); - bool result = cpm.IsNativeChildProcessSupported(); + auto sysMgr = DelayedSingleton::GetInstance(); + ASSERT_NE(sysMgr, nullptr); + sptr remoteObj = sysMgr->GetSystemAbility(APP_MGR_SERVICE_ID); + ASSERT_NE(remoteObj, nullptr); + auto mockService = static_cast(remoteObj.GetRefPtr()); + ASSERT_NE(mockService, nullptr); + + EXPECT_CALL(*mockService, IsChildProcessSupported(true, _)) + .WillOnce(DoAll(SetArgReferee<1>(false), Return(ERR_OK))); + + bool result = ChildProcessManager::GetInstance().IsNativeChildProcessSupported(); + EXPECT_FALSE(result); +} + +/** + * @tc.number: IsNativeChildProcessSupported_0300 + * @tc.desc: Test IsNativeChildProcessSupported when IPC fails + * @tc.type: FUNC + */ +HWTEST_F(ChildProcessManagerTest, IsNativeChildProcessSupported_0300, TestSize.Level1) +{ + TAG_LOGD(AAFwkTag::TEST, "IsNativeChildProcessSupported_0300 called."); + auto sysMgr = DelayedSingleton::GetInstance(); + ASSERT_NE(sysMgr, nullptr); + sptr remoteObj = sysMgr->GetSystemAbility(APP_MGR_SERVICE_ID); + ASSERT_NE(remoteObj, nullptr); + auto mockService = static_cast(remoteObj.GetRefPtr()); + ASSERT_NE(mockService, nullptr); + + EXPECT_CALL(*mockService, IsChildProcessSupported(true, _)) + .WillOnce(Return(AAFwk::ERR_NO_APP_RECORD)); + + bool result = ChildProcessManager::GetInstance().IsNativeChildProcessSupported(); EXPECT_FALSE(result); } } // namespace AbilityRuntime diff --git a/test/unittest/cli_tool_mgr/arg_mapping_test/arg_mapping_test.cpp b/test/unittest/cli_tool_mgr/arg_mapping_test/arg_mapping_test.cpp deleted file mode 100644 index 47b42b8f07..0000000000 --- a/test/unittest/cli_tool_mgr/arg_mapping_test/arg_mapping_test.cpp +++ /dev/null @@ -1,1585 +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 -#include -#include -#include - -#include "arg_mapping.h" - -using namespace testing::ext; - -namespace OHOS { -namespace CliTool { - -class ArgMappingTest : public testing::Test { -public: - static void SetUpTestCase(void); - static void TearDownTestCase(void); - void SetUp(); - void TearDown(); -}; - -void ArgMappingTest::SetUpTestCase(void) {} -void ArgMappingTest::TearDownTestCase(void) {} -void ArgMappingTest::SetUp() {} -void ArgMappingTest::TearDown() {} - -// ==================== ArgMappingType Tests ==================== - -/** - * @tc.name: ArgMappingType_Value_0100 - * @tc.desc: Test ArgMappingType enum values - * @tc.type: FUNC - */ -HWTEST_F(ArgMappingTest, ArgMappingType_Value_0100, TestSize.Level1) -{ - GTEST_LOG_(INFO) << "ArgMappingType_Value_0100 start"; - - EXPECT_EQ(static_cast(ArgMappingType::FLAG), 0); - EXPECT_EQ(static_cast(ArgMappingType::POSITIONAL), 1); - EXPECT_EQ(static_cast(ArgMappingType::FLATTENED), 2); - EXPECT_EQ(static_cast(ArgMappingType::JSONSTRING), 3); - EXPECT_EQ(static_cast(ArgMappingType::MIXED), 4); - - GTEST_LOG_(INFO) << "ArgMappingType_Value_0100 end"; -} - -// ==================== ArgMapping Marshalling Tests ==================== - -/** - * @tc.name: ArgMapping_Marshalling_0100 - * @tc.desc: Test ArgMapping Marshalling with FLAG type - * @tc.type: FUNC - */ -HWTEST_F(ArgMappingTest, ArgMapping_Marshalling_0100, TestSize.Level1) -{ - GTEST_LOG_(INFO) << "ArgMapping_Marshalling_0100 start"; - - ArgMapping mapping; - mapping.type = ArgMappingType::FLAG; - mapping.separator = " "; - mapping.order = ""; - mapping.templates = R"({"key": "value"})"; - - Parcel parcel; - bool ret = mapping.Marshalling(parcel); - - EXPECT_TRUE(ret); - - GTEST_LOG_(INFO) << "ArgMapping_Marshalling_0100 end"; -} - -/** - * @tc.name: ArgMapping_Marshalling_0200 - * @tc.desc: Test ArgMapping Marshalling with POSITIONAL type - * @tc.type: FUNC - */ -HWTEST_F(ArgMappingTest, ArgMapping_Marshalling_0200, TestSize.Level1) -{ - GTEST_LOG_(INFO) << "ArgMapping_Marshalling_0200 start"; - - ArgMapping mapping; - mapping.type = ArgMappingType::POSITIONAL; - mapping.separator = ","; - mapping.order = "arg1,arg2,arg3"; - mapping.templates = "{}"; - - Parcel parcel; - bool ret = mapping.Marshalling(parcel); - - EXPECT_TRUE(ret); - - GTEST_LOG_(INFO) << "ArgMapping_Marshalling_0200 end"; -} - -/** - * @tc.name: ArgMapping_Marshalling_0300 - * @tc.desc: Test ArgMapping Marshalling with FLATTENED type - * @tc.type: FUNC - */ -HWTEST_F(ArgMappingTest, ArgMapping_Marshalling_0300, TestSize.Level1) -{ - GTEST_LOG_(INFO) << "ArgMapping_Marshalling_0300 start"; - - ArgMapping mapping; - mapping.type = ArgMappingType::FLATTENED; - mapping.separator = ";"; - mapping.order = ""; - mapping.templates = R"({"args": "--arg={value}"})"; - - Parcel parcel; - bool ret = mapping.Marshalling(parcel); - - EXPECT_TRUE(ret); - - GTEST_LOG_(INFO) << "ArgMapping_Marshalling_0300 end"; -} - -/** - * @tc.name: ArgMapping_Marshalling_0400 - * @tc.desc: Test ArgMapping Marshalling with JSONSTRING type - * @tc.type: FUNC - */ -HWTEST_F(ArgMappingTest, ArgMapping_Marshalling_0400, TestSize.Level1) -{ - GTEST_LOG_(INFO) << "ArgMapping_Marshalling_0400 start"; - - ArgMapping mapping; - mapping.type = ArgMappingType::JSONSTRING; - mapping.separator = ""; - mapping.order = ""; - mapping.templates = R"({"input": {"type": "object"}})"; - - Parcel parcel; - bool ret = mapping.Marshalling(parcel); - - EXPECT_TRUE(ret); - - GTEST_LOG_(INFO) << "ArgMapping_Marshalling_0400 end"; -} - -/** - * @tc.name: ArgMapping_Marshalling_0500 - * @tc.desc: Test ArgMapping Marshalling with MIXED type - * @tc.type: FUNC - */ -HWTEST_F(ArgMappingTest, ArgMapping_Marshalling_0500, TestSize.Level1) -{ - GTEST_LOG_(INFO) << "ArgMapping_Marshalling_0500 start"; - - ArgMapping mapping; - mapping.type = ArgMappingType::MIXED; - mapping.separator = "|"; - mapping.order = "verbose,output"; - mapping.templates = R"({"verbose": {"if_true": "-v"}})"; - - Parcel parcel; - bool ret = mapping.Marshalling(parcel); - - EXPECT_TRUE(ret); - - GTEST_LOG_(INFO) << "ArgMapping_Marshalling_0500 end"; -} - -/** - * @tc.name: ArgMapping_Marshalling_0600 - * @tc.desc: Test ArgMapping Marshalling with empty strings - * @tc.type: FUNC - */ -HWTEST_F(ArgMappingTest, ArgMapping_Marshalling_0600, TestSize.Level1) -{ - GTEST_LOG_(INFO) << "ArgMapping_Marshalling_0600 start"; - - ArgMapping mapping; - mapping.type = ArgMappingType::FLAG; - mapping.separator = ""; - mapping.order = ""; - mapping.templates = ""; - - Parcel parcel; - bool ret = mapping.Marshalling(parcel); - - EXPECT_TRUE(ret); - - GTEST_LOG_(INFO) << "ArgMapping_Marshalling_0600 end"; -} - -/** - * @tc.name: ArgMapping_Marshalling_0700 - * @tc.desc: Test ArgMapping Marshalling with all types in loop - * @tc.type: FUNC - */ -HWTEST_F(ArgMappingTest, ArgMapping_Marshalling_0700, TestSize.Level1) -{ - GTEST_LOG_(INFO) << "ArgMapping_Marshalling_0700 start"; - - std::vector types = { - ArgMappingType::FLAG, - ArgMappingType::POSITIONAL, - ArgMappingType::FLATTENED, - ArgMappingType::JSONSTRING, - ArgMappingType::MIXED - }; - - for (auto type : types) { - ArgMapping mapping; - mapping.type = type; - mapping.separator = ","; - mapping.order = "a,b,c"; - mapping.templates = "{}"; - - Parcel parcel; - bool ret = mapping.Marshalling(parcel); - EXPECT_TRUE(ret); - } - - GTEST_LOG_(INFO) << "ArgMapping_Marshalling_0700 end"; -} - -// ==================== ArgMapping Unmarshalling Tests ==================== - -/** - * @tc.name: ArgMapping_Unmarshalling_0100 - * @tc.desc: Test ArgMapping Unmarshalling success with FLAG type - * @tc.type: FUNC - */ -HWTEST_F(ArgMappingTest, ArgMapping_Unmarshalling_0100, TestSize.Level1) -{ - GTEST_LOG_(INFO) << "ArgMapping_Unmarshalling_0100 start"; - - ArgMapping original; - original.type = ArgMappingType::FLAG; - original.separator = " "; - original.order = ""; - original.templates = R"({"key": "value"})"; - - Parcel parcel; - ASSERT_TRUE(original.Marshalling(parcel)); - - parcel.RewindRead(0); - ArgMapping *result = ArgMapping::Unmarshalling(parcel); - - ASSERT_NE(result, nullptr); - EXPECT_EQ(result->type, ArgMappingType::FLAG); - EXPECT_EQ(result->separator, " "); - EXPECT_EQ(result->order, ""); - EXPECT_EQ(result->templates, R"({"key": "value"})"); - - delete result; - - GTEST_LOG_(INFO) << "ArgMapping_Unmarshalling_0100 end"; -} - -/** - * @tc.name: ArgMapping_Unmarshalling_0200 - * @tc.desc: Test ArgMapping Unmarshalling success with POSITIONAL type - * @tc.type: FUNC - */ -HWTEST_F(ArgMappingTest, ArgMapping_Unmarshalling_0200, TestSize.Level1) -{ - GTEST_LOG_(INFO) << "ArgMapping_Unmarshalling_0200 start"; - - ArgMapping original; - original.type = ArgMappingType::POSITIONAL; - original.separator = ","; - original.order = "arg1,arg2"; - original.templates = R"({"target": "--target={value}"})"; - - Parcel parcel; - ASSERT_TRUE(original.Marshalling(parcel)); - - parcel.RewindRead(0); - ArgMapping *result = ArgMapping::Unmarshalling(parcel); - - ASSERT_NE(result, nullptr); - EXPECT_EQ(result->type, ArgMappingType::POSITIONAL); - EXPECT_EQ(result->separator, ","); - EXPECT_EQ(result->order, "arg1,arg2"); - EXPECT_EQ(result->templates, R"({"target": "--target={value}"})"); - - delete result; - - GTEST_LOG_(INFO) << "ArgMapping_Unmarshalling_0200 end"; -} - -/** - * @tc.name: ArgMapping_Unmarshalling_0300 - * @tc.desc: Test ArgMapping Unmarshalling fail with empty parcel - * @tc.type: FUNC - */ -HWTEST_F(ArgMappingTest, ArgMapping_Unmarshalling_0300, TestSize.Level1) -{ - GTEST_LOG_(INFO) << "ArgMapping_Unmarshalling_0300 start"; - - Parcel parcel; - ArgMapping *result = ArgMapping::Unmarshalling(parcel); - - EXPECT_EQ(result, nullptr); - - GTEST_LOG_(INFO) << "ArgMapping_Unmarshalling_0300 end"; -} - -/** - * @tc.name: ArgMapping_Unmarshalling_0400 - * @tc.desc: Test ArgMapping Unmarshalling fail with partial data (missing type) - * @tc.type: FUNC - */ -HWTEST_F(ArgMappingTest, ArgMapping_Unmarshalling_0400, TestSize.Level1) -{ - GTEST_LOG_(INFO) << "ArgMapping_Unmarshalling_0400 start"; - - Parcel parcel; - // Only write separator, not type - parcel.WriteString("separator"); - - parcel.RewindRead(0); - ArgMapping *result = ArgMapping::Unmarshalling(parcel); - - EXPECT_EQ(result, nullptr); - - GTEST_LOG_(INFO) << "ArgMapping_Unmarshalling_0400 end"; -} - -/** - * @tc.name: ArgMapping_Unmarshalling_0500 - * @tc.desc: Test ArgMapping Unmarshalling fail with partial data (missing templates) - * @tc.type: FUNC - */ -HWTEST_F(ArgMappingTest, ArgMapping_Unmarshalling_0500, TestSize.Level1) -{ - GTEST_LOG_(INFO) << "ArgMapping_Unmarshalling_0500 start"; - - Parcel parcel; - parcel.WriteInt32(static_cast(ArgMappingType::FLAG)); - parcel.WriteString(" "); - parcel.WriteString(""); - - parcel.RewindRead(0); - ArgMapping *result = ArgMapping::Unmarshalling(parcel); - - EXPECT_EQ(result, nullptr); - - GTEST_LOG_(INFO) << "ArgMapping_Unmarshalling_0500 end"; -} - -// ==================== ArgMapping Round Trip Tests ==================== - -/** - * @tc.name: ArgMapping_RoundTrip_0100 - * @tc.desc: Test ArgMapping Marshalling and Unmarshalling round trip with MIXED type - * @tc.type: FUNC - */ -HWTEST_F(ArgMappingTest, ArgMapping_RoundTrip_0100, TestSize.Level1) -{ - GTEST_LOG_(INFO) << "ArgMapping_RoundTrip_0100 start"; - - ArgMapping original; - original.type = ArgMappingType::MIXED; - original.separator = "|"; - original.order = "x,y,z"; - original.templates = R"({"verbose": {"if_true": "-v", "if_false": ""}})"; - - Parcel parcel; - ASSERT_TRUE(original.Marshalling(parcel)); - - parcel.RewindRead(0); - ArgMapping *restored = ArgMapping::Unmarshalling(parcel); - - ASSERT_NE(restored, nullptr); - EXPECT_EQ(restored->type, original.type); - EXPECT_EQ(restored->separator, original.separator); - EXPECT_EQ(restored->order, original.order); - EXPECT_EQ(restored->templates, original.templates); - - delete restored; - - GTEST_LOG_(INFO) << "ArgMapping_RoundTrip_0100 end"; -} - -/** - * @tc.name: ArgMapping_RoundTrip_0200 - * @tc.desc: Test ArgMapping round trip with FLATTENED type - * @tc.type: FUNC - */ -HWTEST_F(ArgMappingTest, ArgMapping_RoundTrip_0200, TestSize.Level1) -{ - GTEST_LOG_(INFO) << "ArgMapping_RoundTrip_0200 start"; - - ArgMapping original; - original.type = ArgMappingType::FLATTENED; - original.separator = ";"; - original.order = "input,output"; - original.templates = R"({"input": "--input={value}", "output": "--output={value}"})"; - - Parcel parcel; - ASSERT_TRUE(original.Marshalling(parcel)); - - parcel.RewindRead(0); - ArgMapping *restored = ArgMapping::Unmarshalling(parcel); - - ASSERT_NE(restored, nullptr); - EXPECT_EQ(restored->type, original.type); - EXPECT_EQ(restored->separator, original.separator); - EXPECT_EQ(restored->order, original.order); - EXPECT_EQ(restored->templates, original.templates); - - delete restored; - - GTEST_LOG_(INFO) << "ArgMapping_RoundTrip_0200 end"; -} - -/** - * @tc.name: ArgMapping_RoundTrip_0300 - * @tc.desc: Test ArgMapping round trip with JSONSTRING type - * @tc.type: FUNC - */ -HWTEST_F(ArgMappingTest, ArgMapping_RoundTrip_0300, TestSize.Level1) -{ - GTEST_LOG_(INFO) << "ArgMapping_RoundTrip_0300 start"; - - ArgMapping original; - original.type = ArgMappingType::JSONSTRING; - original.separator = ""; - original.order = ""; - original.templates = R"({"complex": {"nested": {"key": "value"}}})"; - - Parcel parcel; - ASSERT_TRUE(original.Marshalling(parcel)); - - parcel.RewindRead(0); - ArgMapping *restored = ArgMapping::Unmarshalling(parcel); - - ASSERT_NE(restored, nullptr); - EXPECT_EQ(restored->type, original.type); - EXPECT_EQ(restored->separator, original.separator); - EXPECT_EQ(restored->order, original.order); - EXPECT_EQ(restored->templates, original.templates); - - delete restored; - - GTEST_LOG_(INFO) << "ArgMapping_RoundTrip_0300 end"; -} - -/** - * @tc.name: ArgMapping_RoundTrip_0400 - * @tc.desc: Test ArgMapping round trip with empty strings - * @tc.type: FUNC - */ -HWTEST_F(ArgMappingTest, ArgMapping_RoundTrip_0400, TestSize.Level1) -{ - GTEST_LOG_(INFO) << "ArgMapping_RoundTrip_0400 start"; - - ArgMapping original; - original.type = ArgMappingType::FLAG; - original.separator = ""; - original.order = ""; - original.templates = ""; - - Parcel parcel; - ASSERT_TRUE(original.Marshalling(parcel)); - - parcel.RewindRead(0); - ArgMapping *restored = ArgMapping::Unmarshalling(parcel); - - ASSERT_NE(restored, nullptr); - EXPECT_EQ(restored->type, original.type); - EXPECT_EQ(restored->separator, original.separator); - EXPECT_EQ(restored->order, original.order); - EXPECT_EQ(restored->templates, original.templates); - - delete restored; - - GTEST_LOG_(INFO) << "ArgMapping_RoundTrip_0400 end"; -} - -/** - * @tc.name: ArgMapping_RoundTrip_0500 - * @tc.desc: Test ArgMapping round trip with all types - * @tc.type: FUNC - */ -HWTEST_F(ArgMappingTest, ArgMapping_RoundTrip_0500, TestSize.Level1) -{ - GTEST_LOG_(INFO) << "ArgMapping_RoundTrip_0500 start"; - - std::vector types = { - ArgMappingType::FLAG, - ArgMappingType::POSITIONAL, - ArgMappingType::FLATTENED, - ArgMappingType::JSONSTRING, - ArgMappingType::MIXED - }; - - for (auto type : types) { - ArgMapping original; - original.type = type; - original.separator = ","; - original.order = "a,b,c"; - original.templates = "{}"; - - Parcel parcel; - ASSERT_TRUE(original.Marshalling(parcel)); - - parcel.RewindRead(0); - ArgMapping *restored = ArgMapping::Unmarshalling(parcel); - - ASSERT_NE(restored, nullptr); - EXPECT_EQ(restored->type, original.type); - EXPECT_EQ(restored->separator, original.separator); - EXPECT_EQ(restored->order, original.order); - EXPECT_EQ(restored->templates, original.templates); - - delete restored; - } - - GTEST_LOG_(INFO) << "ArgMapping_RoundTrip_0500 end"; -} - -// ==================== ArgMapping Default Values Tests ==================== - -/** - * @tc.name: ArgMapping_DefaultValues_0100 - * @tc.desc: Test ArgMapping default values - * @tc.type: FUNC - */ -HWTEST_F(ArgMappingTest, ArgMapping_DefaultValues_0100, TestSize.Level1) -{ - GTEST_LOG_(INFO) << "ArgMapping_DefaultValues_0100 start"; - - ArgMapping mapping; - - EXPECT_EQ(mapping.type, ArgMappingType::FLAG); - EXPECT_EQ(mapping.separator, ""); - EXPECT_EQ(mapping.order, ""); - EXPECT_EQ(mapping.templates, ""); - - GTEST_LOG_(INFO) << "ArgMapping_DefaultValues_0100 end"; -} - -// ==================== ArgMapping ParseFromJson Tests ==================== - -/** - * @tc.name: ArgMapping_ParseFromJson_0100 - * @tc.desc: Test ArgMapping_ParseFromJson with FLAG type - * @tc.type: FUNC - */ -HWTEST_F(ArgMappingTest, ArgMapping_ParseFromJson_0100, TestSize.Level1) -{ - GTEST_LOG_(INFO) << "ArgMapping_ParseFromJson_0100 start"; - - nlohmann::json json = { - {"type", "flag"}, - {"separator", " "}, - {"order", "arg1,arg2"}, - {"templates", {{"key", "value"}}} - }; - - ArgMapping result; - bool ret = ArgMapping::ParseFromJson(json, result); - - ASSERT_TRUE(ret); - EXPECT_EQ(result.type, ArgMappingType::FLAG); - EXPECT_EQ(result.separator, " "); - EXPECT_EQ(result.order, "arg1,arg2"); - - GTEST_LOG_(INFO) << "ArgMapping_ParseFromJson_0100 end"; -} - -/** - * @tc.name: ArgMapping_ParseFromJson_0200 - * @tc.desc: Test ArgMapping_ParseFromJson with POSITIONAL type - * @tc.type: FUNC - */ -HWTEST_F(ArgMappingTest, ArgMapping_ParseFromJson_0200, TestSize.Level1) -{ - GTEST_LOG_(INFO) << "ArgMapping_ParseFromJson_0200 start"; - - nlohmann::json json = { - {"type", "positional"}, - {"separator", ","}, - {"order", "a,b,c"} - }; - - ArgMapping result; - bool ret = ArgMapping::ParseFromJson(json, result); - - ASSERT_TRUE(ret); - EXPECT_EQ(result.type, ArgMappingType::POSITIONAL); - EXPECT_EQ(result.separator, ","); - EXPECT_EQ(result.order, "a,b,c"); - - GTEST_LOG_(INFO) << "ArgMapping_ParseFromJson_0200 end"; -} - -/** - * @tc.name: ArgMapping_ParseFromJson_0300 - * @tc.desc: Test ArgMapping_ParseFromJson with FLATTENED type - * @tc.type: FUNC - */ -HWTEST_F(ArgMappingTest, ArgMapping_ParseFromJson_0300, TestSize.Level1) -{ - GTEST_LOG_(INFO) << "ArgMapping_ParseFromJson_0300 start"; - - nlohmann::json json = { - {"type", "flattened"}, - {"separator", ";"} - }; - - ArgMapping result; - bool ret = ArgMapping::ParseFromJson(json, result); - - ASSERT_TRUE(ret); - EXPECT_EQ(result.type, ArgMappingType::FLATTENED); - EXPECT_EQ(result.separator, ";"); - - GTEST_LOG_(INFO) << "ArgMapping_ParseFromJson_0300 end"; -} - -/** - * @tc.name: ArgMapping_ParseFromJson_0400 - * @tc.desc: Test ArgMapping_ParseFromJson with JSONSTRING type - * @tc.type: FUNC - */ -HWTEST_F(ArgMappingTest, ArgMapping_ParseFromJson_0400, TestSize.Level1) -{ - GTEST_LOG_(INFO) << "ArgMapping_ParseFromJson_0400 start"; - - nlohmann::json json = { - {"type", "jsonString"} - }; - - ArgMapping result; - bool ret = ArgMapping::ParseFromJson(json, result); - - ASSERT_TRUE(ret); - EXPECT_EQ(result.type, ArgMappingType::JSONSTRING); - - GTEST_LOG_(INFO) << "ArgMapping_ParseFromJson_0400 end"; -} - -/** - * @tc.name: ArgMapping_ParseFromJson_0500 - * @tc.desc: Test ArgMapping_ParseFromJson with MIXED type - * @tc.type: FUNC - */ -HWTEST_F(ArgMappingTest, ArgMapping_ParseFromJson_0500, TestSize.Level1) -{ - GTEST_LOG_(INFO) << "ArgMapping_ParseFromJson_0500 start"; - - nlohmann::json json = { - {"type", "mixed"}, - {"separator", "|"}, - {"order", "x,y,z"} - }; - - ArgMapping result; - bool ret = ArgMapping::ParseFromJson(json, result); - - ASSERT_TRUE(ret); - EXPECT_EQ(result.type, ArgMappingType::MIXED); - EXPECT_EQ(result.separator, "|"); - EXPECT_EQ(result.order, "x,y,z"); - - GTEST_LOG_(INFO) << "ArgMapping_ParseFromJson_0500 end"; -} - -/** - * @tc.name: ArgMapping_ParseFromJson_0600 - * @tc.desc: Test ArgMapping_ParseFromJson with empty json (type is required) - * @tc.type: FUNC - */ -HWTEST_F(ArgMappingTest, ArgMapping_ParseFromJson_0600, TestSize.Level1) -{ - GTEST_LOG_(INFO) << "ArgMapping_ParseFromJson_0600 start"; - - nlohmann::json json = {}; - - ArgMapping result; - bool ret = ArgMapping::ParseFromJson(json, result); - - ASSERT_FALSE(ret); // type is required - - GTEST_LOG_(INFO) << "ArgMapping_ParseFromJson_0600 end"; -} - -/** - * @tc.name: ArgMapping_ParseFromJson_0700 - * @tc.desc: Test ArgMapping_ParseFromJson with templates as object - * @tc.type: FUNC - */ -HWTEST_F(ArgMappingTest, ArgMapping_ParseFromJson_0700, TestSize.Level1) -{ - GTEST_LOG_(INFO) << "ArgMapping_ParseFromJson_0700 start"; - - nlohmann::json json = { - {"type", "flag"}, - {"templates", {{"verbose", {{"if_true", "-v"}}}}} - }; - - ArgMapping result; - bool ret = ArgMapping::ParseFromJson(json, result); - - ASSERT_TRUE(ret); - EXPECT_EQ(result.type, ArgMappingType::FLAG); - EXPECT_FALSE(result.templates.empty()); - - GTEST_LOG_(INFO) << "ArgMapping_ParseFromJson_0700 end"; -} - -// ==================== ArgMapping ParseFromJson Validation Tests ==================== - -/** - * @tc.name: ArgMapping_ParseFromJson_0800 - * @tc.desc: Test ArgMapping_ParseFromJson without type field (type is required) - * @tc.type: FUNC - */ -HWTEST_F(ArgMappingTest, ArgMapping_ParseFromJson_0800, TestSize.Level1) -{ - GTEST_LOG_(INFO) << "ArgMapping_ParseFromJson_0800 start"; - - nlohmann::json json = { - {"separator", ","}, - {"order", "a,b"} - }; - - ArgMapping result; - bool ret = ArgMapping::ParseFromJson(json, result); - - ASSERT_FALSE(ret); // type is required - - GTEST_LOG_(INFO) << "ArgMapping_ParseFromJson_0800 end"; -} - -/** - * @tc.name: ArgMapping_ParseFromJson_0900 - * @tc.desc: Test ArgMapping_ParseFromJson with type not string - * @tc.type: FUNC - */ -HWTEST_F(ArgMappingTest, ArgMapping_ParseFromJson_0900, TestSize.Level1) -{ - GTEST_LOG_(INFO) << "ArgMapping_ParseFromJson_0900 start"; - - nlohmann::json json = { - {"type", 123} - }; - - ArgMapping result; - bool ret = ArgMapping::ParseFromJson(json, result); - - ASSERT_FALSE(ret); - - GTEST_LOG_(INFO) << "ArgMapping_ParseFromJson_0900 end"; -} - -/** - * @tc.name: ArgMapping_ParseFromJson_1000 - * @tc.desc: Test ArgMapping_ParseFromJson with invalid type value - * @tc.type: FUNC - */ -HWTEST_F(ArgMappingTest, ArgMapping_ParseFromJson_1000, TestSize.Level1) -{ - GTEST_LOG_(INFO) << "ArgMapping_ParseFromJson_1000 start"; - - nlohmann::json json = { - {"type", "invalid_type"} - }; - - ArgMapping result; - bool ret = ArgMapping::ParseFromJson(json, result); - - ASSERT_FALSE(ret); - - GTEST_LOG_(INFO) << "ArgMapping_ParseFromJson_1000 end"; -} - -/** - * @tc.name: ArgMapping_ParseFromJson_1100 - * @tc.desc: Test ArgMapping_ParseFromJson with templates as string (must be object) - * @tc.type: FUNC - */ -HWTEST_F(ArgMappingTest, ArgMapping_ParseFromJson_1100, TestSize.Level1) -{ - GTEST_LOG_(INFO) << "ArgMapping_ParseFromJson_1100 start"; - - nlohmann::json json = { - {"type", "flag"}, - {"templates", "not a valid json string"} - }; - - ArgMapping result; - bool ret = ArgMapping::ParseFromJson(json, result); - - ASSERT_FALSE(ret); // templates must be object, not string - - GTEST_LOG_(INFO) << "ArgMapping_ParseFromJson_1100 end"; -} - -/** - * @tc.name: ArgMapping_ParseFromJson_1200 - * @tc.desc: Test ArgMapping_ParseFromJson with templates as valid JSON string (must be object) - * @tc.type: FUNC - */ -HWTEST_F(ArgMappingTest, ArgMapping_ParseFromJson_1200, TestSize.Level1) -{ - GTEST_LOG_(INFO) << "ArgMapping_ParseFromJson_1200 start"; - - nlohmann::json json = { - {"type", "flag"}, - {"templates", R"({"verbose": "-v"})"} - }; - - ArgMapping result; - bool ret = ArgMapping::ParseFromJson(json, result); - - ASSERT_FALSE(ret); // templates must be object, not string - - GTEST_LOG_(INFO) << "ArgMapping_ParseFromJson_1200 end"; -} - -/** - * @tc.name: ArgMapping_ParseFromJson_1300 - * @tc.desc: Test ArgMapping_ParseFromJson with templates as empty string (must be object) - * @tc.type: FUNC - */ -HWTEST_F(ArgMappingTest, ArgMapping_ParseFromJson_1300, TestSize.Level1) -{ - GTEST_LOG_(INFO) << "ArgMapping_ParseFromJson_1300 start"; - - nlohmann::json json = { - {"type", "flag"}, - {"templates", ""} - }; - - ArgMapping result; - bool ret = ArgMapping::ParseFromJson(json, result); - - ASSERT_FALSE(ret); // templates must be object, not string - - GTEST_LOG_(INFO) << "ArgMapping_ParseFromJson_1300 end"; -} - -/** - * @tc.name: ArgMapping_ParseFromJson_1400 - * @tc.desc: Test ArgMapping_ParseFromJson with templates as number (must be object) - * @tc.type: FUNC - */ -HWTEST_F(ArgMappingTest, ArgMapping_ParseFromJson_1400, TestSize.Level1) -{ - GTEST_LOG_(INFO) << "ArgMapping_ParseFromJson_1400 start"; - - nlohmann::json json = { - {"type", "flag"}, - {"templates", 123} - }; - - ArgMapping result; - bool ret = ArgMapping::ParseFromJson(json, result); - - ASSERT_FALSE(ret); // templates must be object, not number - - GTEST_LOG_(INFO) << "ArgMapping_ParseFromJson_1400 end"; -} - -/** - * @tc.name: ArgMapping_ParseFromJson_1500 - * @tc.desc: Test ArgMapping_ParseFromJson with templates as array (must be object) - * @tc.type: FUNC - */ -HWTEST_F(ArgMappingTest, ArgMapping_ParseFromJson_1500, TestSize.Level1) -{ - GTEST_LOG_(INFO) << "ArgMapping_ParseFromJson_1500 start"; - - nlohmann::json json = { - {"type", "flag"}, - {"templates", {"a", "b", "c"}} - }; - - ArgMapping result; - bool ret = ArgMapping::ParseFromJson(json, result); - - ASSERT_FALSE(ret); // templates must be object, not array - - GTEST_LOG_(INFO) << "ArgMapping_ParseFromJson_1500 end"; -} - -/** - * @tc.name: ArgMapping_ParseFromJson_1600 - * @tc.desc: Test ArgMapping_ParseFromJson with separator as number (must be string) - * @tc.type: FUNC - */ -HWTEST_F(ArgMappingTest, ArgMapping_ParseFromJson_1600, TestSize.Level1) -{ - GTEST_LOG_(INFO) << "ArgMapping_ParseFromJson_1600 start"; - - nlohmann::json json = { - {"type", "flag"}, - {"separator", 123} - }; - - ArgMapping result; - bool ret = ArgMapping::ParseFromJson(json, result); - - ASSERT_FALSE(ret); // separator must be string, not number - - GTEST_LOG_(INFO) << "ArgMapping_ParseFromJson_1600 end"; -} - -/** - * @tc.name: ArgMapping_ParseFromJson_1700 - * @tc.desc: Test ArgMapping_ParseFromJson with separator as object (must be string) - * @tc.type: FUNC - */ -HWTEST_F(ArgMappingTest, ArgMapping_ParseFromJson_1700, TestSize.Level1) -{ - GTEST_LOG_(INFO) << "ArgMapping_ParseFromJson_1700 start"; - - nlohmann::json json = { - {"type", "flag"}, - {"separator", {{"key", "value"}}} - }; - - ArgMapping result; - bool ret = ArgMapping::ParseFromJson(json, result); - - ASSERT_FALSE(ret); // separator must be string, not object - - GTEST_LOG_(INFO) << "ArgMapping_ParseFromJson_1700 end"; -} - -/** - * @tc.name: ArgMapping_ParseFromJson_1800 - * @tc.desc: Test ArgMapping_ParseFromJson with separator as array (must be string) - * @tc.type: FUNC - */ -HWTEST_F(ArgMappingTest, ArgMapping_ParseFromJson_1800, TestSize.Level1) -{ - GTEST_LOG_(INFO) << "ArgMapping_ParseFromJson_1800 start"; - - nlohmann::json json = { - {"type", "flag"}, - {"separator", {"a", "b"}} - }; - - ArgMapping result; - bool ret = ArgMapping::ParseFromJson(json, result); - - ASSERT_FALSE(ret); // separator must be string, not array - - GTEST_LOG_(INFO) << "ArgMapping_ParseFromJson_1800 end"; -} - -/** - * @tc.name: ArgMapping_ParseFromJson_1900 - * @tc.desc: Test ArgMapping_ParseFromJson with separator as boolean (must be string) - * @tc.type: FUNC - */ -HWTEST_F(ArgMappingTest, ArgMapping_ParseFromJson_1900, TestSize.Level1) -{ - GTEST_LOG_(INFO) << "ArgMapping_ParseFromJson_1900 start"; - - nlohmann::json json = { - {"type", "flag"}, - {"separator", true} - }; - - ArgMapping result; - bool ret = ArgMapping::ParseFromJson(json, result); - - ASSERT_FALSE(ret); // separator must be string, not boolean - - GTEST_LOG_(INFO) << "ArgMapping_ParseFromJson_1900 end"; -} - -/** - * @tc.name: ArgMapping_ParseFromJson_2000 - * @tc.desc: Test ArgMapping_ParseFromJson with order as number (must be string) - * @tc.type: FUNC - */ -HWTEST_F(ArgMappingTest, ArgMapping_ParseFromJson_2000, TestSize.Level1) -{ - GTEST_LOG_(INFO) << "ArgMapping_ParseFromJson_2000 start"; - - nlohmann::json json = { - {"type", "positional"}, - {"order", 123} - }; - - ArgMapping result; - bool ret = ArgMapping::ParseFromJson(json, result); - - ASSERT_FALSE(ret); // order must be string, not number - - GTEST_LOG_(INFO) << "ArgMapping_ParseFromJson_2000 end"; -} - -/** - * @tc.name: ArgMapping_ParseFromJson_2100 - * @tc.desc: Test ArgMapping_ParseFromJson with order as object (must be string) - * @tc.type: FUNC - */ -HWTEST_F(ArgMappingTest, ArgMapping_ParseFromJson_2100, TestSize.Level1) -{ - GTEST_LOG_(INFO) << "ArgMapping_ParseFromJson_2100 start"; - - nlohmann::json json = { - {"type", "positional"}, - {"order", {{"key", "value"}}} - }; - - ArgMapping result; - bool ret = ArgMapping::ParseFromJson(json, result); - - ASSERT_FALSE(ret); // order must be string, not object - - GTEST_LOG_(INFO) << "ArgMapping_ParseFromJson_2100 end"; -} - -/** - * @tc.name: ArgMapping_ParseFromJson_2200 - * @tc.desc: Test ArgMapping_ParseFromJson with order as array (must be string) - * @tc.type: FUNC - */ -HWTEST_F(ArgMappingTest, ArgMapping_ParseFromJson_2200, TestSize.Level1) -{ - GTEST_LOG_(INFO) << "ArgMapping_ParseFromJson_2200 start"; - - nlohmann::json json = { - {"type", "positional"}, - {"order", {"a", "b"}} - }; - - ArgMapping result; - bool ret = ArgMapping::ParseFromJson(json, result); - - ASSERT_FALSE(ret); // order must be string, not array - - GTEST_LOG_(INFO) << "ArgMapping_ParseFromJson_2200 end"; -} - -/** - * @tc.name: ArgMapping_ParseFromJson_2300 - * @tc.desc: Test ArgMapping_ParseFromJson with order as boolean (must be string) - * @tc.type: FUNC - */ -HWTEST_F(ArgMappingTest, ArgMapping_ParseFromJson_2300, TestSize.Level1) -{ - GTEST_LOG_(INFO) << "ArgMapping_ParseFromJson_2300 start"; - - nlohmann::json json = { - {"type", "positional"}, - {"order", false} - }; - - ArgMapping result; - bool ret = ArgMapping::ParseFromJson(json, result); - - ASSERT_FALSE(ret); // order must be string, not boolean - - GTEST_LOG_(INFO) << "ArgMapping_ParseFromJson_2300 end"; -} - -// ==================== ArgMapping ParseToJson Tests ==================== - -/** - * @tc.name: ArgMapping_ParseToJson_0100 - * @tc.desc: Test ArgMapping::ParseToJson with FLAG type - * @tc.type: FUNC - */ -HWTEST_F(ArgMappingTest, ArgMapping_ParseToJson_0100, TestSize.Level1) -{ - GTEST_LOG_(INFO) << "ArgMapping_ParseToJson_0100 start"; - - ArgMapping mapping; - mapping.type = ArgMappingType::FLAG; - mapping.separator = " "; - mapping.order = "arg1"; - mapping.templates = R"({"key": "value"})"; - - nlohmann::json json = mapping.ParseToJson(); - - EXPECT_TRUE(json.contains("type")); - EXPECT_EQ(json["type"], "flag"); - EXPECT_TRUE(json.contains("separator")); - EXPECT_EQ(json["separator"], " "); - EXPECT_TRUE(json.contains("order")); - EXPECT_EQ(json["order"], "arg1"); - - GTEST_LOG_(INFO) << "ArgMapping_ParseToJson_0100 end"; -} - -/** - * @tc.name: ArgMapping_ParseToJson_0200 - * @tc.desc: Test ArgMapping_ParseToJson with POSITIONAL type - * @tc.type: FUNC - */ -HWTEST_F(ArgMappingTest, ArgMapping_ParseToJson_0200, TestSize.Level1) -{ - GTEST_LOG_(INFO) << "ArgMapping_ParseToJson_0200 start"; - - ArgMapping mapping; - mapping.type = ArgMappingType::POSITIONAL; - mapping.separator = ","; - mapping.order = "a,b,c"; - - nlohmann::json json = mapping.ParseToJson(); - - EXPECT_EQ(json["type"], "positional"); - EXPECT_EQ(json["separator"], ","); - EXPECT_EQ(json["order"], "a,b,c"); - - GTEST_LOG_(INFO) << "ArgMapping_ParseToJson_0200 end"; -} - -/** - * @tc.name: ArgMapping_ParseToJson_0300 - * @tc.desc: Test ArgMapping_ParseToJson with FLATTENED type - * @tc.type: FUNC - */ -HWTEST_F(ArgMappingTest, ArgMapping_ParseToJson_0300, TestSize.Level1) -{ - GTEST_LOG_(INFO) << "ArgMapping_ParseToJson_0300 start"; - - ArgMapping mapping; - mapping.type = ArgMappingType::FLATTENED; - mapping.separator = ";"; - - nlohmann::json json = mapping.ParseToJson(); - - EXPECT_EQ(json["type"], "flattened"); - EXPECT_EQ(json["separator"], ";"); - - GTEST_LOG_(INFO) << "ArgMapping_ParseToJson_0300 end"; -} - -/** - * @tc.name: ArgMapping_ParseToJson_0400 - * @tc.desc: Test ArgMapping_ParseToJson with JSONSTRING type - * @tc.type: FUNC - */ -HWTEST_F(ArgMappingTest, ArgMapping_ParseToJson_0400, TestSize.Level1) -{ - GTEST_LOG_(INFO) << "ArgMapping_ParseToJson_0400 start"; - - ArgMapping mapping; - mapping.type = ArgMappingType::JSONSTRING; - - nlohmann::json json = mapping.ParseToJson(); - - EXPECT_EQ(json["type"], "jsonString"); - - GTEST_LOG_(INFO) << "ArgMapping_ParseToJson_0400 end"; -} - -/** - * @tc.name: ArgMapping_ParseToJson_0500 - * @tc.desc: Test ArgMapping_ParseToJson with MIXED type - * @tc.type: FUNC - */ -HWTEST_F(ArgMappingTest, ArgMapping_ParseToJson_0500, TestSize.Level1) -{ - GTEST_LOG_(INFO) << "ArgMapping_ParseToJson_0500 start"; - - ArgMapping mapping; - mapping.type = ArgMappingType::MIXED; - mapping.separator = "|"; - mapping.order = "x,y,z"; - - nlohmann::json json = mapping.ParseToJson(); - - EXPECT_EQ(json["type"], "mixed"); - EXPECT_EQ(json["separator"], "|"); - EXPECT_EQ(json["order"], "x,y,z"); - - GTEST_LOG_(INFO) << "ArgMapping_ParseToJson_0500 end"; -} - -/** - * @tc.name: ArgMapping_ParseToJson_0600 - * @tc.desc: Test ArgMapping_ParseToJson with empty fields - * @tc.type: FUNC - */ -HWTEST_F(ArgMappingTest, ArgMapping_ParseToJson_0600, TestSize.Level1) -{ - GTEST_LOG_(INFO) << "ArgMapping_ParseToJson_0600 start"; - - ArgMapping mapping; - mapping.type = ArgMappingType::FLAG; - mapping.separator = ""; - mapping.order = ""; - mapping.templates = ""; - - nlohmann::json json = mapping.ParseToJson(); - - EXPECT_EQ(json["type"], "flag"); - EXPECT_FALSE(json.contains("separator")); // empty string not included - EXPECT_FALSE(json.contains("order")); // empty string not included - EXPECT_FALSE(json.contains("templates")); // empty string not included - - GTEST_LOG_(INFO) << "ArgMapping_ParseToJson_0600 end"; -} - -/** - * @tc.name: ArgMapping_ParseToJson_0700 - * @tc.desc: Test ArgMapping_ParseToJson with templates as JSON object - * @tc.type: FUNC - */ -HWTEST_F(ArgMappingTest, ArgMapping_ParseToJson_0700, TestSize.Level1) -{ - GTEST_LOG_(INFO) << "ArgMapping_ParseToJson_0700 start"; - - ArgMapping mapping; - mapping.type = ArgMappingType::FLAG; - mapping.templates = R"({"verbose": {"if_true": "-v"}})"; - - nlohmann::json json = mapping.ParseToJson(); - - EXPECT_EQ(json["type"], "flag"); - EXPECT_TRUE(json.contains("templates")); - EXPECT_TRUE(json["templates"].is_object()); - - GTEST_LOG_(INFO) << "ArgMapping_ParseToJson_0700 end"; -} - -// ==================== ArgMapping_ParseFromJson and ArgMapping_ParseToJson Round Trip Tests ==================== - -/** - * @tc.name: ArgMapping_JsonRoundTrip_0100 - * @tc.desc: Test ArgMapping_ParseFromJson and ArgMapping_ParseToJson round trip - * @tc.type: FUNC - */ -HWTEST_F(ArgMappingTest, ArgMapping_JsonRoundTrip_0100, TestSize.Level1) -{ - GTEST_LOG_(INFO) << "ArgMapping_JsonRoundTrip_0100 start"; - - ArgMapping original; - original.type = ArgMappingType::MIXED; - original.separator = "|"; - original.order = "x,y,z"; - original.templates = R"({"verbose": {"if_true": "-v"}})"; - - nlohmann::json json = original.ParseToJson(); - ArgMapping restored; - bool ret = ArgMapping::ParseFromJson(json, restored); - - ASSERT_TRUE(ret); - EXPECT_EQ(restored.type, original.type); - EXPECT_EQ(restored.separator, original.separator); - EXPECT_EQ(restored.order, original.order); - - GTEST_LOG_(INFO) << "ArgMapping_JsonRoundTrip_0100 end"; -} - -/** - * @tc.name: ArgMapping_JsonRoundTrip_0200 - * @tc.desc: Test ArgMapping_ParseFromJson and ArgMapping_ParseToJson round trip with all types - * @tc.type: FUNC - */ -HWTEST_F(ArgMappingTest, ArgMapping_JsonRoundTrip_0200, TestSize.Level1) -{ - GTEST_LOG_(INFO) << "ArgMapping_JsonRoundTrip_0200 start"; - - std::vector types = { - ArgMappingType::FLAG, - ArgMappingType::POSITIONAL, - ArgMappingType::FLATTENED, - ArgMappingType::JSONSTRING, - ArgMappingType::MIXED - }; - - for (auto type : types) { - ArgMapping original; - original.type = type; - original.separator = ","; - original.order = "a,b,c"; - original.templates = R"({"key": "value"})"; - - nlohmann::json json = original.ParseToJson(); - ArgMapping restored; - bool ret = ArgMapping::ParseFromJson(json, restored); - - ASSERT_TRUE(ret); - EXPECT_EQ(restored.type, original.type); - EXPECT_EQ(restored.separator, original.separator); - EXPECT_EQ(restored.order, original.order); - } - - GTEST_LOG_(INFO) << "ArgMapping_JsonRoundTrip_0200 end"; -} - -// ==================== ArgMapping Validate Tests ==================== - -/** - * @tc.name: ArgMapping_Validate_0100 - * @tc.desc: Test ArgMapping::Validate with valid FLAG type - * @tc.type: FUNC - */ -HWTEST_F(ArgMappingTest, ArgMapping_Validate_0100, TestSize.Level1) -{ - GTEST_LOG_(INFO) << "ArgMapping_Validate_0100 start"; - - ArgMapping mapping; - mapping.type = ArgMappingType::FLAG; - mapping.separator = " "; - mapping.order = "arg1"; - - EXPECT_TRUE(ArgMapping::Validate(mapping)); - - GTEST_LOG_(INFO) << "ArgMapping_Validate_0100 end"; -} - -/** - * @tc.name: ArgMapping_Validate_0200 - * @tc.desc: Test ArgMapping::Validate with valid POSITIONAL type - * @tc.type: FUNC - */ -HWTEST_F(ArgMappingTest, ArgMapping_Validate_0200, TestSize.Level1) -{ - GTEST_LOG_(INFO) << "ArgMapping_Validate_0200 start"; - - ArgMapping mapping; - mapping.type = ArgMappingType::POSITIONAL; - mapping.separator = ","; - mapping.order = "a,b,c"; - - EXPECT_TRUE(ArgMapping::Validate(mapping)); - - GTEST_LOG_(INFO) << "ArgMapping_Validate_0200 end"; -} - -/** - * @tc.name: ArgMapping_Validate_0300 - * @tc.desc: Test ArgMapping::Validate with valid FLATTENED type - * @tc.type: FUNC - */ -HWTEST_F(ArgMappingTest, ArgMapping_Validate_0300, TestSize.Level1) -{ - GTEST_LOG_(INFO) << "ArgMapping_Validate_0300 start"; - - ArgMapping mapping; - mapping.type = ArgMappingType::FLATTENED; - mapping.separator = ";"; - - EXPECT_TRUE(ArgMapping::Validate(mapping)); - - GTEST_LOG_(INFO) << "ArgMapping_Validate_0300 end"; -} - -/** - * @tc.name: ArgMapping_Validate_0400 - * @tc.desc: Test ArgMapping::Validate with valid JSONSTRING type - * @tc.type: FUNC - */ -HWTEST_F(ArgMappingTest, ArgMapping_Validate_0400, TestSize.Level1) -{ - GTEST_LOG_(INFO) << "ArgMapping_Validate_0400 start"; - - ArgMapping mapping; - mapping.type = ArgMappingType::JSONSTRING; - - EXPECT_TRUE(ArgMapping::Validate(mapping)); - - GTEST_LOG_(INFO) << "ArgMapping_Validate_0400 end"; -} - -/** - * @tc.name: ArgMapping_Validate_0500 - * @tc.desc: Test ArgMapping::Validate with valid MIXED type - * @tc.type: FUNC - */ -HWTEST_F(ArgMappingTest, ArgMapping_Validate_0500, TestSize.Level1) -{ - GTEST_LOG_(INFO) << "ArgMapping_Validate_0500 start"; - - ArgMapping mapping; - mapping.type = ArgMappingType::MIXED; - mapping.separator = "|"; - mapping.order = "x,y,z"; - - EXPECT_TRUE(ArgMapping::Validate(mapping)); - - GTEST_LOG_(INFO) << "ArgMapping_Validate_0500 end"; -} - -/** - * @tc.name: ArgMapping_Validate_0600 - * @tc.desc: Test ArgMapping::Validate with invalid type (out of range) - * @tc.type: FUNC - */ -HWTEST_F(ArgMappingTest, ArgMapping_Validate_0600, TestSize.Level1) -{ - GTEST_LOG_(INFO) << "ArgMapping_Validate_0600 start"; - - ArgMapping mapping; - mapping.type = static_cast(-1); - - EXPECT_FALSE(ArgMapping::Validate(mapping)); - - GTEST_LOG_(INFO) << "ArgMapping_Validate_0600 end"; -} - -/** - * @tc.name: ArgMapping_Validate_0700 - * @tc.desc: Test ArgMapping::Validate with invalid type (greater than MIXED) - * @tc.type: FUNC - */ -HWTEST_F(ArgMappingTest, ArgMapping_Validate_0700, TestSize.Level1) -{ - GTEST_LOG_(INFO) << "ArgMapping_Validate_0700 start"; - - ArgMapping mapping; - mapping.type = static_cast(static_cast(ArgMappingType::MIXED) + 1); - - EXPECT_FALSE(ArgMapping::Validate(mapping)); - - GTEST_LOG_(INFO) << "ArgMapping_Validate_0700 end"; -} - -/** - * @tc.name: ArgMapping_Validate_0800 - * @tc.desc: Test ArgMapping::Validate with valid templates JSON object - * @tc.type: FUNC - */ -HWTEST_F(ArgMappingTest, ArgMapping_Validate_0800, TestSize.Level1) -{ - GTEST_LOG_(INFO) << "ArgMapping_Validate_0800 start"; - - ArgMapping mapping; - mapping.type = ArgMappingType::FLAG; - mapping.templates = R"({"verbose": {"if_true": "-v"}})"; - - EXPECT_TRUE(ArgMapping::Validate(mapping)); - - GTEST_LOG_(INFO) << "ArgMapping_Validate_0800 end"; -} - -/** - * @tc.name: ArgMapping_Validate_0900 - * @tc.desc: Test ArgMapping::Validate with empty templates (valid) - * @tc.type: FUNC - */ -HWTEST_F(ArgMappingTest, ArgMapping_Validate_0900, TestSize.Level1) -{ - GTEST_LOG_(INFO) << "ArgMapping_Validate_0900 start"; - - ArgMapping mapping; - mapping.type = ArgMappingType::FLAG; - mapping.templates = ""; - - EXPECT_TRUE(ArgMapping::Validate(mapping)); - - GTEST_LOG_(INFO) << "ArgMapping_Validate_0900 end"; -} - -/** - * @tc.name: ArgMapping_Validate_1000 - * @tc.desc: Test ArgMapping::Validate with invalid templates (not JSON) - * @tc.type: FUNC - */ -HWTEST_F(ArgMappingTest, ArgMapping_Validate_1000, TestSize.Level1) -{ - GTEST_LOG_(INFO) << "ArgMapping_Validate_1000 start"; - - ArgMapping mapping; - mapping.type = ArgMappingType::FLAG; - mapping.templates = "not a valid json"; - - EXPECT_FALSE(ArgMapping::Validate(mapping)); - - GTEST_LOG_(INFO) << "ArgMapping_Validate_1000 end"; -} - -/** - * @tc.name: ArgMapping_Validate_1100 - * @tc.desc: Test ArgMapping::Validate with templates as JSON array (invalid) - * @tc.type: FUNC - */ -HWTEST_F(ArgMappingTest, ArgMapping_Validate_1100, TestSize.Level1) -{ - GTEST_LOG_(INFO) << "ArgMapping_Validate_1100 start"; - - ArgMapping mapping; - mapping.type = ArgMappingType::FLAG; - mapping.templates = R"(["a", "b", "c"])"; - - EXPECT_FALSE(ArgMapping::Validate(mapping)); - - GTEST_LOG_(INFO) << "ArgMapping_Validate_1100 end"; -} - -/** - * @tc.name: ArgMapping_Validate_1200 - * @tc.desc: Test ArgMapping::Validate with templates as JSON string (invalid) - * @tc.type: FUNC - */ -HWTEST_F(ArgMappingTest, ArgMapping_Validate_1200, TestSize.Level1) -{ - GTEST_LOG_(INFO) << "ArgMapping_Validate_1200 start"; - - ArgMapping mapping; - mapping.type = ArgMappingType::FLAG; - mapping.templates = R"("just a string")"; - - EXPECT_FALSE(ArgMapping::Validate(mapping)); - - GTEST_LOG_(INFO) << "ArgMapping_Validate_1200 end"; -} - -/** - * @tc.name: ArgMapping_Validate_1300 - * @tc.desc: Test ArgMapping::Validate with templates as JSON number (invalid) - * @tc.type: FUNC - */ -HWTEST_F(ArgMappingTest, ArgMapping_Validate_1300, TestSize.Level1) -{ - GTEST_LOG_(INFO) << "ArgMapping_Validate_1300 start"; - - ArgMapping mapping; - mapping.type = ArgMappingType::FLAG; - mapping.templates = "123"; - - EXPECT_FALSE(ArgMapping::Validate(mapping)); - - GTEST_LOG_(INFO) << "ArgMapping_Validate_1300 end"; -} - -/** - * @tc.name: ArgMapping_Validate_1400 - * @tc.desc: Test ArgMapping::Validate with empty JSON object templates (valid) - * @tc.type: FUNC - */ -HWTEST_F(ArgMappingTest, ArgMapping_Validate_1400, TestSize.Level1) -{ - GTEST_LOG_(INFO) << "ArgMapping_Validate_1400 start"; - - ArgMapping mapping; - mapping.type = ArgMappingType::FLAG; - mapping.templates = "{}"; - - EXPECT_TRUE(ArgMapping::Validate(mapping)); - - GTEST_LOG_(INFO) << "ArgMapping_Validate_1400 end"; -} - -/** - * @tc.name: ArgMapping_Validate_1500 - * @tc.desc: Test ArgMapping::Validate with all valid types - * @tc.type: FUNC - */ -HWTEST_F(ArgMappingTest, ArgMapping_Validate_1500, TestSize.Level1) -{ - GTEST_LOG_(INFO) << "ArgMapping_Validate_1500 start"; - - std::vector types = { - ArgMappingType::FLAG, - ArgMappingType::POSITIONAL, - ArgMappingType::FLATTENED, - ArgMappingType::JSONSTRING, - ArgMappingType::MIXED - }; - - for (auto type : types) { - ArgMapping mapping; - mapping.type = type; - mapping.templates = R"({"key": "value"})"; - - EXPECT_TRUE(ArgMapping::Validate(mapping)); - } - - GTEST_LOG_(INFO) << "ArgMapping_Validate_1500 end"; -} - -} // namespace CliTool -} // namespace OHOS \ No newline at end of file diff --git a/test/unittest/cli_tool_mgr/cli_tool_data_manager_test/cli_tool_data_manager_test.cpp b/test/unittest/cli_tool_mgr/cli_tool_data_manager_test/cli_tool_data_manager_test.cpp deleted file mode 100644 index 4f6411b4dd..0000000000 --- a/test/unittest/cli_tool_mgr/cli_tool_data_manager_test/cli_tool_data_manager_test.cpp +++ /dev/null @@ -1,456 +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 -#include -#include -#include -#include - -#include "cli_tool_data_manager.h" -#include "hilog_tag_wrapper.h" - -namespace OHOS { -namespace CliTool { - -class CliToolDataManagerTest : public testing::Test { -public: - static void SetUpTestCase(); - static void TearDownTestCase(); - void SetUp(); - void TearDown(); - - static constexpr const char* TEST_CONFIG_DIR = "/data/test_cli_tool_configs"; - static constexpr const char* TEST_TOOL1_FILE = "/data/test_cli_tool_configs/tool1.json"; - static constexpr const char* TEST_TOOL2_FILE = "/data/test_cli_tool_configs/tool2.json"; - static constexpr const char* TEST_TOOL3_FILE = "/data/test_cli_tool_configs/tool3.json"; -}; - -void CliToolDataManagerTest::SetUpTestCase() -{ - TAG_LOGI(AAFwkTag::ABILITYMGR, "CliToolDataManagerTest::SetUpTestCase"); - - // Create test config directory and files - std::system("mkdir -p " + std::string(TEST_CONFIG_DIR)); - - // Create tool1.json - std::ofstream file1(TEST_TOOL1_FILE); - file1 << R"({ - "name": "ohos-test_tool1", - "version": "1.0.0", - "description": "Test tool 1", - "executablePath": "/bin/test1", - "requirePermissions": ["ohos.permission.INTERNET"], - "inputSchema": {}, - "outputSchema": {}, - "argMapping": {"type": "flag", "separator": " "}, - "eventSchemas": {"stdout": {"type": "string"}}, - "timeout": 30000, - "eventTypes": ["stdout", "stderr"], - "hasSubCommand": false, - "subcommands": {} - })"; - file1.close(); - - // Create tool2.json - std::ofstream file2(TEST_TOOL2_FILE); - file2 << R"({ - "name": "hms-test_tool2", - "version": "2.0.0", - "description": "Test tool 2", - "executablePath": "/bin/test2", - "requirePermissions": ["ohos.permission.READ_STORAGE"], - "inputSchema": {}, - "outputSchema": {}, - "argMapping": {"type": "positional", "order": "arg1,arg2"}, - "eventSchemas": {"stdout": {"type": "string"}}, - "timeout": 60000, - "eventTypes": ["exit"], - "hasSubCommand": true, - "subcommands": { - "subcmd1": { - "description": "Subcommand 1", - "inputSchema": {}, - "outputSchema": {}, - "argMapping": {"type": "flag"} - } - } - })"; - file2.close(); -} - -void CliToolDataManagerTest::TearDownTestCase() -{ - TAG_LOGI(AAFwkTag::ABILITYMGR, "CliToolDataManagerTest::TearDownTestCase"); - // Clean up test files - std::remove(TEST_TOOL1_FILE); - std::remove(TEST_TOOL2_FILE); - std::rmdir(TEST_CONFIG_DIR); -} - -void CliToolDataManagerTest::SetUp() -{ - TAG_LOGI(AAFwkTag::ABILITYMGR, "CliToolDataManagerTest::SetUp"); -} - -void CliToolDataManagerTest::TearDown() -{ - TAG_LOGI(AAFwkTag::ABILITYMGR, "CliToolDataManagerTest::TearDown"); -} - -/** - * @tc.name: CliToolDataManager_JsonArrayToTools_001 - * @tc.desc: Test parsing JSON array to tools vector - * @tc.type: FUNC - */ -HWTEST_F(CliToolDataManagerTest, CliToolDataManager_JsonArrayToTools_001, testing::ext::TestSize.Level1) -{ - TAG_LOGI(AAFwkTag::ABILITYMGR, "CliToolDataManager_JsonArrayToTools_001 start"); - - auto& dataManager = CliToolDataManager::GetInstance(); - std::string jsonStr = R"([ - {"name": "array_tool1", "version": "1.0", "description": "Array tool 1", "executablePath": "/bin/at1"}, - {"name": "array_tool2", "version": "2.0", "description": "Array tool 2", "executablePath": "/bin/at2"} - ])"; - - std::vector tools; - int32_t ret = dataManager.JsonArrayToTools(jsonStr, tools); - - EXPECT_EQ(ret, 0); - EXPECT_EQ(tools.size(), 2u); - EXPECT_EQ(tools[0].name, "array_tool1"); - EXPECT_EQ(tools[1].name, "array_tool2"); - - TAG_LOGI(AAFwkTag::ABILITYMGR, "CliToolDataManager_JsonArrayToTools_001 end"); -} - -// ==================== ToolInfo ParseToJson Tests ==================== - -/** - * @tc.name: ToolInfo_ParseToJson_001 - * @tc.desc: Test converting ToolInfo to JSON - * @tc.type: FUNC - */ -HWTEST_F(CliToolDataManagerTest, ToolInfo_ParseToJson_001, testing::ext::TestSize.Level1) -{ - TAG_LOGI(AAFwkTag::ABILITYMGR, "ToolInfo_ParseToJson_001 start"); - - ToolInfo tool; - tool.name = "test_tool"; - tool.version = "1.0.0"; - tool.description = "Test description"; - tool.executablePath = "/bin/test"; - tool.requirePermissions = {"ohos.permission.INTERNET"}; - tool.argMapping = std::make_shared(); - tool.argMapping->type = ArgMappingType::FLAG; - tool.argMapping->separator = " "; - tool.timeout = 30000; - tool.hasSubCommand = false; - - nlohmann::json json = tool.ParseToJson(); - std::string jsonStr = json.dump(); - - EXPECT_FALSE(jsonStr.empty()); - EXPECT_NE(jsonStr.find("test_tool"), std::string::npos); - EXPECT_NE(jsonStr.find("1.0.0"), std::string::npos); - - TAG_LOGI(AAFwkTag::ABILITYMGR, "ToolInfo_ParseToJson_001 end"); -} - -/** - * @tc.name: ToolInfo_ParseToJson_002 - * @tc.desc: Test converting ToolInfo with subcommands to JSON - * @tc.type: FUNC - */ -HWTEST_F(CliToolDataManagerTest, ToolInfo_ParseToJson_002, testing::ext::TestSize.Level1) -{ - TAG_LOGI(AAFwkTag::ABILITYMGR, "ToolInfo_ParseToJson_002 start"); - - ToolInfo tool; - tool.name = "tool_with_subcmds"; - tool.version = "2.0.0"; - tool.hasSubCommand = true; - - SubCommandInfo subCmd; - subCmd.description = "Test subcommand"; - subCmd.inputSchema = R"({"type": "object"})"; - subCmd.outputSchema = R"({"type": "string"})"; - tool.subcommands["sub1"] = subCmd; - - nlohmann::json json = tool.ParseToJson(); - - EXPECT_TRUE(json.contains("subcommands")); - EXPECT_TRUE(json["subcommands"].contains("sub1")); - - TAG_LOGI(AAFwkTag::ABILITYMGR, "ToolInfo_ParseToJson_002 end"); -} - -// ==================== ToolInfo ParseFromJson Tests ==================== - -/** - * @tc.name: ToolInfo_ParseFromJson_001 - * @tc.desc: Test parsing JSON to ToolInfo - * @tc.type: FUNC - */ -HWTEST_F(CliToolDataManagerTest, ToolInfo_ParseFromJson_001, testing::ext::TestSize.Level1) -{ - TAG_LOGI(AAFwkTag::ABILITYMGR, "ToolInfo_ParseFromJson_001 start"); - - nlohmann::json json = R"({ - "name": "ohos-json_tool", - "version": "1.0.0", - "description": "JSON test tool", - "executablePath": "/bin/jsontest", - "requirePermissions": ["ohos.permission.INTERNET"], - "inputSchema": {"type": "object"}, - "outputSchema": {"type": "string"}, - "argMapping": {"type": "flag", "separator": " "}, - "eventSchemas": {"stdout": {"type": "string"}}, - "timeout": 30000, - "eventTypes": ["stdout"], - "hasSubCommand": false, - "subcommands": {} - })"_json; - - ToolInfo tool; - bool result = ToolInfo::ParseFromJson(json, tool); - - EXPECT_TRUE(result); - EXPECT_EQ(tool.name, "ohos-json_tool"); - EXPECT_EQ(tool.version, "1.0.0"); - EXPECT_EQ(tool.description, "JSON test tool"); - EXPECT_EQ(tool.executablePath, "/bin/jsontest"); - EXPECT_EQ(tool.timeout, 30000); - EXPECT_EQ(tool.hasSubCommand, false); - EXPECT_TRUE(tool.argMapping != nullptr); - EXPECT_EQ(tool.argMapping->type, ArgMappingType::FLAG); - - TAG_LOGI(AAFwkTag::ABILITYMGR, "ToolInfo_ParseFromJson_001 end"); -} - -/** - * @tc.name: ToolInfo_ParseFromJson_002 - * @tc.desc: Test parsing JSON with subcommands to ToolInfo - * @tc.type: FUNC - */ -HWTEST_F(CliToolDataManagerTest, ToolInfo_ParseFromJson_002, testing::ext::TestSize.Level1) -{ - TAG_LOGI(AAFwkTag::ABILITYMGR, "ToolInfo_ParseFromJson_002 start"); - - nlohmann::json json = R"({ - "name": "hms-tool_with_subs", - "version": "1.0.0", - "description": "Tool with subcommands", - "executablePath": "/bin/tool", - "hasSubCommand": true, - "subcommands": { - "build": { - "description": "Build subcommand", - "inputSchema": {"type": "object"}, - "outputSchema": {"type": "string"}, - "argMapping": {"type": "flag"} - }, - "run": { - "description": "Run subcommand", - "argMapping": {"type": "positional", "order": "arg1"} - } - } - })"_json; - - ToolInfo tool; - bool result = ToolInfo::ParseFromJson(json, tool); - - EXPECT_TRUE(result); - EXPECT_EQ(tool.name, "hms-tool_with_subs"); - EXPECT_TRUE(tool.hasSubCommand); - EXPECT_EQ(tool.subcommands.size(), 2u); - EXPECT_EQ(tool.subcommands["build"].description, "Build subcommand"); - - TAG_LOGI(AAFwkTag::ABILITYMGR, "ToolInfo_ParseFromJson_002 end"); -} - -/** - * @tc.name: ToolInfo_ParseFromJson_003 - * @tc.desc: Test parsing empty JSON to ToolInfo - * @tc.type: FUNC - */ -HWTEST_F(CliToolDataManagerTest, ToolInfo_ParseFromJson_003, testing::ext::TestSize.Level1) -{ - TAG_LOGI(AAFwkTag::ABILITYMGR, "ToolInfo_ParseFromJson_003 start"); - - nlohmann::json json; - - ToolInfo tool; - bool result = ToolInfo::ParseFromJson(json, tool); - - EXPECT_FALSE(result); - EXPECT_TRUE(tool.name.empty()); - - TAG_LOGI(AAFwkTag::ABILITYMGR, "ToolInfo_ParseFromJson_003 end"); -} - -// ==================== ToolInfo ParseFromJson/ParseToJson Round Trip Tests ==================== - -/** - * @tc.name: ToolInfo_ParseFromJson_ParseToJson_RoundTrip_001 - * @tc.desc: Test ToolInfo ParseFromJson and ParseToJson round trip - * @tc.type: FUNC - */ -HWTEST_F(CliToolDataManagerTest, ToolInfo_ParseFromJson_ParseToJson_RoundTrip_001, testing::ext::TestSize.Level1) -{ - TAG_LOGI(AAFwkTag::ABILITYMGR, "ToolInfo_ParseFromJson_ParseToJson_RoundTrip_001 start"); - - nlohmann::json originalJson = R"({ - "name": "ohos-roundtrip_tool", - "version": "3.0.0", - "description": "Round trip test", - "executablePath": "/bin/roundtrip", - "requirePermissions": ["ohos.permission.CAMERA"], - "inputSchema": {"type": "object", "properties": {"input": {"type": "string"}}}, - "outputSchema": {"type": "array"}, - "argMapping": {"type": "positional", "order": "arg1,arg2"}, - "eventSchemas": {"exit": {"type": "number"}}, - "timeout": 60000, - "eventTypes": ["stdout", "stderr", "exit"], - "hasSubCommand": true, - "subcommands": { - "sub1": { - "description": "Sub 1", - "inputSchema": {"type": "object"}, - "outputSchema": {"type": "string"}, - "argMapping": {"type": "flag"} - } - } - })"_json; - - ToolInfo tool; - bool result = ToolInfo::ParseFromJson(originalJson, tool); - EXPECT_TRUE(result); - nlohmann::json resultJson = tool.ParseToJson(); - - EXPECT_EQ(resultJson["name"], originalJson["name"]); - EXPECT_EQ(resultJson["version"], originalJson["version"]); - EXPECT_EQ(resultJson["description"], originalJson["description"]); - EXPECT_EQ(resultJson["executablePath"], originalJson["executablePath"]); - EXPECT_EQ(resultJson["timeout"], originalJson["timeout"]); - EXPECT_EQ(resultJson["hasSubCommand"], originalJson["hasSubCommand"]); - EXPECT_EQ(resultJson["eventTypes"], originalJson["eventTypes"]); - - TAG_LOGI(AAFwkTag::ABILITYMGR, "ToolInfo_ParseFromJson_ParseToJson_RoundTrip_001 end"); -} - -// ==================== SyncToolNames Tests ==================== - -/** - * @tc.name: CliToolDataManager_SyncToolNames_001 - * @tc.desc: Test that removed tools are deleted from KVStore when loading from directory - * @tc.type: FUNC - */ -HWTEST_F(CliToolDataManagerTest, CliToolDataManager_SyncToolNames_001, testing::ext::TestSize.Level1) -{ - TAG_LOGI(AAFwkTag::ABILITYMGR, "CliToolDataManager_SyncToolNames_001 start"); - - // Create tool3.json for this test - std::ofstream file3(TEST_TOOL3_FILE); - file3 << R"({ - "name": "ohos-test_tool3", - "version": "1.0.0", - "description": "Test tool 3", - "executablePath": "/bin/test3", - "requirePermissions": [], - "inputSchema": {}, - "outputSchema": {}, - "argMapping": {"type": "flag"}, - "eventSchemas": {}, - "timeout": 30000, - "eventTypes": [], - "hasSubCommand": false, - "subcommands": {} - })"; - file3.close(); - - // First load: load all three tools - auto& dataManager = CliToolDataManager::GetInstance(); - std::vector tools; - int32_t ret = dataManager.GetAllTools(tools); - EXPECT_EQ(ret, 0); - - // Verify tool3 exists - ToolInfo tool3; - bool foundTool3 = false; - for (const auto& tool : tools) { - if (tool.name == "ohos-test_tool3") { - foundTool3 = true; - break; - } - } - EXPECT_TRUE(foundTool3); - - // Remove tool3.json to simulate tool removal - std::remove(TEST_TOOL3_FILE); - - // Reset the loaded flag to force reload - // Note: This test relies on the implementation detail that tools are loaded lazily - // In a real scenario, the manager would be restarted or the cache would be invalidated - - TAG_LOGI(AAFwkTag::ABILITYMGR, "CliToolDataManager_SyncToolNames_001 end"); -} - -/** - * @tc.name: CliToolDataManager_SyncToolNames_002 - * @tc.desc: Test that AllCliToolNames key is stored in KVStore after loading - * @tc.type: FUNC - */ -HWTEST_F(CliToolDataManagerTest, CliToolDataManager_SyncToolNames_002, testing::ext::TestSize.Level1) -{ - TAG_LOGI(AAFwkTag::ABILITYMGR, "CliToolDataManager_SyncToolNames_002 start"); - - // Ensure tools are loaded - auto& dataManager = CliToolDataManager::GetInstance(); - std::vector tools; - int32_t ret = dataManager.GetAllTools(tools); - EXPECT_EQ(ret, 0); - - // The test verifies that the loading process completes successfully - // The AllCliToolNames key should be stored internally - EXPECT_TRUE(tools.size() >= 0); - - TAG_LOGI(AAFwkTag::ABILITYMGR, "CliToolDataManager_SyncToolNames_002 end"); -} - -/** - * @tc.name: CliToolDataManager_SyncToolNames_003 - * @tc.desc: Test loading tools when directory has no JSON files - * @tc.type: FUNC - */ -HWTEST_F(CliToolDataManagerTest, CliToolDataManager_SyncToolNames_003, testing::ext::TestSize.Level1) -{ - TAG_LOGI(AAFwkTag::ABILITYMGR, "CliToolDataManager_SyncToolNames_003 start"); - - // Create an empty temporary directory - const char* emptyDir = "/data/test_empty_configs"; - std::system("mkdir -p " + std::string(emptyDir)); - - // The test verifies that loading from empty directory doesn't crash - // and returns successfully - - // Clean up - std::rmdir(emptyDir); - - TAG_LOGI(AAFwkTag::ABILITYMGR, "CliToolDataManager_SyncToolNames_003 end"); -} - -} // namespace CliTool -} // namespace OHOS diff --git a/test/unittest/cli_tool_mgr/cli_tool_mgr_client_test/cli_tool_mgr_client_test.cpp b/test/unittest/cli_tool_mgr/cli_tool_mgr_client_test/cli_tool_mgr_client_test.cpp deleted file mode 100644 index 9fa05e59a9..0000000000 --- a/test/unittest/cli_tool_mgr/cli_tool_mgr_client_test/cli_tool_mgr_client_test.cpp +++ /dev/null @@ -1,130 +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 - -#define private public -#include "cli_tool_mgr_client.h" -#undef private - -using namespace testing::ext; - -namespace OHOS { -namespace CliTool { - -class CliToolMGRClientTest : public testing::Test { -public: - static void SetUpTestCase(void); - static void TearDownTestCase(void); - void SetUp(); - void TearDown(); -}; - -void CliToolMGRClientTest::SetUpTestCase(void) {} -void CliToolMGRClientTest::TearDownTestCase(void) {} -void CliToolMGRClientTest::SetUp() {} -void CliToolMGRClientTest::TearDown() {} - -/** - * @tc.name: CliToolMGRClient_GetInstance_0100 - * @tc.desc: Test GetInstance returns singleton instance - * @tc.type: FUNC - */ -HWTEST_F(CliToolMGRClientTest, GetInstance_0100, TestSize.Level1) -{ - GTEST_LOG_(INFO) << "CliToolMGRClient_GetInstance_0100 start"; - - auto& instance1 = CliToolMGRClient::GetInstance(); - auto& instance2 = CliToolMGRClient::GetInstance(); - - EXPECT_EQ(&instance1, &instance2); - - GTEST_LOG_(INFO) << "CliToolMGRClient_GetInstance_0100 end"; -} - -/** - * @tc.name: CliToolMGRClient_GetAllToolSummaries_0100 - * @tc.desc: Test GetAllToolSummaries returns error when proxy is null - * @tc.type: FUNC - */ -HWTEST_F(CliToolMGRClientTest, GetAllToolSummaries_0100, TestSize.Level1) -{ - GTEST_LOG_(INFO) << "CliToolMGRClient_GetAllToolSummaries_0100 start"; - - auto& client = CliToolMGRClient::GetInstance(); - std::vector summaries; - ErrCode ret = client.GetAllToolSummaries(summaries); - - EXPECT_NE(ret, -2); - - GTEST_LOG_(INFO) << "CliToolMGRClient_GetAllToolSummaries_0100 end"; -} - -/** - * @tc.name: CliToolMGRClient_GetToolInfoByName_0100 - * @tc.desc: Test GetToolInfoByName returns error when proxy is null - * @tc.type: FUNC - */ -HWTEST_F(CliToolMGRClientTest, GetToolInfoByName_0100, TestSize.Level1) -{ - GTEST_LOG_(INFO) << "CliToolMGRClient_GetToolInfoByName_0100 start"; - - auto& client = CliToolMGRClient::GetInstance(); - ToolInfo tool; - ErrCode ret = client.GetToolInfoByName("test_tool", tool); - - EXPECT_NE(ret, -2); - - GTEST_LOG_(INFO) << "CliToolMGRClient_GetToolInfoByName_0100 end"; -} - -/** - * @tc.name: CliToolMGRClient_GetAllToolInfos_0100 - * @tc.desc: Test GetAllToolInfos returns error when proxy is null - * @tc.type: FUNC - */ -HWTEST_F(CliToolMGRClientTest, GetAllToolInfos_0100, TestSize.Level1) -{ - GTEST_LOG_(INFO) << "CliToolMGRClient_GetAllToolInfos_0100 start"; - - auto& client = CliToolMGRClient::GetInstance(); - std::vector tools; - ErrCode ret = client.GetAllToolInfos(tools); - - EXPECT_NE(ret, -2); - - GTEST_LOG_(INFO) << "CliToolMGRClient_GetAllToolInfos_0100 end"; -} - -/** - * @tc.name: CliToolMGRClient_RegisterTool_0100 - * @tc.desc: Test RegisterTool returns error when proxy is null - * @tc.type: FUNC - */ -HWTEST_F(CliToolMGRClientTest, RegisterTool_0100, TestSize.Level1) -{ - GTEST_LOG_(INFO) << "CliToolMGRClient_RegisterTool_0100 start"; - - auto& client = CliToolMGRClient::GetInstance(); - ToolInfo tool; - tool.name = "test_tool"; - ErrCode ret = client.RegisterTool(tool); - - EXPECT_NE(ret, -2); - - GTEST_LOG_(INFO) << "CliToolMGRClient_RegisterTool_0100 end"; -} -} // namespace CliTool -} // namespace OHOS diff --git a/test/unittest/cli_tool_mgr/cli_tool_mgr_service_test/cli_tool_mgr_service_test.cpp b/test/unittest/cli_tool_mgr/cli_tool_mgr_service_test/cli_tool_mgr_service_test.cpp deleted file mode 100644 index 1d035cee02..0000000000 --- a/test/unittest/cli_tool_mgr/cli_tool_mgr_service_test/cli_tool_mgr_service_test.cpp +++ /dev/null @@ -1,274 +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 -#include - -#define private public -#include "cli_tool_manager_service.h" -#undef private - -#include "cli_error_code.h" -#include "exec_options.h" -#include "tool_info.h" - -using namespace testing::ext; -using namespace OHOS::CliTool; - -namespace OHOS { -namespace CliTool { -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 service_; -}; - -void CliToolManagerServiceTest::SetUpTestCase(void) -{ - // Initialize test environment -} - -void CliToolManagerServiceTest::TearDownTestCase(void) -{ - // Cleanup test environment -} - -void CliToolManagerServiceTest::SetUp() -{ - service_ = CliToolManagerService::GetInstance(); - service_->activeSessionCount_.store(0); -} - -void CliToolManagerServiceTest::TearDown() -{ - service_->activeSessionCount_.store(0); -} - -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_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"; - - service_->activeSessionCount_.store(8); - - ExecToolParam param; - param.toolName = "test_tool"; - param.subcommand = ""; - param.challenge = "test_challenge"; - - int32_t result = service_->ExecTool(param, "test"); - - EXPECT_EQ(result, ERR_SESSION_LIMIT_EXCEEDED); - EXPECT_EQ(service_->activeSessionCount_.load(), 8); - - 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"; - - int32_t result = service_->ExecTool(param, "test"); - - EXPECT_EQ(result, ERR_TOOL_NOT_EXIST); - - 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"; - - int32_t result = service_->ExecTool(param, "test"); - - EXPECT_EQ(result, ERR_TOOL_NOT_EXIST); - - 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"; - - std::string schema = R"({ - "properties": { - "build": { - "type": "object", - "description": "Build subcommand" - } - } - })"; - RegisterTestTool("test_tool_subcmd", schema); - - ExecToolParam param; - param.toolName = "test_tool_subcmd"; - param.subcommand = "invalid_subcmd"; - param.challenge = "test_challenge"; - - int32_t result = service_->ExecTool(param, "test"); - - 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 - std::vector tools; - int32_t result = service_->GetAllToolInfos(tools); - - // 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 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"; -} - -} // namespace CliTool -} // namespace OHOS diff --git a/test/unittest/dfr_test/appfreeze_cpu_freq_manager_test/BUILD.gn b/test/unittest/dfr_test/appfreeze_cpu_freq_manager_test/BUILD.gn index c97fb26e67..d95cdb10c0 100644 --- a/test/unittest/dfr_test/appfreeze_cpu_freq_manager_test/BUILD.gn +++ b/test/unittest/dfr_test/appfreeze_cpu_freq_manager_test/BUILD.gn @@ -29,12 +29,12 @@ ohos_unittest("appfreeze_cpu_freq_manager_test") { sources = [ "appfreeze_cpu_freq_manager_test.cpp" ] deps = [ + "${ability_runtime_innerkits_path}/app_manager:app_manager", "${ability_runtime_path}/utils/global/freeze:freeze_util", "${ability_runtime_services_path}/abilitymgr:abilityms", ] external_deps = [ - "ability_runtime:app_manager", "ffrt:libffrt", "googletest:gmock_main", "googletest:gtest_main", diff --git a/test/unittest/dfr_test/appfreeze_inner_test/appfreeze_inner_test.cpp b/test/unittest/dfr_test/appfreeze_inner_test/appfreeze_inner_test.cpp index e0b43c70b6..8565d91cdc 100644 --- a/test/unittest/dfr_test/appfreeze_inner_test/appfreeze_inner_test.cpp +++ b/test/unittest/dfr_test/appfreeze_inner_test/appfreeze_inner_test.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2023 Huawei Device Co., Ltd. + * Copyright (c) 2022-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 @@ -479,6 +479,50 @@ HWTEST_F(AppfreezeInnerTest, AppfreezeInner_ChangeFaultDateInfo_004, TestSize.Le EXPECT_TRUE(faultData.forceExit); } +/** + * @tc.number: AppfreezeInner_GetMainStackDump_001 + * @tc.name: GetMainStackDump + * @tc.desc: Verify that function GetMainStackDump. + */ +HWTEST_F(AppfreezeInnerTest, AppfreezeInner_GetMainStackDump_001, TestSize.Level1) +{ + appfreezeInner->lastMainStack_ = ""; + int pid = -1; + std::string ret = appfreezeInner->GetMainStackDump(pid); + pid = getpid(); + appfreezeInner->GetMainStackDump(pid); + pid = 1; + appfreezeInner->GetMainStackDump(pid); + EXPECT_TRUE(!ret.empty()); +} + +/** + * @tc.number: AppfreezeInner_GetMainStackDump_002 + * @tc.name: GetMainStackDump + * @tc.desc: Verify that function GetMainStackDump. + */ +HWTEST_F(AppfreezeInnerTest, AppfreezeInner_GetMainStackDump_002, TestSize.Level1) +{ + appfreezeInner->lastMainStack_ = ""; + int pid = getpid(); + std::string firstCall = appfreezeInner->GetMainStackDump(pid); + std::string secondCall = appfreezeInner->GetMainStackDump(pid); + EXPECT_EQ(firstCall, secondCall); +} + +/** + * @tc.number: AppfreezeInner_GetMainStackDump_003 + * @tc.name: GetMainStackDump + * @tc.desc: Verify that function GetMainStackDump. + */ +HWTEST_F(AppfreezeInnerTest, AppfreezeInner_GetMainStackDump_003, TestSize.Level1) +{ + appfreezeInner->lastMainStack_ = ""; + int pid = 999999; + std::string ret = appfreezeInner->GetMainStackDump(pid); + EXPECT_TRUE(ret.empty()); +} + /** * @tc.number: AppfreezeInnerTest * @tc.name: add test diff --git a/test/unittest/dfr_test/appfreeze_manager_test/appfreeze_manager_test.cpp b/test/unittest/dfr_test/appfreeze_manager_test/appfreeze_manager_test.cpp index 4a824e2bf6..c4feb6033c 100644 --- a/test/unittest/dfr_test/appfreeze_manager_test/appfreeze_manager_test.cpp +++ b/test/unittest/dfr_test/appfreeze_manager_test/appfreeze_manager_test.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023-2025 Huawei Device Co., Ltd. + * Copyright (c) 2023-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 @@ -914,53 +914,70 @@ HWTEST_F(AppfreezeManagerTest, AppfreezeManagerTest_UpdateFreezeExcludedPid_001, EXPECT_FALSE(appfreezeManager->IsFreezeExcludedPid(TEST_PID_THIRD)); } -/** - * @tc.number: AppfreezeManagerTest_UpdateFreezeExcludedPid_002 - * @tc.desc: Test capacity limit cleanup logic when size exceeds FREEZE_EXCLUDED_PID_MAX_SIZE - * @tc.type: FUNC - */ -HWTEST_F(AppfreezeManagerTest, AppfreezeManagerTest_UpdateFreezeExcludedPid_002, TestSize.Level1) -{ - EXPECT_NE(appfreezeManager, nullptr); - appfreezeManager->freezeExcludedPidMap_.clear(); - - constexpr int32_t TEST_PROFILER_PID = 5000; - constexpr int32_t BASE_PID = 10000; - constexpr size_t MAX_SIZE = 100; - constexpr size_t CLEAN_COUNT = 30; - constexpr int32_t TIME_DELAY_US = 100; - - for (size_t i = 0; i < MAX_SIZE; i++) { - int32_t targetPid = BASE_PID + static_cast(i); - appfreezeManager->UpdateFreezeExcludedPid(true, targetPid, TEST_PROFILER_PID); - usleep(TIME_DELAY_US); - } - EXPECT_EQ(appfreezeManager->freezeExcludedPidMap_.size(), MAX_SIZE); - - usleep(TIME_DELAY_US); - int32_t newPid = BASE_PID + static_cast(MAX_SIZE); - appfreezeManager->UpdateFreezeExcludedPid(true, newPid, TEST_PROFILER_PID); - - size_t expectedSize = MAX_SIZE + 1 - CLEAN_COUNT; - EXPECT_EQ(appfreezeManager->freezeExcludedPidMap_.size(), expectedSize); - - for (size_t i = 0; i < CLEAN_COUNT; i++) { - int32_t oldPid = BASE_PID + static_cast(i); - EXPECT_FALSE(appfreezeManager->freezeExcludedPidMap_.find(oldPid) != - appfreezeManager->freezeExcludedPidMap_.end()); - } - - for (size_t i = CLEAN_COUNT; i < MAX_SIZE + 1; i++) { - int32_t remainingPid = BASE_PID + static_cast(i); - EXPECT_TRUE(appfreezeManager->freezeExcludedPidMap_.find(remainingPid) != - appfreezeManager->freezeExcludedPidMap_.end()); - } - - int32_t newestPid = BASE_PID + static_cast(MAX_SIZE); - EXPECT_TRUE(appfreezeManager->freezeExcludedPidMap_.find(newestPid) != - appfreezeManager->freezeExcludedPidMap_.end()); - - appfreezeManager->freezeExcludedPidMap_.clear(); -} +/** + * @tc.number: AppfreezeManagerTest_UpdateFreezeExcludedPid_002 + * @tc.desc: Test capacity limit cleanup logic when size exceeds FREEZE_EXCLUDED_PID_MAX_SIZE + * @tc.type: FUNC + */ +HWTEST_F(AppfreezeManagerTest, AppfreezeManagerTest_UpdateFreezeExcludedPid_002, TestSize.Level1) +{ + EXPECT_NE(appfreezeManager, nullptr); + appfreezeManager->freezeExcludedPidMap_.clear(); + + constexpr int32_t TEST_PROFILER_PID = 5000; + constexpr int32_t BASE_PID = 10000; + constexpr size_t MAX_SIZE = 100; + constexpr size_t CLEAN_COUNT = 30; + constexpr int32_t TIME_DELAY_US = 100; + + for (size_t i = 0; i < MAX_SIZE; i++) { + int32_t targetPid = BASE_PID + static_cast(i); + appfreezeManager->UpdateFreezeExcludedPid(true, targetPid, TEST_PROFILER_PID); + usleep(TIME_DELAY_US); + } + EXPECT_EQ(appfreezeManager->freezeExcludedPidMap_.size(), MAX_SIZE); + + usleep(TIME_DELAY_US); + int32_t newPid = BASE_PID + static_cast(MAX_SIZE); + appfreezeManager->UpdateFreezeExcludedPid(true, newPid, TEST_PROFILER_PID); + + size_t expectedSize = MAX_SIZE + 1 - CLEAN_COUNT; + EXPECT_EQ(appfreezeManager->freezeExcludedPidMap_.size(), expectedSize); + + for (size_t i = 0; i < CLEAN_COUNT; i++) { + int32_t oldPid = BASE_PID + static_cast(i); + EXPECT_FALSE(appfreezeManager->freezeExcludedPidMap_.find(oldPid) != + appfreezeManager->freezeExcludedPidMap_.end()); + } + + for (size_t i = CLEAN_COUNT; i < MAX_SIZE + 1; i++) { + int32_t remainingPid = BASE_PID + static_cast(i); + EXPECT_TRUE(appfreezeManager->freezeExcludedPidMap_.find(remainingPid) != + appfreezeManager->freezeExcludedPidMap_.end()); + } + + int32_t newestPid = BASE_PID + static_cast(MAX_SIZE); + EXPECT_TRUE(appfreezeManager->freezeExcludedPidMap_.find(newestPid) != + appfreezeManager->freezeExcludedPidMap_.end()); + + appfreezeManager->freezeExcludedPidMap_.clear(); +} + +/** + * @tc.number: AppfreezeManagerTest CheckPreloadUIExtension Test + * @tc.desc: add testcase + * @tc.type: FUNC + */ +HWTEST_F(AppfreezeManagerTest, AppfreezeManagerTest_CheckPreloadUIExtension_Test001, TestSize.Level1) +{ + std::string bundleName = "AppfreezeManagerTest_CheckPreloadUIExtension_Test001"; + int32_t pid = getpid(); + std::string message = "test"; + bool result = appfreezeManager->CheckPreloadUIExtension(message, bundleName, pid); + EXPECT_EQ(result, false); + message = "PreloadUIExtension test"; + result = appfreezeManager->CheckPreloadUIExtension(message, bundleName, pid); + EXPECT_EQ(result, true); +} } // namespace AppExecFwk -} // namespace OHOS \ No newline at end of file +} // namespace OHOS diff --git a/test/unittest/dfr_test/cpu_data_processor_test/BUILD.gn b/test/unittest/dfr_test/cpu_data_processor_test/BUILD.gn index f719b538d7..6c4c5ae720 100644 --- a/test/unittest/dfr_test/cpu_data_processor_test/BUILD.gn +++ b/test/unittest/dfr_test/cpu_data_processor_test/BUILD.gn @@ -28,10 +28,9 @@ ohos_unittest("cpu_data_processor_test") { sources = [ "cpu_data_processor_test.cpp" ] - deps = [] + deps = [ "${ability_runtime_innerkits_path}/app_manager:app_manager" ] external_deps = [ - "ability_runtime:app_manager", "googletest:gmock_main", "googletest:gtest_main", ] diff --git a/test/unittest/dialog_session_manager_test/dialog_session_manager_test.cpp b/test/unittest/dialog_session_manager_test/dialog_session_manager_test.cpp index a352e20795..6b03b3936c 100644 --- a/test/unittest/dialog_session_manager_test/dialog_session_manager_test.cpp +++ b/test/unittest/dialog_session_manager_test/dialog_session_manager_test.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Copyright (c) 2024-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 @@ -22,6 +22,7 @@ #undef private #include "hilog_tag_wrapper.h" #include "mock_ability_token.h" +#include "request_start_ability_callback_stub.h" #include "start_ability_utils.h" using OHOS::AppExecFwk::AbilityType; @@ -79,6 +80,18 @@ sptr DialogSessionManagerTest::MockToken(AbilityType abilityType) return abilityRecord->GetToken(); } +class RequestAbilityImplCallback : public AAFwk::RequestStartAbilityCallbackStub { +public: + void OnRequestStartAbilityResult(bool result) override + { + TAG_LOGI(AAFwkTag::ABILITYMGR, "UIAbility start request result: %{public}s", + result ? "success" : "failed"); + result_ = result; + } + +bool result_ = false; +}; + /** * @tc.name: GetStartupSessionInfoTest_0100 * @tc.desc: Test GetStartupSessionInfo @@ -749,6 +762,33 @@ HWTEST_F(DialogSessionManagerTest, NotifyAbilityRequestFailure_0700, TestSize.Le GTEST_LOG_(INFO) << "NotifyAbilityRequestFailure_0700 end"; } +/** + * @tc.name: NotifyAbilityRequestFailure_0800 + * @tc.desc: Test NotifyAbilityRequestFailure triggers OnRequestStartAbilityResult(false) callback + * @tc.type: FUNC + */ +HWTEST_F(DialogSessionManagerTest, NotifyAbilityRequestFailure_0800, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "NotifyAbilityRequestFailure_0800 start"; + DialogSessionManager dialogSessionManager; + Want want; + want.SetParam(KEY_REQUEST_ID, std::string("test_request_id_0800")); + sptr dialogSessionInfo = nullptr; + std::shared_ptr dialogCallerInfo = std::make_shared(); + ASSERT_NE(dialogCallerInfo, nullptr); + + dialogCallerInfo->targetWant = want; + sptr testCallback = new RequestAbilityImplCallback(); + dialogCallerInfo->requestCallback = testCallback; + dialogSessionManager.SetDialogSessionInfo(TEST_DIALOG_SESSION_ID, dialogSessionInfo, dialogCallerInfo); + auto storedCallerInfo = dialogSessionManager.GetDialogCallerInfo(TEST_DIALOG_SESSION_ID); + ASSERT_NE(storedCallerInfo, nullptr); + ASSERT_NE(storedCallerInfo->requestCallback, nullptr); + dialogSessionManager.NotifyAbilityRequestFailure(TEST_DIALOG_SESSION_ID, want); + EXPECT_EQ(testCallback->result_, false); + GTEST_LOG_(INFO) << "NotifyAbilityRequestFailure_0800 end"; +} + /** * @tc.name: SetQueryERMSInfo_001 * @tc.desc: test SetQueryERMSInfo function diff --git a/test/unittest/disposed_observer_test/BUILD.gn b/test/unittest/disposed_observer_test/BUILD.gn new file mode 100644 index 0000000000..19594fb5ef --- /dev/null +++ b/test/unittest/disposed_observer_test/BUILD.gn @@ -0,0 +1,70 @@ +# 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("//build/test.gni") +import("//foundation/ability/ability_runtime/ability_runtime.gni") + +ohos_unittest("disposed_observer_test") { + module_out_path = "ability_runtime/ability_runtime/abilitymgr" + + sources = [ + "disposed_observer_test.cpp", + ] + + include_dirs = [ + "./", + "${ability_runtime_path}/interfaces/inner_api/ability_manager/include", + "${ability_runtime_path}/interfaces/inner_api/app_manager/include/appmgr", + "${ability_runtime_path}/interfaces/kits/native/ability/ability_runtime/", + "${ability_runtime_services_path}/abilitymgr/include", + "${ability_runtime_services_path}/abilitymgr/include/interceptor", + "${ability_runtime_test_path}/mock/frameworks_kits_ability_native_test/include", + ] + + deps = [ + "${ability_runtime_innerkits_path}/ability_manager:ability_manager", + "${ability_runtime_innerkits_path}/ability_manager:ability_start_options", + "${ability_runtime_innerkits_path}/app_manager:app_manager", + "${ability_runtime_services_path}/abilitymgr:abilityms", + ] + + external_deps = [ + "ability_base:base", + "ability_base:configuration", + "ability_base:want", + "bundle_framework:appexecfwk_base", + "bundle_framework:appexecfwk_core", + "c_utils:utils", + "eventhandler:libeventhandler", + "ffrt:libffrt", + "googletest:gmock", + "googletest:gmock_main", + "googletest:gtest_main", + "hilog:libhilog", + "ipc:ipc_core", + "samgr:samgr_proxy", + ] + + if (ability_runtime_graphics) { + external_deps += [ + "window_manager:libwsutils", + "window_manager:scene_session", + ] + } +} + +group("unittest") { + testonly = true + deps = [ ":disposed_observer_test" ] +} diff --git a/test/unittest/disposed_observer_test/disposed_observer_test.cpp b/test/unittest/disposed_observer_test/disposed_observer_test.cpp new file mode 100644 index 0000000000..7c6b4d31e9 --- /dev/null +++ b/test/unittest/disposed_observer_test/disposed_observer_test.cpp @@ -0,0 +1,552 @@ +/* + * 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 +#include +#include + +#include "ability_manager_errors.h" +#include "hilog_tag_wrapper.h" +#include "mock_ability_token.h" +#include "want.h" +#include "page_state_data.h" +#include "ability_info.h" +#include "application_info.h" + +#define private public +#define protected public +#include "disposed_observer.h" +#include "ability_record.h" +#include "interceptor/disposed_rule_interceptor.h" +#undef private +#undef protected + +namespace OHOS { +namespace AAFwk { +using namespace testing::ext; +using namespace AppExecFwk; + +class DisposedObserverTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp() override; + void TearDown() override; + + std::shared_ptr CreateDisposedObserver(ComponentType type); + std::shared_ptr CreateInterceptor(); + +protected: + std::shared_ptr interceptor_ = nullptr; + int32_t testUid_ = 1001; +}; + +void DisposedObserverTest::SetUpTestCase(void) +{} + +void DisposedObserverTest::TearDownTestCase(void) +{} + +void DisposedObserverTest::SetUp() +{} + +void DisposedObserverTest::TearDown() +{} + +std::shared_ptr DisposedObserverTest::CreateInterceptor() +{ + return std::make_shared(); +} + +std::shared_ptr DisposedObserverTest::CreateDisposedObserver(ComponentType type) +{ + Want want; + want.SetElementName("device", "com.example.test", "TestAbility"); + DisposedRule disposedRule; + disposedRule.want = std::make_shared(want); + disposedRule.componentType = type; + + interceptor_ = CreateInterceptor(); + return std::make_shared(disposedRule, interceptor_, testUid_); +} + +/** + * @tc.number: DisposedObserver_OnPageShow_0100 + * @tc.name: DisposedObserver::OnPageShow + * @tc.desc: pageStateData uid does not match observer uid, return directly without unregister. + */ +HWTEST_F(DisposedObserverTest, DisposedObserver_OnPageShow_0100, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::ABILITYMGR, "DisposedObserver_OnPageShow_0100 start"); + auto observer = CreateDisposedObserver(ComponentType::UI_EXTENSION); + + PageStateData pageStateData; + pageStateData.uid = testUid_ + 1; // Different uid + + // When uid doesn't match, OnPageShow returns early without any action + observer->OnPageShow(pageStateData); + + // Verify observer state remains unchanged + EXPECT_EQ(observer->uid_, testUid_); + + TAG_LOGI(AAFwkTag::ABILITYMGR, "DisposedObserver_OnPageShow_0100 end"); +} + +/** + * @tc.number: DisposedObserver_OnPageShow_0200 + * @tc.name: DisposedObserver::OnPageShow + * @tc.desc: componentType is UI_ABILITY, OnPageShow executes successfully. + */ +HWTEST_F(DisposedObserverTest, DisposedObserver_OnPageShow_0200, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::ABILITYMGR, "DisposedObserver_OnPageShow_0200 start"); + auto observer = CreateDisposedObserver(ComponentType::UI_ABILITY); + + PageStateData pageStateData; + pageStateData.uid = testUid_; + + // Verify observer is properly initialized + EXPECT_EQ(observer->uid_, testUid_); + EXPECT_NE(observer->interceptor_, nullptr); + + observer->OnPageShow(pageStateData); + + TAG_LOGI(AAFwkTag::ABILITYMGR, "DisposedObserver_OnPageShow_0200 end"); +} + +/** + * @tc.number: DisposedObserver_OnPageShow_0300 + * @tc.name: DisposedObserver::OnPageShow + * @tc.desc: componentType is UI_ABILITY with null token, UnregisterObserver still called. + */ +HWTEST_F(DisposedObserverTest, DisposedObserver_OnPageShow_0300, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::ABILITYMGR, "DisposedObserver_OnPageShow_0300 start"); + auto observer = CreateDisposedObserver(ComponentType::UI_ABILITY); + + PageStateData pageStateData; + pageStateData.uid = testUid_; + + observer->token_ = nullptr; + + observer->OnPageShow(pageStateData); + + EXPECT_NE(observer->interceptor_, nullptr); + EXPECT_EQ(observer->uid_, testUid_); + + TAG_LOGI(AAFwkTag::ABILITYMGR, "DisposedObserver_OnPageShow_0300 end"); +} + +/** + * @tc.number: DisposedObserver_OnPageShow_0400 + * @tc.name: DisposedObserver::OnPageShow + * @tc.desc: componentType is UI_EXTENSION with valid token, ExecuteUIExtension called. + */ +HWTEST_F(DisposedObserverTest, DisposedObserver_OnPageShow_0400, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::ABILITYMGR, "DisposedObserver_OnPageShow_0400 start"); + auto observer = CreateDisposedObserver(ComponentType::UI_EXTENSION); + + // Setup mock token + sptr token = new MockAbilityToken(); + observer->token_ = token; + + PageStateData pageStateData; + pageStateData.uid = testUid_; + + // Expect UnregisterObserver to be called + + observer->OnPageShow(pageStateData); + + // Verify observer state after OnPageShow + EXPECT_EQ(observer->uid_, testUid_); + + TAG_LOGI(AAFwkTag::ABILITYMGR, "DisposedObserver_OnPageShow_0400 end"); +} + +/** + * @tc.number: DisposedObserver_OnPageShow_0500 + * @tc.name: DisposedObserver::OnPageShow + * @tc.desc: componentType is UI_EXTENSION with null token, UnregisterObserver called. + */ +HWTEST_F(DisposedObserverTest, DisposedObserver_OnPageShow_0500, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::ABILITYMGR, "DisposedObserver_OnPageShow_0500 start"); + auto observer = CreateDisposedObserver(ComponentType::UI_EXTENSION); + + PageStateData pageStateData; + pageStateData.uid = testUid_; + + // Keep token_ as nullptr + observer->token_ = nullptr; + + // UnregisterObserver should be called even when ExecuteUIExtension fails + + observer->OnPageShow(pageStateData); + + // Verify observer state + EXPECT_EQ(observer->uid_, testUid_); + EXPECT_EQ(observer->token_, nullptr); + + TAG_LOGI(AAFwkTag::ABILITYMGR, "DisposedObserver_OnPageShow_0500 end"); +} + +/** + * * @tc.number: DisposedObserver_OnPageShow_0600 + * * @tc.name: DisposedObserver::OnPageShow - embeddable UIExtension path + * * @tc.desc: abilityRecord with EMBEDDED_FULL_SCREEN_MODE triggers embeddable path. + * */ +HWTEST_F(DisposedObserverTest, DisposedObserver_OnPageShow_0600, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::ABILITYMGR, "DisposedObserver_OnPageShow_0600 start"); + auto observer = CreateDisposedObserver(ComponentType::UI_EXTENSION); + + // Create real AbilityRecord with embeddable screen mode + Want want; + want.SetElementName("device", "com.example.test", "TestAbility"); + want.SetParam("ohos.extra.param.key.showMode", static_cast(1)); // EMBEDDED_FULL_SCREEN_MODE + + AppExecFwk::AbilityInfo abilityInfo; + abilityInfo.type = AppExecFwk::AbilityType::PAGE; + abilityInfo.process = "test_process"; + + AppExecFwk::ApplicationInfo applicationInfo; + + auto abilityRecord = std::make_shared(want, abilityInfo, applicationInfo); + EXPECT_NE(abilityRecord, nullptr); + + // Create token associated with abilityRecord + sptr token = new Token(abilityRecord); + observer->token_ = token; + + PageStateData pageStateData; + pageStateData.uid = testUid_; + + // This should trigger embeddable UIExtension path + observer->OnPageShow(pageStateData); + + EXPECT_EQ(observer->uid_, testUid_); + EXPECT_NE(observer->token_, nullptr); + + TAG_LOGI(AAFwkTag::ABILITYMGR, "DisposedObserver_OnPageShow_0600 end"); +} + +/** + * * @tc.number: DisposedObserver_OnPageShow_0700 + * * @tc.name: DisposedObserver::OnPageShow - PAGE type UIExtension path + * * @tc.desc: abilityRecord with PAGE type and non-embeddable mode triggers PAGE path. + * */ +HWTEST_F(DisposedObserverTest, DisposedObserver_OnPageShow_0700, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::ABILITYMGR, "DisposedObserver_OnPageShow_0700 start"); + auto observer = CreateDisposedObserver(ComponentType::UI_EXTENSION); + + // Create real AbilityRecord with PAGE type and non-embeddable mode + Want want; + want.SetElementName("device", "com.example.test", "TestAbility"); + want.SetParam("ohos.extra.param.key.showMode", static_cast(-1)); // IDLE_SCREEN_MODE + + AppExecFwk::AbilityInfo abilityInfo; + abilityInfo.type = AppExecFwk::AbilityType::PAGE; + abilityInfo.process = "test_process"; + + AppExecFwk::ApplicationInfo applicationInfo; + + auto abilityRecord = std::make_shared(want, abilityInfo, applicationInfo); + EXPECT_NE(abilityRecord, nullptr); + + // Create token associated with abilityRecord + sptr token = new Token(abilityRecord); + observer->token_ = token; + + PageStateData pageStateData; + pageStateData.uid = testUid_; + + // This should trigger PAGE type UIExtension path + observer->OnPageShow(pageStateData); + + EXPECT_EQ(observer->uid_, testUid_); + EXPECT_NE(observer->token_, nullptr); + + TAG_LOGI(AAFwkTag::ABILITYMGR, "DisposedObserver_OnPageShow_0700 end"); +} + +/** + * @tc.number: DisposedObserver_Constructor_0100 + * @tc.name: DisposedObserver Constructor + * @tc.desc: Verify DisposedObserver constructor initializes members correctly. + */ +HWTEST_F(DisposedObserverTest, DisposedObserver_Constructor_0100, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::ABILITYMGR, "DisposedObserver_Constructor_0100 start"); + + Want want; + want.SetElementName("device", "com.example.test", "TestAbility"); + DisposedRule disposedRule; + disposedRule.want = std::make_shared(want); + disposedRule.componentType = ComponentType::UI_EXTENSION; + + auto mockInterceptor = CreateInterceptor(); + int32_t testUid = 2002; + + auto observer = std::make_shared(disposedRule, mockInterceptor, testUid); + + EXPECT_NE(observer, nullptr); + EXPECT_EQ(observer->uid_, testUid); + + TAG_LOGI(AAFwkTag::ABILITYMGR, "DisposedObserver_Constructor_0100 end"); +} + +/** + * @tc.number: DisposedObserver_Members_0100 + * @tc.name: DisposedObserver member initialization + * @tc.desc: Verify disposedRule_ and interceptor_ are set correctly. + */ +HWTEST_F(DisposedObserverTest, DisposedObserver_Members_0100, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::ABILITYMGR, "DisposedObserver_Members_0100 start"); + + Want want; + want.SetElementName("device", "com.example.test", "TestAbility"); + DisposedRule disposedRule; + disposedRule.want = std::make_shared(want); + disposedRule.componentType = ComponentType::UI_ABILITY; + + auto mockInterceptor = CreateInterceptor(); + int32_t testUid = 3003; + + auto observer = std::make_shared(disposedRule, mockInterceptor, testUid); + + EXPECT_EQ(observer->disposedRule_.componentType, ComponentType::UI_ABILITY); + EXPECT_NE(observer->disposedRule_.want, nullptr); + EXPECT_EQ(observer->disposedRule_.want->GetElement().GetAbilityName(), "TestAbility"); + EXPECT_EQ(observer->interceptor_, mockInterceptor); + EXPECT_EQ(observer->uid_, testUid); + + TAG_LOGI(AAFwkTag::ABILITYMGR, "DisposedObserver_Members_0100 end"); +} + +/** + * @tc.number: DisposedObserver_Token_0100 + * @tc.name: DisposedObserver token_ member + * @tc.desc: Verify token_ can be set and retrieved. + */ +HWTEST_F(DisposedObserverTest, DisposedObserver_Token_0100, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::ABILITYMGR, "DisposedObserver_Token_0100 start"); + + auto observer = CreateDisposedObserver(ComponentType::UI_EXTENSION); + + // Initially token_ should be nullptr + EXPECT_EQ(observer->token_, nullptr); + + // Set token_ + sptr token = new MockAbilityToken(); + observer->token_ = token; + + // Verify token_ is set + EXPECT_NE(observer->token_, nullptr); + EXPECT_EQ(observer->token_, token); + + TAG_LOGI(AAFwkTag::ABILITYMGR, "DisposedObserver_Token_0100 end"); +} + +/** + * @tc.number: DisposedObserver_UID_0100 + * @tc.name: DisposedObserver uid_ mismatch check + * @tc.desc: Verify OnPageShow returns early when uid doesn't match. + */ +HWTEST_F(DisposedObserverTest, DisposedObserver_UID_0100, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::ABILITYMGR, "DisposedObserver_UID_0100 start"); + + auto observer = CreateDisposedObserver(ComponentType::UI_ABILITY); + int32_t differentUid = testUid_ + 100; + + PageStateData pageStateData; + pageStateData.uid = differentUid; + + // When uid doesn't match, UnregisterObserver should NOT be called + + observer->OnPageShow(pageStateData); + + // Verify observer's uid_ hasn't changed + EXPECT_EQ(observer->uid_, testUid_); + + TAG_LOGI(AAFwkTag::ABILITYMGR, "DisposedObserver_UID_0100 end"); +} + +/** + * @tc.number: DisposedObserver_ComponentType_UIAbility_0100 + * @tc.name: DisposedObserver UI_ABILITY component type + * @tc.desc: Verify UI_ABILITY type triggers UnregisterObserver. + */ +HWTEST_F(DisposedObserverTest, DisposedObserver_ComponentType_UIAbility_0100, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::ABILITYMGR, "DisposedObserver_ComponentType_UIAbility_0100 start"); + + Want want; + want.SetElementName("device", "com.example.test", "TestAbility"); + DisposedRule disposedRule; + disposedRule.want = std::make_shared(want); + disposedRule.componentType = ComponentType::UI_ABILITY; + + auto mockInterceptor = CreateInterceptor(); + auto observer = std::make_shared(disposedRule, mockInterceptor, testUid_); + + PageStateData pageStateData; + pageStateData.uid = testUid_; + + // Verify componentType is correctly set + EXPECT_EQ(observer->disposedRule_.componentType, ComponentType::UI_ABILITY); + + observer->OnPageShow(pageStateData); + + TAG_LOGI(AAFwkTag::ABILITYMGR, "DisposedObserver_ComponentType_UIAbility_0100 end"); +} + +/** + * @tc.number: DisposedObserver_ComponentType_UIExtension_0100 + * @tc.name: DisposedObserver UI_EXTENSION component type + * @tc.desc: Verify UI_EXTENSION type triggers ExecuteUIExtension. + */ +HWTEST_F(DisposedObserverTest, DisposedObserver_ComponentType_UIExtension_0100, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::ABILITYMGR, "DisposedObserver_ComponentType_UIExtension_0100 start"); + + Want want; + want.SetElementName("device", "com.example.test", "TestAbility"); + DisposedRule disposedRule; + disposedRule.want = std::make_shared(want); + disposedRule.componentType = ComponentType::UI_EXTENSION; + + auto mockInterceptor = CreateInterceptor(); + auto observer = std::make_shared(disposedRule, mockInterceptor, testUid_); + + PageStateData pageStateData; + pageStateData.uid = testUid_; + + // Verify componentType is correctly set + EXPECT_EQ(observer->disposedRule_.componentType, ComponentType::UI_EXTENSION); + + // Verify UnregisterObserver is called (after ExecuteUIExtension) + + observer->OnPageShow(pageStateData); + + // Verify componentType is correctly set + EXPECT_EQ(observer->disposedRule_.componentType, ComponentType::UI_EXTENSION); + + TAG_LOGI(AAFwkTag::ABILITYMGR, "DisposedObserver_ComponentType_UIExtension_0100 end"); +} + +/** + * @tc.number: DisposedObserver_Want_0100 + * @tc.name: DisposedObserver want parameter + * @tc.desc: Verify want is correctly stored in disposedRule. + */ +HWTEST_F(DisposedObserverTest, DisposedObserver_Want_0100, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::ABILITYMGR, "DisposedObserver_Want_0100 start"); + + Want want; + want.SetElementName("testDevice", "com.example.bundle", "TestAbility"); + want.SetAction("test.action"); + want.SetFlags(0x1234); + + DisposedRule disposedRule; + disposedRule.want = std::make_shared(want); + disposedRule.componentType = ComponentType::UI_EXTENSION; + + auto mockInterceptor = CreateInterceptor(); + auto observer = std::make_shared(disposedRule, mockInterceptor, testUid_); + + // Verify want is correctly stored + EXPECT_NE(observer->disposedRule_.want, nullptr); + EXPECT_EQ(observer->disposedRule_.want->GetElement().GetDeviceID(), "testDevice"); + EXPECT_EQ(observer->disposedRule_.want->GetElement().GetBundleName(), "com.example.bundle"); + EXPECT_EQ(observer->disposedRule_.want->GetElement().GetAbilityName(), "TestAbility"); + EXPECT_EQ(observer->disposedRule_.want->GetAction(), "test.action"); + EXPECT_EQ(observer->disposedRule_.want->GetFlags(), 0x1234); + + TAG_LOGI(AAFwkTag::ABILITYMGR, "DisposedObserver_Want_0100 end"); +} + +/** + * @tc.number: DisposedObserver_MultipleObservers_0100 + * @tc.name: DisposedObserver multiple instances + * @tc.desc: Verify multiple observer instances can coexist independently. + */ +HWTEST_F(DisposedObserverTest, DisposedObserver_MultipleObservers_0100, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::ABILITYMGR, "DisposedObserver_MultipleObservers_0100 start"); + + Want want1, want2; + want1.SetElementName("device", "com.test1", "Ability1"); + want2.SetElementName("device", "com.test2", "Ability2"); + + DisposedRule disposedRule1, disposedRule2; + disposedRule1.want = std::make_shared(want1); + disposedRule1.componentType = ComponentType::UI_ABILITY; + disposedRule2.want = std::make_shared(want2); + disposedRule2.componentType = ComponentType::UI_EXTENSION; + + auto mockInterceptor1 = CreateInterceptor(); + auto mockInterceptor2 = CreateInterceptor(); + + int32_t uid1 = 1001; + int32_t uid2 = 1002; + + auto observer1 = std::make_shared(disposedRule1, mockInterceptor1, uid1); + auto observer2 = std::make_shared(disposedRule2, mockInterceptor2, uid2); + + // Verify each observer has independent state + EXPECT_EQ(observer1->uid_, uid1); + EXPECT_EQ(observer2->uid_, uid2); + EXPECT_EQ(observer1->disposedRule_.componentType, ComponentType::UI_ABILITY); + EXPECT_EQ(observer2->disposedRule_.componentType, ComponentType::UI_EXTENSION); + EXPECT_EQ(observer1->disposedRule_.want->GetElement().GetBundleName(), "com.test1"); + EXPECT_EQ(observer2->disposedRule_.want->GetElement().GetBundleName(), "com.test2"); + + TAG_LOGI(AAFwkTag::ABILITYMGR, "DisposedObserver_MultipleObservers_0100 end"); +} + +/** + * @tc.number: DisposedObserver_Interceptor_0100 + * @tc.name: DisposedObserver interceptor initialization + * @tc.desc: Verify interceptor is correctly initialized and stored. + */ +HWTEST_F(DisposedObserverTest, DisposedObserver_Interceptor_0100, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::ABILITYMGR, "DisposedObserver_Interceptor_0100 start"); + + Want want; + want.SetElementName("device", "com.example.test", "TestAbility"); + + DisposedRule disposedRule; + disposedRule.want = std::make_shared(want); + disposedRule.componentType = ComponentType::UI_EXTENSION; + + auto mockInterceptor = CreateInterceptor(); + auto observer = std::make_shared(disposedRule, mockInterceptor, testUid_); + + // Verify interceptor is stored + EXPECT_EQ(observer->interceptor_, mockInterceptor); + EXPECT_NE(observer->interceptor_, nullptr); + + TAG_LOGI(AAFwkTag::ABILITYMGR, "DisposedObserver_Interceptor_0100 end"); +} + +} // namespace AAFwk +} // namespace OHOS diff --git a/test/unittest/frameworks_kits_ability_ability_runtime_test/BUILD.gn b/test/unittest/frameworks_kits_ability_ability_runtime_test/BUILD.gn index a5958285a4..f05fe0ff60 100644 --- a/test/unittest/frameworks_kits_ability_ability_runtime_test/BUILD.gn +++ b/test/unittest/frameworks_kits_ability_ability_runtime_test/BUILD.gn @@ -306,6 +306,53 @@ ohos_unittest("dialog_callback_test") { ] } +ohos_unittest("ability_context_impl_third_test") { + module_out_path = module_out_path + + include_dirs = [ "${ability_runtime_path}/interfaces/kits/native/appkit/ability_runtime/context/" ] + + sources = [ + "ability_context_impl_third_test.cpp", + "mock_ability_manager_client.cpp", + "mock_context.cpp", + "mock_my_flag.cpp", + ] + + deps = [ + "${ability_runtime_innerkits_path}/ability_manager:ability_connect_callback_stub", + "${ability_runtime_innerkits_path}/ability_manager:ability_manager", + "${ability_runtime_innerkits_path}/ability_manager:mission_info", + "${ability_runtime_native_path}/ability:ability_context_native", + "${ability_runtime_native_path}/ability/native:abilitykit_native", + "${ability_runtime_services_path}/abilitymgr:abilityms", + ] + + external_deps = [ + "ability_base:configuration", + "ability_base:want", + "bundle_framework:appexecfwk_base", + "bundle_framework:appexecfwk_core", + "c_utils:utils", + "common_event_service:cesfwk_innerkits", + "eventhandler:libeventhandler", + "googletest:gmock_main", + "googletest:gtest_main", + "hilog:libhilog", + "ipc:ipc_core", + "ipc:ipc_napi", + "napi:ace_napi", + "resource_management:global_resmgr", + "samgr:samgr_proxy", + ] + + if (ability_runtime_graphics) { + external_deps += [ + "window_manager:libwsutils", + "window_manager:scene_session", + ] + } +} + ohos_unittest("ability_context_impl_new_test") { module_out_path = module_out_path @@ -361,6 +408,7 @@ group("unittest") { ":ability_context_impl_new_test", ":ability_context_impl_second_test", ":ability_context_impl_test", + ":ability_context_impl_third_test", ":ability_context_test1", ":caller_call_back_ut_test", ":dialog_callback_test", diff --git a/test/unittest/frameworks_kits_ability_ability_runtime_test/ability_context_impl_third_test.cpp b/test/unittest/frameworks_kits_ability_ability_runtime_test/ability_context_impl_third_test.cpp new file mode 100644 index 0000000000..e90b65e171 --- /dev/null +++ b/test/unittest/frameworks_kits_ability_ability_runtime_test/ability_context_impl_third_test.cpp @@ -0,0 +1,936 @@ +/* + * 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 + +#include "ability_context_impl.h" +#include "mock_context.h" +#include "mock_my_flag.h" +#include "ability_manager_errors.h" + +namespace OHOS { +namespace AppExecFwk { +using namespace testing::ext; +using namespace OHOS::AbilityRuntime; +using namespace OHOS::AAFwk; +namespace { +const std::string TEST_BUNDLE_NAME = "com.test.bundle"; +const std::string TEST_ABILITY_NAME = "TestAbility"; +const std::string TEST_MODULE_NAME = "entry"; +const int32_t TEST_REQUEST_CODE = 100; +const int32_t USER_CANCEL = -7; +} + +class AbilityContextImplThirdTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +public: + std::shared_ptr context_ = nullptr; + std::shared_ptr mock_ = nullptr; +}; + +void AbilityContextImplThirdTest::SetUpTestCase(void) {} + +void AbilityContextImplThirdTest::TearDownTestCase(void) {} + +void AbilityContextImplThirdTest::SetUp(void) +{ + context_ = std::make_shared(); + mock_ = std::make_shared(); +} + +void AbilityContextImplThirdTest::TearDown(void) {} + +/** + * @tc.number: AbilityContextImplThirdTest_SetToken_0100 + * @tc.name: SetToken and GetToken + * @tc.desc: Test SetToken and GetToken with valid token. + */ +HWTEST_F(AbilityContextImplThirdTest, AbilityContextImplThirdTest_SetToken_0100, Function | MediumTest | Level1) +{ + EXPECT_EQ(context_->GetToken(), nullptr); + sptr token = new (std::nothrow) IPCObjectStub(); + ASSERT_NE(token, nullptr); + context_->SetToken(token); + EXPECT_EQ(context_->GetToken(), token); +} + +/** + * @tc.number: AbilityContextImplThirdTest_SetToken_0200 + * @tc.name: SetToken and GetToken + * @tc.desc: Test SetToken with nullptr. + */ +HWTEST_F(AbilityContextImplThirdTest, AbilityContextImplThirdTest_SetToken_0200, Function | MediumTest | Level1) +{ + sptr token = new (std::nothrow) IPCObjectStub(); + context_->SetToken(token); + EXPECT_EQ(context_->GetToken(), token); + context_->SetToken(nullptr); + EXPECT_EQ(context_->GetToken(), nullptr); +} + +/** + * @tc.number: AbilityContextImplThirdTest_SetAbilityRecordId_0100 + * @tc.name: SetAbilityRecordId and GetAbilityRecordId + * @tc.desc: Test SetAbilityRecordId and GetAbilityRecordId with valid id. + */ +HWTEST_F(AbilityContextImplThirdTest, AbilityContextImplThirdTest_SetAbilityRecordId_0100, + Function | MediumTest | Level1) +{ + EXPECT_EQ(context_->GetAbilityRecordId(), 0); + int32_t recordId = 12345; + context_->SetAbilityRecordId(recordId); + EXPECT_EQ(context_->GetAbilityRecordId(), recordId); +} + +/** + * @tc.number: AbilityContextImplThirdTest_SetAbilityRecordId_0200 + * @tc.name: SetAbilityRecordId and GetAbilityRecordId + * @tc.desc: Test SetAbilityRecordId with negative value. + */ +HWTEST_F(AbilityContextImplThirdTest, AbilityContextImplThirdTest_SetAbilityRecordId_0200, + Function | MediumTest | Level1) +{ + int32_t recordId = -1; + context_->SetAbilityRecordId(recordId); + EXPECT_EQ(context_->GetAbilityRecordId(), recordId); +} + +/** + * @tc.number: AbilityContextImplThirdTest_RestoreWindowStage_0100 + * @tc.name: RestoreWindowStage(void*) + * @tc.desc: Test RestoreWindowStage with void* contentStorage. + */ +HWTEST_F(AbilityContextImplThirdTest, AbilityContextImplThirdTest_RestoreWindowStage_0100, + Function | MediumTest | Level1) +{ + int testData = 42; + void *contentStorage = &testData; + ErrCode ret = context_->RestoreWindowStage(contentStorage); + EXPECT_EQ(ret, ERR_OK); + EXPECT_EQ(context_->GetEtsContentStorage(), contentStorage); +} + +/** + * @tc.number: AbilityContextImplThirdTest_RestoreWindowStage_0200 + * @tc.name: RestoreWindowStage(void*) + * @tc.desc: Test RestoreWindowStage with nullptr. + */ +HWTEST_F(AbilityContextImplThirdTest, AbilityContextImplThirdTest_RestoreWindowStage_0200, + Function | MediumTest | Level1) +{ + ErrCode ret = context_->RestoreWindowStage(nullptr); + EXPECT_EQ(ret, ERR_OK); + EXPECT_EQ(context_->GetEtsContentStorage(), nullptr); +} + +/** + * @tc.number: AbilityContextImplThirdTest_RestoreWindowStage_0300 + * @tc.name: RestoreWindowStage(void*) + * @tc.desc: Test RestoreWindowStage returns ERR_NOT_SUPPORTED when IsHook is true. + */ +HWTEST_F(AbilityContextImplThirdTest, AbilityContextImplThirdTest_RestoreWindowStage_0300, + Function | MediumTest | Level1) +{ + context_->SetHook(true); + int testData = 42; + void *contentStorage = &testData; + ErrCode ret = context_->RestoreWindowStage(contentStorage); + EXPECT_EQ(ret, -2); +} + +/** + * @tc.number: AbilityContextImplThirdTest_StartSelf_0100 + * @tc.name: StartSelf + * @tc.desc: Test StartSelf calls AbilityManagerClient, verifies context is not destroyed. + */ +HWTEST_F(AbilityContextImplThirdTest, AbilityContextImplThirdTest_StartSelf_0100, Function | MediumTest | Level1) +{ + sptr token = new (std::nothrow) IPCObjectStub(); + context_->SetToken(token); + ErrCode ret = context_->StartSelf(); + EXPECT_EQ(ret, ERR_OK); +} + +/** + * @tc.number: AbilityContextImplThirdTest_SetHook_0100 + * @tc.name: SetHook and IsHook + * @tc.desc: Test SetHook and IsHook default and modified values. + */ +HWTEST_F(AbilityContextImplThirdTest, AbilityContextImplThirdTest_SetHook_0100, Function | MediumTest | Level1) +{ + EXPECT_EQ(context_->IsHook(), false); + context_->SetHook(true); + EXPECT_EQ(context_->IsHook(), true); + context_->SetHook(false); + EXPECT_EQ(context_->IsHook(), false); +} + +/** + * @tc.number: AbilityContextImplThirdTest_SetHookOff_0100 + * @tc.name: SetHookOff and GetHookOff + * @tc.desc: Test SetHookOff and GetHookOff default and modified values. + */ +HWTEST_F(AbilityContextImplThirdTest, AbilityContextImplThirdTest_SetHookOff_0100, Function | MediumTest | Level1) +{ + EXPECT_EQ(context_->GetHookOff(), false); + context_->SetHookOff(true); + EXPECT_EQ(context_->GetHookOff(), true); + context_->SetHookOff(false); + EXPECT_EQ(context_->GetHookOff(), false); +} + +/** + * @tc.number: AbilityContextImplThirdTest_IsTerminating_0100 + * @tc.name: IsTerminating and SetTerminating + * @tc.desc: Test IsTerminating initial state and after setting. + */ +HWTEST_F(AbilityContextImplThirdTest, AbilityContextImplThirdTest_IsTerminating_0100, + Function | MediumTest | Level1) +{ + EXPECT_EQ(context_->IsTerminating(), false); + context_->SetTerminating(true); + EXPECT_EQ(context_->IsTerminating(), true); + context_->SetTerminating(false); + EXPECT_EQ(context_->IsTerminating(), false); +} + +/** + * @tc.number: AbilityContextImplThirdTest_SetStageContext_0100 + * @tc.name: SetStageContext + * @tc.desc: Test SetStageContext with valid context and verify getters work. + */ +HWTEST_F(AbilityContextImplThirdTest, AbilityContextImplThirdTest_SetStageContext_0100, + Function | MediumTest | Level1) +{ + context_->SetStageContext(mock_); + EXPECT_EQ(context_->GetBundleName(), "com.test.bundleName"); + EXPECT_NE(context_->GetApplicationInfo(), nullptr); + EXPECT_EQ(context_->GetBundleCodePath(), "codePath"); +} + +/** + * @tc.number: AbilityContextImplThirdTest_SetStageContext_0200 + * @tc.name: SetStageContext with nullptr + * @tc.desc: Test SetStageContext with nullptr and verify getters return defaults. + */ +HWTEST_F(AbilityContextImplThirdTest, AbilityContextImplThirdTest_SetStageContext_0200, + Function | MediumTest | Level1) +{ + context_->SetStageContext(nullptr); + EXPECT_EQ(context_->GetBundleName(), ""); + EXPECT_EQ(context_->GetApplicationInfo(), nullptr); + EXPECT_EQ(context_->GetBundleCodePath(), ""); +} + +/** + * @tc.number: AbilityContextImplThirdTest_SetAbilityInfo_0100 + * @tc.name: SetAbilityInfo and GetAbilityInfo + * @tc.desc: Test SetAbilityInfo with valid info. + */ +HWTEST_F(AbilityContextImplThirdTest, AbilityContextImplThirdTest_SetAbilityInfo_0100, + Function | MediumTest | Level1) +{ + EXPECT_EQ(context_->GetAbilityInfo(), nullptr); + auto abilityInfo = std::make_shared(); + abilityInfo->name = TEST_ABILITY_NAME; + abilityInfo->bundleName = TEST_BUNDLE_NAME; + context_->SetAbilityInfo(abilityInfo); + EXPECT_EQ(context_->GetAbilityInfo(), abilityInfo); + EXPECT_EQ(context_->GetAbilityInfo()->name, TEST_ABILITY_NAME); + EXPECT_EQ(context_->GetAbilityInfo()->bundleName, TEST_BUNDLE_NAME); +} + +/** + * @tc.number: AbilityContextImplThirdTest_SetConfiguration_0100 + * @tc.name: SetConfiguration and GetConfiguration + * @tc.desc: Test SetConfiguration and GetConfiguration. + */ +HWTEST_F(AbilityContextImplThirdTest, AbilityContextImplThirdTest_SetConfiguration_0100, + Function | MediumTest | Level1) +{ + EXPECT_EQ(context_->GetConfiguration(), nullptr); + auto config = std::make_shared(); + config->AddItem(AAFwk::GlobalConfigurationKey::SYSTEM_LANGUAGE, "zh_CN"); + context_->SetConfiguration(config); + EXPECT_EQ(context_->GetConfiguration(), config); +} + +/** + * @tc.number: AbilityContextImplThirdTest_GetWant_0100 + * @tc.name: GetWant + * @tc.desc: Test GetWant when abilityCallback_ is nullptr. + */ +HWTEST_F(AbilityContextImplThirdTest, AbilityContextImplThirdTest_GetWant_0100, Function | MediumTest | Level1) +{ + std::weak_ptr emptyCallback; + context_->RegisterAbilityCallback(emptyCallback); + auto want = context_->GetWant(); + EXPECT_EQ(want, nullptr); +} + +/** + * @tc.number: AbilityContextImplThirdTest_InsertRemoveResultCallbackTask_0100 + * @tc.name: InsertResultCallbackTask and RemoveResultCallbackTask + * @tc.desc: Test insert and remove result callback task via OnAbilityResult behavior. + */ +HWTEST_F(AbilityContextImplThirdTest, AbilityContextImplThirdTest_InsertRemoveResultCallbackTask_0100, + Function | MediumTest | Level1) +{ + // Test insert: verify callback is invoked by OnAbilityResult + bool callbackInvoked = false; + RuntimeTask task = [&callbackInvoked](int32_t code, const AAFwk::Want& want, bool isInner) { + callbackInvoked = true; + }; + context_->InsertResultCallbackTask(TEST_REQUEST_CODE, std::move(task)); + AAFwk::Want resultData; + context_->OnAbilityResult(TEST_REQUEST_CODE, 0, resultData); + EXPECT_EQ(callbackInvoked, true); + + // Test remove: verify removed callback is not invoked by OnAbilityResult + bool callbackInvoked2 = false; + RuntimeTask task2 = [&callbackInvoked2](int32_t code, const AAFwk::Want& want, bool isInner) { + callbackInvoked2 = true; + }; + int requestCode2 = TEST_REQUEST_CODE + 1; + context_->InsertResultCallbackTask(requestCode2, std::move(task2)); + context_->RemoveResultCallbackTask(requestCode2); + context_->OnAbilityResult(requestCode2, 0, resultData); + EXPECT_EQ(callbackInvoked2, false); +} + +/** + * @tc.number: AbilityContextImplThirdTest_RemoveResultCallbackTask_0100 + * @tc.name: RemoveResultCallbackTask + * @tc.desc: Test remove non-existent result callback task does not affect other callbacks. + */ +HWTEST_F(AbilityContextImplThirdTest, AbilityContextImplThirdTest_RemoveResultCallbackTask_0100, + Function | MediumTest | Level1) +{ + bool callbackInvoked = false; + RuntimeTask task = [&callbackInvoked](int32_t code, const AAFwk::Want& want, bool isInner) { + callbackInvoked = true; + }; + context_->InsertResultCallbackTask(TEST_REQUEST_CODE, std::move(task)); + context_->RemoveResultCallbackTask(999); + AAFwk::Want resultData; + context_->OnAbilityResult(TEST_REQUEST_CODE, 0, resultData); + EXPECT_EQ(callbackInvoked, true); +} + +/** + * @tc.number: AbilityContextImplThirdTest_SetAbilityResourceManager_0100 + * @tc.name: SetAbilityResourceManager and GetResourceManager + * @tc.desc: Test SetAbilityResourceManager overrides stageContext resource manager. + */ +HWTEST_F(AbilityContextImplThirdTest, AbilityContextImplThirdTest_SetAbilityResourceManager_0100, + Function | MediumTest | Level1) +{ + EXPECT_EQ(context_->GetResourceManager(), nullptr); + auto resourceMgr = std::shared_ptr(Global::Resource::CreateResourceManager()); + ASSERT_NE(resourceMgr, nullptr); + context_->SetAbilityResourceManager(resourceMgr); + auto retrieved = context_->GetResourceManager(); + EXPECT_EQ(retrieved, resourceMgr); +} + +/** + * @tc.number: AbilityContextImplThirdTest_RegisterAbilityConfigUpdateCallback_0100 + * @tc.name: RegisterAbilityConfigUpdateCallback + * @tc.desc: Test RegisterAbilityConfigUpdateCallback registers callback that is invoked. + */ +HWTEST_F(AbilityContextImplThirdTest, AbilityContextImplThirdTest_RegisterAbilityConfigUpdateCallback_0100, + Function | MediumTest | Level1) +{ + bool called = false; + auto callback = [&called](AppExecFwk::Configuration& config) { + called = true; + }; + context_->RegisterAbilityConfigUpdateCallback(callback); + context_->SetAbilityColorMode(0); + EXPECT_EQ(called, true); +} + +/** + * @tc.number: AbilityContextImplThirdTest_GetAbilityConfiguration_0100 + * @tc.name: GetAbilityConfiguration + * @tc.desc: Test GetAbilityConfiguration default and after setting. + */ +HWTEST_F(AbilityContextImplThirdTest, AbilityContextImplThirdTest_GetAbilityConfiguration_0100, + Function | MediumTest | Level1) +{ + EXPECT_EQ(context_->GetAbilityConfiguration(), nullptr); + AppExecFwk::Configuration config; + config.AddItem(AAFwk::GlobalConfigurationKey::SYSTEM_COLORMODE, "dark"); + context_->SetAbilityConfiguration(config); + auto abilityConfig = context_->GetAbilityConfiguration(); + ASSERT_NE(abilityConfig, nullptr); +} + +/** + * @tc.number: AbilityContextImplThirdTest_SetAbilityColorMode_0100 + * @tc.name: SetAbilityColorMode + * @tc.desc: Test SetAbilityColorMode with invalid color mode values does not invoke callback. + */ +HWTEST_F(AbilityContextImplThirdTest, AbilityContextImplThirdTest_SetAbilityColorMode_0100, + Function | MediumTest | Level1) +{ + bool called = false; + auto callback = [&called](AppExecFwk::Configuration& config) { + called = true; + }; + context_->RegisterAbilityConfigUpdateCallback(callback); + context_->SetAbilityColorMode(-2); + EXPECT_EQ(called, false); + context_->SetAbilityColorMode(2); + EXPECT_EQ(called, false); +} + +/** + * @tc.number: AbilityContextImplThirdTest_SetAbilityColorMode_0200 + * @tc.name: SetAbilityColorMode + * @tc.desc: Test SetAbilityColorMode with valid color mode and callback. + */ +HWTEST_F(AbilityContextImplThirdTest, AbilityContextImplThirdTest_SetAbilityColorMode_0200, + Function | MediumTest | Level1) +{ + bool called = false; + auto callback = [&called](AppExecFwk::Configuration& config) { + called = true; + }; + context_->RegisterAbilityConfigUpdateCallback(callback); + context_->SetAbilityColorMode(0); + EXPECT_EQ(called, true); +} + +/** + * @tc.number: AbilityContextImplThirdTest_SetAbilityColorMode_0300 + * @tc.name: SetAbilityColorMode + * @tc.desc: Test SetAbilityColorMode with empty callback does not crash. + */ +HWTEST_F(AbilityContextImplThirdTest, AbilityContextImplThirdTest_SetAbilityColorMode_0300, + Function | MediumTest | Level1) +{ + context_->RegisterAbilityConfigUpdateCallback(AbilityConfigUpdateCallback()); + context_->SetAbilityColorMode(0); + EXPECT_EQ(context_->GetAbilityConfiguration(), nullptr); +} + +/** + * @tc.number: AbilityContextImplThirdTest_AddCompletionHandler_0100 + * @tc.name: AddCompletionHandler + * @tc.desc: Test AddCompletionHandler with duplicate requestId returns ERR_OK and does not add duplicate. + */ +HWTEST_F(AbilityContextImplThirdTest, AbilityContextImplThirdTest_AddCompletionHandler_0100, + Function | MediumTest | Level1) +{ + int successCount = 0; + std::string requestId = "dup_request_id"; + OnRequestResult onRequestSucc = [&successCount](const AppExecFwk::ElementName&, const std::string&) { + successCount++; + }; + OnRequestResult onRequestFail = [](const AppExecFwk::ElementName&, const std::string&) {}; + auto ret = context_->AddCompletionHandler(requestId, onRequestSucc, onRequestFail); + EXPECT_EQ(ret, ERR_OK); + ret = context_->AddCompletionHandler(requestId, onRequestSucc, onRequestFail); + EXPECT_EQ(ret, ERR_OK); + // Verify only one handler exists by calling OnRequestSuccess twice + AppExecFwk::ElementName element; + context_->OnRequestSuccess(requestId, element, "test"); + EXPECT_EQ(successCount, 1); + context_->OnRequestSuccess(requestId, element, "test"); + EXPECT_EQ(successCount, 1); +} + +/** + * @tc.number: AbilityContextImplThirdTest_SetRestoreEnabled_0100 + * @tc.name: SetRestoreEnabled and GetRestoreEnabled + * @tc.desc: Test SetRestoreEnabled and GetRestoreEnabled with normal context. + */ +HWTEST_F(AbilityContextImplThirdTest, AbilityContextImplThirdTest_SetRestoreEnabled_0100, + Function | MediumTest | Level1) +{ + EXPECT_EQ(context_->GetRestoreEnabled(), false); + context_->SetRestoreEnabled(true); + EXPECT_EQ(context_->GetRestoreEnabled(), true); + context_->SetRestoreEnabled(false); + EXPECT_EQ(context_->GetRestoreEnabled(), false); +} + +/** + * @tc.number: AbilityContextImplThirdTest_SetRestoreEnabled_0200 + * @tc.name: SetRestoreEnabled when isHook + * @tc.desc: Test SetRestoreEnabled is skipped when context is hook module. + */ +HWTEST_F(AbilityContextImplThirdTest, AbilityContextImplThirdTest_SetRestoreEnabled_0200, + Function | MediumTest | Level1) +{ + context_->SetHook(true); + context_->SetRestoreEnabled(false); + context_->SetRestoreEnabled(true); + EXPECT_EQ(context_->GetRestoreEnabled(), false); +} + +/** + * @tc.number: AbilityContextImplThirdTest_OnAbilityResult_0100 + * @tc.name: OnAbilityResult + * @tc.desc: Test OnAbilityResult with registered callback, verifying isInner is false. + */ +HWTEST_F(AbilityContextImplThirdTest, AbilityContextImplThirdTest_OnAbilityResult_0100, + Function | MediumTest | Level1) +{ + int requestCode = TEST_REQUEST_CODE; + bool innerReceived = true; + RuntimeTask task = [&innerReceived](int32_t code, const AAFwk::Want& want, bool isInner) { + innerReceived = isInner; + }; + context_->InsertResultCallbackTask(requestCode, std::move(task)); + AAFwk::Want resultData; + context_->OnAbilityResult(requestCode, 0, resultData); + EXPECT_EQ(innerReceived, false); +} + +/** + * @tc.number: AbilityContextImplThirdTest_OnAbilityResult_0200 + * @tc.name: OnAbilityResult + * @tc.desc: Test OnAbilityResult with no registered callback does not crash. + */ +HWTEST_F(AbilityContextImplThirdTest, AbilityContextImplThirdTest_OnAbilityResult_0200, + Function | MediumTest | Level1) +{ + int requestCode = TEST_REQUEST_CODE; + AAFwk::Want resultData; + context_->OnAbilityResult(requestCode, 0, resultData); + // No callback registered, no crash expected + EXPECT_EQ(context_->IsTerminating(), false); +} + +/** + * @tc.number: AbilityContextImplThirdTest_SetWeakSessionToken_0100 + * @tc.name: SetWeakSessionToken + * @tc.desc: Test SetWeakSessionToken with valid token does not crash. + */ +HWTEST_F(AbilityContextImplThirdTest, AbilityContextImplThirdTest_SetWeakSessionToken_0100, + Function | MediumTest | Level1) +{ + sptr token = new (std::nothrow) IPCObjectStub(); + ASSERT_NE(token, nullptr); + context_->SetWeakSessionToken(token); + // SetWeakSessionToken stores weak pointer internally, no crash expected + EXPECT_NE(token, nullptr); +} + +/** + * @tc.number: AbilityContextImplThirdTest_SetWeakSessionToken_0200 + * @tc.name: SetWeakSessionToken + * @tc.desc: Test SetWeakSessionToken with nullptr does not crash. + */ +HWTEST_F(AbilityContextImplThirdTest, AbilityContextImplThirdTest_SetWeakSessionToken_0200, + Function | MediumTest | Level1) +{ + context_->SetWeakSessionToken(nullptr); + // SetWeakSessionToken with nullptr, no crash expected + EXPECT_EQ(context_->GetToken(), nullptr); +} + +/** + * @tc.number: AbilityContextImplThirdTest_SetOnNewWantSkipScenarios_0100 + * @tc.name: SetOnNewWantSkipScenarios + * @tc.desc: Test SetOnNewWantSkipScenarios calls AbilityManagerClient without crash and returns ErrCode. + */ +HWTEST_F(AbilityContextImplThirdTest, AbilityContextImplThirdTest_SetOnNewWantSkipScenarios_0100, + Function | MediumTest | Level1) +{ + sptr token = new (std::nothrow) IPCObjectStub(); + context_->SetToken(token); + auto ret = context_->SetOnNewWantSkipScenarios(0); + EXPECT_EQ(ret, ERR_OK); + ret = context_->SetOnNewWantSkipScenarios(1); + EXPECT_EQ(ret, ERR_OK); +} + +/** + * @tc.number: AbilityContextImplThirdTest_NotifyCancelGamePreLaunch_0100 + * @tc.name: NotifyCancelGamePreLaunch + * @tc.desc: Test NotifyCancelGamePreLaunch calls AbilityManagerClient and returns ERR_OK. + */ +HWTEST_F(AbilityContextImplThirdTest, AbilityContextImplThirdTest_NotifyCancelGamePreLaunch_0100, + Function | MediumTest | Level1) +{ + sptr token = new (std::nothrow) IPCObjectStub(); + context_->SetToken(token); + auto ret = context_->NotifyCancelGamePreLaunch(); + EXPECT_EQ(ret, ERR_OK); +} + +/** + * @tc.number: AbilityContextImplThirdTest_NotifyCompleteGamePreLaunch_0100 + * @tc.name: NotifyCompleteGamePreLaunch + * @tc.desc: Test NotifyCompleteGamePreLaunch calls AbilityManagerClient and returns ERR_OK. + */ +HWTEST_F(AbilityContextImplThirdTest, AbilityContextImplThirdTest_NotifyCompleteGamePreLaunch_0100, + Function | MediumTest | Level1) +{ + sptr token = new (std::nothrow) IPCObjectStub(); + context_->SetToken(token); + auto ret = context_->NotifyCompleteGamePreLaunch(); + EXPECT_EQ(ret, ERR_OK); +} + +/** + * @tc.number: AbilityContextImplThirdTest_CreateModuleContext_0100 + * @tc.name: CreateModuleContext + * @tc.desc: Test CreateModuleContext with bundle name and module name when stageContext_ is null. + */ +HWTEST_F(AbilityContextImplThirdTest, AbilityContextImplThirdTest_CreateModuleContext_0100, + Function | MediumTest | Level1) +{ + auto result = context_->CreateModuleContext(TEST_BUNDLE_NAME, TEST_MODULE_NAME); + EXPECT_EQ(result, nullptr); +} + +/** + * @tc.number: AbilityContextImplThirdTest_CreateModuleContext_0200 + * @tc.name: CreateModuleContext + * @tc.desc: Test CreateModuleContext with only module name when stageContext_ is null. + */ +HWTEST_F(AbilityContextImplThirdTest, AbilityContextImplThirdTest_CreateModuleContext_0200, + Function | MediumTest | Level1) +{ + auto result = context_->CreateModuleContext(TEST_MODULE_NAME); + EXPECT_EQ(result, nullptr); +} + +/** + * @tc.number: AbilityContextImplThirdTest_CreateBundleContext_0100 + * @tc.name: CreateBundleContext + * @tc.desc: Test CreateBundleContext when stageContext_ is null. + */ +HWTEST_F(AbilityContextImplThirdTest, AbilityContextImplThirdTest_CreateBundleContext_0100, + Function | MediumTest | Level1) +{ + auto result = context_->CreateBundleContext(TEST_BUNDLE_NAME); + EXPECT_EQ(result, nullptr); +} + +/** + * @tc.number: AbilityContextImplThirdTest_CreateModuleResourceManager_0100 + * @tc.name: CreateModuleResourceManager + * @tc.desc: Test CreateModuleResourceManager when stageContext_ is null. + */ +HWTEST_F(AbilityContextImplThirdTest, AbilityContextImplThirdTest_CreateModuleResourceManager_0100, + Function | MediumTest | Level1) +{ + auto result = context_->CreateModuleResourceManager(TEST_BUNDLE_NAME, TEST_MODULE_NAME); + EXPECT_EQ(result, nullptr); +} + +/** + * @tc.number: AbilityContextImplThirdTest_CreateSystemHspModuleResourceManager_0100 + * @tc.name: CreateSystemHspModuleResourceManager + * @tc.desc: Test CreateSystemHspModuleResourceManager when stageContext_ is null. + */ +HWTEST_F(AbilityContextImplThirdTest, AbilityContextImplThirdTest_CreateSystemHspModuleResourceManager_0100, + Function | MediumTest | Level1) +{ + std::shared_ptr resourceMgr = nullptr; + auto ret = context_->CreateSystemHspModuleResourceManager(TEST_BUNDLE_NAME, TEST_MODULE_NAME, resourceMgr); + EXPECT_EQ(ret, ERR_INVALID_VALUE); +} + +/** + * @tc.number: AbilityContextImplThirdTest_CreateAreaModeContext_0100 + * @tc.name: CreateAreaModeContext + * @tc.desc: Test CreateAreaModeContext when stageContext_ is null. + */ +HWTEST_F(AbilityContextImplThirdTest, AbilityContextImplThirdTest_CreateAreaModeContext_0100, + Function | MediumTest | Level1) +{ + auto result = context_->CreateAreaModeContext(0); + EXPECT_EQ(result, nullptr); +} + +/** + * @tc.number: AbilityContextImplThirdTest_GetArea_0100 + * @tc.name: GetArea + * @tc.desc: Test GetArea when stageContext_ is null returns EL_DEFAULT. + */ +HWTEST_F(AbilityContextImplThirdTest, AbilityContextImplThirdTest_GetArea_0100, Function | MediumTest | Level1) +{ + context_->SetStageContext(nullptr); + int area = context_->GetArea(); + EXPECT_EQ(area, 1); +} + +/** + * @tc.number: AbilityContextImplThirdTest_GetArea_0200 + * @tc.name: GetArea + * @tc.desc: Test GetArea when stageContext_ is set. + */ +HWTEST_F(AbilityContextImplThirdTest, AbilityContextImplThirdTest_GetArea_0200, Function | MediumTest | Level1) +{ + context_->SetStageContext(mock_); + int mode = 2; + context_->SwitchArea(mode); + int area = context_->GetArea(); + EXPECT_EQ(area, mode); +} + +/** + * @tc.number: AbilityContextImplThirdTest_StartExtensionAbilityWithExtensionType_0100 + * @tc.name: StartExtensionAbilityWithExtensionType + * @tc.desc: Test StartExtensionAbilityWithExtensionType with SERVICE type. + */ +HWTEST_F(AbilityContextImplThirdTest, AbilityContextImplThirdTest_StartExtensionAbilityWithExtensionType_0100, + Function | MediumTest | Level1) +{ + AAFwk::Want want; + want.SetElementName(TEST_BUNDLE_NAME, TEST_ABILITY_NAME); + auto result = context_->StartExtensionAbilityWithExtensionType( + want, AppExecFwk::ExtensionAbilityType::SERVICE); + EXPECT_EQ(result, ERR_OK); +} + +/** + * @tc.number: AbilityContextImplThirdTest_StopExtensionAbilityWithExtensionType_0100 + * @tc.name: StopExtensionAbilityWithExtensionType + * @tc.desc: Test StopExtensionAbilityWithExtensionType with SERVICE type. + */ +HWTEST_F(AbilityContextImplThirdTest, AbilityContextImplThirdTest_StopExtensionAbilityWithExtensionType_0100, + Function | MediumTest | Level1) +{ + AAFwk::Want want; + want.SetElementName(TEST_BUNDLE_NAME, TEST_ABILITY_NAME); + auto result = context_->StopExtensionAbilityWithExtensionType( + want, AppExecFwk::ExtensionAbilityType::SERVICE); + EXPECT_EQ(result, ERR_OK); +} + +/** + * @tc.number: AbilityContextImplThirdTest_NotifyBindingObjectConfigUpdate_0100 + * @tc.name: NotifyBindingObjectConfigUpdate + * @tc.desc: Test NotifyBindingObjectConfigUpdate when config is null, callback should not be invoked. + */ +HWTEST_F(AbilityContextImplThirdTest, AbilityContextImplThirdTest_NotifyBindingObjectConfigUpdate_0100, + Function | MediumTest | Level1) +{ + bool callbackInvoked = false; + context_->RegisterBindingObjectConfigUpdateCallback( + [&callbackInvoked](std::shared_ptr config) { + callbackInvoked = true; + }); + // config is null by default (no SetConfiguration called) + context_->NotifyBindingObjectConfigUpdate(); + EXPECT_EQ(callbackInvoked, false); +} + +/** + * @tc.number: AbilityContextImplThirdTest_NotifyBindingObjectConfigUpdate_0200 + * @tc.name: NotifyBindingObjectConfigUpdate + * @tc.desc: Test NotifyBindingObjectConfigUpdate with config and valid callback, callback is invoked. + */ +HWTEST_F(AbilityContextImplThirdTest, AbilityContextImplThirdTest_NotifyBindingObjectConfigUpdate_0200, + Function | MediumTest | Level1) +{ + bool callbackInvoked = false; + std::shared_ptr receivedConfig; + auto testConfig = std::make_shared(); + testConfig->AddItem(AAFwk::GlobalConfigurationKey::SYSTEM_LANGUAGE, "en"); + context_->SetConfiguration(testConfig); + context_->RegisterBindingObjectConfigUpdateCallback( + [&callbackInvoked, &receivedConfig](std::shared_ptr config) { + callbackInvoked = true; + receivedConfig = config; + }); + context_->NotifyBindingObjectConfigUpdate(); + EXPECT_EQ(callbackInvoked, true); + EXPECT_EQ(receivedConfig, testConfig); +} + +/** + * @tc.number: AbilityContextImplThirdTest_StartSelfUIAbilityInCurrentProcess_0100 + * @tc.name: StartSelfUIAbilityInCurrentProcess + * @tc.desc: Test StartSelfUIAbilityInCurrentProcess with hasOptions false. + */ +HWTEST_F(AbilityContextImplThirdTest, AbilityContextImplThirdTest_StartSelfUIAbilityInCurrentProcess_0100, + Function | MediumTest | Level1) +{ + AAFwk::Want want; + want.SetElementName(TEST_BUNDLE_NAME, TEST_ABILITY_NAME); + std::string specifiedFlag = "testFlag"; + AAFwk::StartOptions startOptions; + auto ret = context_->StartSelfUIAbilityInCurrentProcess(want, specifiedFlag, startOptions, false); + EXPECT_EQ(ret, ERR_OK); +} + +/** + * @tc.number: AbilityContextImplThirdTest_StartSelfUIAbilityInCurrentProcess_0200 + * @tc.name: StartSelfUIAbilityInCurrentProcess + * @tc.desc: Test StartSelfUIAbilityInCurrentProcess with hasOptions true. + */ +HWTEST_F(AbilityContextImplThirdTest, AbilityContextImplThirdTest_StartSelfUIAbilityInCurrentProcess_0200, + Function | MediumTest | Level1) +{ + AAFwk::Want want; + want.SetElementName(TEST_BUNDLE_NAME, TEST_ABILITY_NAME); + std::string specifiedFlag; + AAFwk::StartOptions startOptions; + auto ret = context_->StartSelfUIAbilityInCurrentProcess(want, specifiedFlag, startOptions, true); + EXPECT_EQ(ret, ERR_OK); +} + +/** + * @tc.number: AbilityContextImplThirdTest_GetProcessName_0100 + * @tc.name: GetProcessName + * @tc.desc: Test GetProcessName with stageContext_ set returns correct value. + */ +HWTEST_F(AbilityContextImplThirdTest, AbilityContextImplThirdTest_GetProcessName_0100, + Function | MediumTest | Level1) +{ + context_->SetStageContext(mock_); + EXPECT_EQ(context_->GetProcessName(), "processName"); +} + +/** + * @tc.number: AbilityContextImplThirdTest_GetProcessName_0200 + * @tc.name: GetProcessName + * @tc.desc: Test GetProcessName with stageContext_ null returns empty string. + */ +HWTEST_F(AbilityContextImplThirdTest, AbilityContextImplThirdTest_GetProcessName_0200, + Function | MediumTest | Level1) +{ + context_->SetStageContext(nullptr); + EXPECT_EQ(context_->GetProcessName(), ""); +} + +/** + * @tc.number: AbilityContextImplThirdTest_GetHapModuleInfo_0100 + * @tc.name: GetHapModuleInfo + * @tc.desc: Test GetHapModuleInfo returns nullptr when stageContext_ is null. + */ +HWTEST_F(AbilityContextImplThirdTest, AbilityContextImplThirdTest_GetHapModuleInfo_0100, + Function | MediumTest | Level1) +{ + context_->SetStageContext(nullptr); + auto ret = context_->GetHapModuleInfo(); + EXPECT_EQ(ret, nullptr); +} + +/** + * @tc.number: AbilityContextImplThirdTest_EraseUIExtension_0100 + * @tc.name: EraseUIExtension + * @tc.desc: Test EraseUIExtension with non-existent sessionId does not crash. + */ +HWTEST_F(AbilityContextImplThirdTest, AbilityContextImplThirdTest_EraseUIExtension_0100, + Function | MediumTest | Level1) +{ + context_->EraseUIExtension(999); + context_->EraseUIExtension(1); + AAFwk::Want want; + EXPECT_EQ(context_->IsUIExtensionExist(want), false); +} + +/** + * @tc.number: AbilityContextImplThirdTest_IsUIExtensionExist_0100 + * @tc.name: IsUIExtensionExist + * @tc.desc: Test IsUIExtensionExist with empty map returns false. + */ +HWTEST_F(AbilityContextImplThirdTest, AbilityContextImplThirdTest_IsUIExtensionExist_0100, + Function | MediumTest | Level1) +{ + AAFwk::Want matchWant; + matchWant.SetElementName(TEST_BUNDLE_NAME, TEST_MODULE_NAME, TEST_ABILITY_NAME); + EXPECT_EQ(context_->IsUIExtensionExist(matchWant), false); + AAFwk::Want noMatchWant; + noMatchWant.SetElementName("other.bundle", "other.module", "OtherAbility"); + EXPECT_EQ(context_->IsUIExtensionExist(noMatchWant), false); +} + +/** + * @tc.number: AbilityContextImplThirdTest_OnRequestFailure_0100 + * @tc.name: OnRequestFailure with USER_CANCEL resultCode + * @tc.desc: Test failure callback receives USER_CANCEL info when resultCode is USER_CANCEL. + */ +HWTEST_F(AbilityContextImplThirdTest, AbilityContextImplThirdTest_OnRequestFailure_0100, + Function | MediumTest | Level1) +{ + int32_t failureCode = -1; + std::string failureMessage; + std::string requestId = "test_cancel"; + OnAtomicRequestSuccess onSuccess = [](const std::string&) {}; + OnAtomicRequestFailure onFailure = + [&failureCode, &failureMessage](const std::string& appId, int32_t code, const std::string& msg) { + failureCode = code; + failureMessage = msg; + }; + context_->AddCompletionHandlerForAtomicService(requestId, onSuccess, onFailure, "com.test"); + AppExecFwk::ElementName element; + context_->OnRequestFailure(requestId, element, "test message", USER_CANCEL); + EXPECT_EQ(failureCode, 1); + EXPECT_EQ(failureMessage, "The user canceled this startup"); +} + +/** + * @tc.number: AbilityContextImplThirdTest_OnRequestFailure_0200 + * @tc.name: OnRequestFailure with user refused message + * @tc.desc: Test failure callback receives user refuse info when message contains refusal. + */ +HWTEST_F(AbilityContextImplThirdTest, AbilityContextImplThirdTest_OnRequestFailure_0200, + Function | MediumTest | Level1) +{ + int32_t failureCode = -1; + std::string failureMessage; + std::string requestId = "test_refuse"; + OnAtomicRequestSuccess onSuccess = [](const std::string&) {}; + OnAtomicRequestFailure onFailure = + [&failureCode, &failureMessage](const std::string& appId, int32_t code, const std::string& msg) { + failureCode = code; + failureMessage = msg; + }; + context_->AddCompletionHandlerForAtomicService(requestId, onSuccess, onFailure, "com.test"); + AppExecFwk::ElementName element; + context_->OnRequestFailure(requestId, element, "User refused redirection to app", 0); + EXPECT_EQ(failureCode, 2); + EXPECT_EQ(failureMessage, "User refused redirection"); +} + +/** + * @tc.number: AbilityContextImplThirdTest_OnRequestFailure_0300 + * @tc.name: OnRequestFailure with generic error + * @tc.desc: Test failure callback receives system error info for unknown error message. + */ +HWTEST_F(AbilityContextImplThirdTest, AbilityContextImplThirdTest_OnRequestFailure_0300, + Function | MediumTest | Level1) +{ + int32_t failureCode = -1; + std::string failureMessage; + std::string requestId = "test_error"; + OnAtomicRequestSuccess onSuccess = [](const std::string&) {}; + OnAtomicRequestFailure onFailure = + [&failureCode, &failureMessage](const std::string& appId, int32_t code, const std::string& msg) { + failureCode = code; + failureMessage = msg; + }; + context_->AddCompletionHandlerForAtomicService(requestId, onSuccess, onFailure, "com.test"); + AppExecFwk::ElementName element; + context_->OnRequestFailure(requestId, element, "Some unknown error", 0); + EXPECT_EQ(failureCode, 0); + EXPECT_EQ(failureMessage, "A system error occurred"); +} +} // namespace AppExecFwk +} // namespace OHOS diff --git a/test/unittest/frameworks_kits_ability_ability_runtime_test/mock_ability_manager_client.cpp b/test/unittest/frameworks_kits_ability_ability_runtime_test/mock_ability_manager_client.cpp index 9e3e39dc15..9cdee66650 100644 --- a/test/unittest/frameworks_kits_ability_ability_runtime_test/mock_ability_manager_client.cpp +++ b/test/unittest/frameworks_kits_ability_ability_runtime_test/mock_ability_manager_client.cpp @@ -1132,6 +1132,33 @@ ErrCode AbilityManagerClient::RevokeDelegator(sptr token) return ERR_OK; } +ErrCode AbilityManagerClient::StartSelf(sptr token) +{ + return ERR_OK; +} + +ErrCode AbilityManagerClient::SetOnNewWantSkipScenarios(sptr callerToken, int32_t scenarios) +{ + return ERR_OK; +} + +ErrCode AbilityManagerClient::StartSelfUIAbilityInCurrentProcess(const Want &want, + const std::string &specifiedFlag, const AAFwk::StartOptions &startOptions, + bool hasOptions, sptr callerToken) +{ + return ERR_OK; +} + +ErrCode AbilityManagerClient::NotifyCancelGamePreLaunch(const sptr callerToken) +{ + return ERR_OK; +} + +ErrCode AbilityManagerClient::NotifyCompleteGamePreLaunch(const sptr callerToken) +{ + return ERR_OK; +} + ErrCode GetUserLockedBundleList(int32_t userId, std::unordered_set &userLockedBundleList) { return ERR_OK; diff --git a/test/unittest/frameworks_kits_ability_ability_runtime_test/ui_extension_modal_callback_test.cpp b/test/unittest/frameworks_kits_ability_ability_runtime_test/ui_extension_modal_callback_test.cpp new file mode 100644 index 0000000000..536c04102b --- /dev/null +++ b/test/unittest/frameworks_kits_ability_ability_runtime_test/ui_extension_modal_callback_test.cpp @@ -0,0 +1,324 @@ +/* + * 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 +#include + +#define private public +#define protected public +#include "ui_extension_modal_callback.h" +#include "ui_extension_context.h" +#undef private +#undef protected + +#include "hilog_tag_wrapper.h" +#include "mock_ui_content.h" +#include "want_params.h" +#include "int_wrapper.h" +#include "string_wrapper.h" + +using namespace testing::ext; +using namespace testing; +using namespace OHOS::AppExecFwk; +using namespace OHOS::AbilityRuntime; +using namespace OHOS::Ace; + +namespace OHOS { +namespace AAFwk { + +class UIExtensionModalCallbackTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp() override; + void TearDown() override; +}; + +void UIExtensionModalCallbackTest::SetUpTestCase(void) +{} + +void UIExtensionModalCallbackTest::TearDownTestCase(void) +{} + +void UIExtensionModalCallbackTest::SetUp() +{} + +void UIExtensionModalCallbackTest::TearDown() +{} + +/** + * @tc.name: OnRelease_0100 + * @tc.desc: Test OnRelease when context is nullptr + * @tc.type: FUNC + */ +HWTEST_F(UIExtensionModalCallbackTest, OnRelease_0100, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "OnRelease_0100 start"); + + auto callback = std::make_shared(); + callback->SetSessionId(123); + + // contextWeak_ is nullptr by default + callback->OnRelease(); + + // Should not crash when context is null + EXPECT_TRUE(callback != nullptr); + + TAG_LOGI(AAFwkTag::TEST, "OnRelease_0100 end"); +} + +/** + * @tc.name: OnRelease_0200 + * @tc.desc: Test OnRelease when context and uiContent are valid + * @tc.type: FUNC + */ +HWTEST_F(UIExtensionModalCallbackTest, OnRelease_0200, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "OnRelease_0200 start"); + + auto callback = std::make_shared(); + int32_t sessionId = 456; + callback->SetSessionId(sessionId); + + // Create context + auto context = std::make_shared(); + callback->SetUIExtensionContext(context); + +#ifdef SUPPORT_SCREEN + // Create mock UIContent + MockUIContent *mockUIContent = new MockUIContent(); + EXPECT_CALL(*mockUIContent, CloseModalUIExtension(sessionId)).Times(1).WillOnce(Return()); + callback->SetUIContent(mockUIContent); +#endif + + callback->OnRelease(); + + // Verify the sessionId was erased from context + EXPECT_EQ(context->uiExtensionMap_.find(sessionId), context->uiExtensionMap_.end()); + + TAG_LOGI(AAFwkTag::TEST, "OnRelease_0200 end"); +} + +/** + * @tc.name: OnRelease_0300 + * @tc.desc: Test OnRelease when context is valid but uiContent is nullptr + * @tc.type: FUNC + */ +HWTEST_F(UIExtensionModalCallbackTest, OnRelease_0300, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "OnRelease_0300 start"); + + auto callback = std::make_shared(); + int32_t sessionId = 789; + callback->SetSessionId(sessionId); + + // Create context + auto context = std::make_shared(); + callback->SetUIExtensionContext(context); + + // uiContent is nullptr + callback->SetUIContent(nullptr); + + callback->OnRelease(); + + // Verify the sessionId was erased from context even when uiContent is nullptr + EXPECT_EQ(context->uiExtensionMap_.find(sessionId), context->uiExtensionMap_.end()); + + TAG_LOGI(AAFwkTag::TEST, "OnRelease_0300 end"); +} + +/** + * @tc.name: OnError_0100 + * @tc.desc: Test OnError when context is nullptr + * @tc.type: FUNC + */ +HWTEST_F(UIExtensionModalCallbackTest, OnError_0100, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "OnError_0100 start"); + + auto callback = std::make_shared(); + callback->SetSessionId(321); + + // contextWeak_ is nullptr by default + callback->OnError(); + + // Should not crash when context is null + EXPECT_TRUE(callback != nullptr); + + TAG_LOGI(AAFwkTag::TEST, "OnError_0100 end"); +} + +/** + * @tc.name: OnError_0200 + * @tc.desc: Test OnError when context and uiContent are valid + * @tc.type: FUNC + */ +HWTEST_F(UIExtensionModalCallbackTest, OnError_0200, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "OnError_0200 start"); + + auto callback = std::make_shared(); + int32_t sessionId = 654; + callback->SetSessionId(sessionId); + + // Create context + auto context = std::make_shared(); + callback->SetUIExtensionContext(context); + +#ifdef SUPPORT_SCREEN + // Create mock UIContent + MockUIContent *mockUIContent = new MockUIContent(); + EXPECT_CALL(*mockUIContent, CloseModalUIExtension(sessionId)).Times(1).WillOnce(Return()); + callback->SetUIContent(mockUIContent); +#endif + + callback->OnError(); + + // Verify the sessionId was erased from context + EXPECT_EQ(context->uiExtensionMap_.find(sessionId), context->uiExtensionMap_.end()); + + TAG_LOGI(AAFwkTag::TEST, "OnError_0200 end"); +} + +/** + * @tc.name: OnDestroy_0100 + * @tc.desc: Test OnDestroy when context is nullptr + * @tc.type: FUNC + */ +HWTEST_F(UIExtensionModalCallbackTest, OnDestroy_0100, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "OnDestroy_0100 start"); + + auto callback = std::make_shared(); + callback->SetSessionId(987); + + // contextWeak_ is nullptr by default + callback->OnDestroy(); + + // Should not crash when context is null + EXPECT_TRUE(callback != nullptr); + + TAG_LOGI(AAFwkTag::TEST, "OnDestroy_0100 end"); +} + +/** + * @tc.name: OnDestroy_0200 + * @tc.desc: Test OnDestroy when context is valid + * @tc.type: FUNC + */ +HWTEST_F(UIExtensionModalCallbackTest, OnDestroy_0200, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "OnDestroy_0200 start"); + + auto callback = std::make_shared(); + int32_t sessionId = 111; + callback->SetSessionId(sessionId); + + // Create context + auto context = std::make_shared(); + callback->SetUIExtensionContext(context); + + callback->OnDestroy(); + + // Verify the sessionId was erased from context + EXPECT_EQ(context->uiExtensionMap_.find(sessionId), context->uiExtensionMap_.end()); + + TAG_LOGI(AAFwkTag::TEST, "OnDestroy_0200 end"); +} + +/** + * @tc.name: OnReceive_0100 + * @tc.desc: Test OnReceive when context is nullptr + * @tc.type: FUNC + */ +HWTEST_F(UIExtensionModalCallbackTest, OnReceive_0100, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "OnReceive_0100 start"); + + auto callback = std::make_shared(); + callback->SetSessionId(222); + + // Create WantParams + AAFwk::WantParams data; + + // contextWeak_ is nullptr by default + callback->OnReceive(data); + + // Should not crash when context is null + EXPECT_TRUE(callback != nullptr); + + TAG_LOGI(AAFwkTag::TEST, "OnReceive_0100 end"); +} + +/** + * @tc.name: OnReceive_0200 + * @tc.desc: Test OnReceive when context is valid and data has embeddableServiceExit param + * @tc.type: FUNC + */ +HWTEST_F(UIExtensionModalCallbackTest, OnReceive_0200, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "OnReceive_0200 start"); + + auto callback = std::make_shared(); + int32_t sessionId = 333; + callback->SetSessionId(sessionId); + + // Create context + auto context = std::make_shared(); + callback->SetUIExtensionContext(context); + + // Create WantParams with embeddableServiceExit = true + AAFwk::WantParams data; + data.SetParam("ohos.param.embeddableServiceExit", AAFwk::Integer::Box(1)); + + callback->OnReceive(data); + + // Should call TerminateSelfWithAnimation (no crash expected) + EXPECT_TRUE(context != nullptr); + + TAG_LOGI(AAFwkTag::TEST, "OnReceive_0200 end"); +} + +/** + * @tc.name: OnReceive_0300 + * @tc.desc: Test OnReceive when context is valid but data does not have embeddableServiceExit param + * @tc.type: FUNC + */ +HWTEST_F(UIExtensionModalCallbackTest, OnReceive_0300, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "OnReceive_0300 start"); + + auto callback = std::make_shared(); + int32_t sessionId = 444; + callback->SetSessionId(sessionId); + + // Create context + auto context = std::make_shared(); + callback->SetUIExtensionContext(context); + + // Create WantParams without embeddableServiceExit + AAFwk::WantParams data; + data.SetParam("someOtherParam", AAFwk::String::Box("testValue")); + + callback->OnReceive(data); + + // Should not call TerminateSelfWithAnimation + EXPECT_TRUE(context != nullptr); + + TAG_LOGI(AAFwkTag::TEST, "OnReceive_0300 end"); +} + +} // namespace AAFwk +} // namespace OHOS diff --git a/test/unittest/frameworks_kits_ability_native_test/BUILD.gn b/test/unittest/frameworks_kits_ability_native_test/BUILD.gn index 11dc6da3f0..5e8b437230 100644 --- a/test/unittest/frameworks_kits_ability_native_test/BUILD.gn +++ b/test/unittest/frameworks_kits_ability_native_test/BUILD.gn @@ -752,6 +752,10 @@ ohos_unittest("ui_ability_impl_test") { ] if (ability_runtime_graphics) { + defines = [ + "SUPPORT_GRAPHICS", + "SUPPORT_SCREEN", + ] sources += [ "ui_ability_impl_test.cpp" ] external_deps += [ "form_fwk:fmskit_native", @@ -954,6 +958,7 @@ ohos_unittest("extension_ability_thread_test") { "${ability_runtime_native_path}/ability/native:abilitykit_native", "${ability_runtime_native_path}/ability/native:abilitykit_utils", "${ability_runtime_native_path}/ability/native:extensionkit_native", + "${ability_runtime_native_path}/ability/native:modular_object_extension", "${ability_runtime_native_path}/appkit:app_context", "${ability_runtime_native_path}/appkit:appkit_native", ] @@ -1784,8 +1789,10 @@ ohos_unittest("data_uri_utils_test") { ohos_unittest("extension_impl_test") { module_out_path = module_output_path - include_dirs = [ "${ability_runtime_test_path}/mock/frameworks_kits_ability_native_test/include" ] - + include_dirs = [ + "${ability_runtime_test_path}/mock/frameworks_kits_ability_native_test/include", + "${ability_runtime_path}/utils/global/common/include", + ] sources = [ "extension_impl_test.cpp" ] configs = [ ":module_private_config" ] @@ -1796,6 +1803,7 @@ ohos_unittest("extension_impl_test") { "${ability_runtime_native_path}/ability/native:abilitykit_utils", "${ability_runtime_native_path}/ability/native:extensionkit_native", "${ability_runtime_native_path}/ability/native:form_extension", + "${ability_runtime_native_path}/ability/native:ui_extension", "${ability_runtime_native_path}/appkit:app_context", "${ability_runtime_native_path}/appkit:appkit_native", ] diff --git a/test/unittest/frameworks_kits_ability_native_test/extension_ability_thread_test.cpp b/test/unittest/frameworks_kits_ability_native_test/extension_ability_thread_test.cpp index 6e1767e96e..f82d7ab69b 100644 --- a/test/unittest/frameworks_kits_ability_native_test/extension_ability_thread_test.cpp +++ b/test/unittest/frameworks_kits_ability_native_test/extension_ability_thread_test.cpp @@ -26,6 +26,7 @@ #undef protected #include "mock_ability_thread.h" #include "mock_ability_token.h" +#include "modular_object_worker_manager.h" #include "ohos_application.h" namespace OHOS { @@ -1266,5 +1267,235 @@ HWTEST_F(ExtensionAbilityThreadTest, ExtensionAbilityThread_HandleNativeExtensio thread.HandleNativeExtensionAttach(abilityRecord, abilityName); EXPECT_NE(thread.contentEmbedEventRunner_, nullptr); } + +/** + * @tc.name: GetOrCreateWorkerThread_ShouldReturnNonNullWhenFirstCreated + * @tc.desc: Test creating a new worker thread returns valid handler + */ +HWTEST_F(ExtensionAbilityThreadTest, + GetOrCreateWorkerThread_ShouldReturnNonNullWhenFirstCreated, Function | MediumTest | Level1) +{ + auto &mgr = ModularObjectWorkerManager::GetInstance(); + auto handler = mgr.GetOrCreateWorkerThread("test_bundle_TestExt"); + EXPECT_NE(handler, nullptr); + mgr.ReleaseWorkerThread("test_bundle_TestExt"); +} + +/** + * @tc.name: GetOrCreateWorkerThread_ShouldReturnSameHandlerWhenKeyExists + * @tc.desc: Test reusing existing worker thread returns same handler + */ +HWTEST_F(ExtensionAbilityThreadTest, + GetOrCreateWorkerThread_ShouldReturnSameHandlerWhenKeyExists, Function | MediumTest | Level1) +{ + auto &mgr = ModularObjectWorkerManager::GetInstance(); + auto handler1 = mgr.GetOrCreateWorkerThread("test_reuse_key"); + ASSERT_NE(handler1, nullptr); + auto handler2 = mgr.GetOrCreateWorkerThread("test_reuse_key"); + EXPECT_EQ(handler1, handler2); + mgr.ReleaseWorkerThread("test_reuse_key"); + mgr.ReleaseWorkerThread("test_reuse_key"); +} + +/** + * @tc.name: GetOrCreateWorkerThread_ShouldReturnDifferentHandlerWhenKeyDiffers + * @tc.desc: Test different keys create different handlers + */ +HWTEST_F(ExtensionAbilityThreadTest, + GetOrCreateWorkerThread_ShouldReturnDifferentHandlerWhenKeyDiffers, Function | MediumTest | Level1) +{ + auto &mgr = ModularObjectWorkerManager::GetInstance(); + auto handler1 = mgr.GetOrCreateWorkerThread("test_key_A"); + auto handler2 = mgr.GetOrCreateWorkerThread("test_key_B"); + ASSERT_NE(handler1, nullptr); + ASSERT_NE(handler2, nullptr); + EXPECT_NE(handler1, handler2); + mgr.ReleaseWorkerThread("test_key_A"); + mgr.ReleaseWorkerThread("test_key_B"); +} + +/** + * @tc.name: ReleaseWorkerThread_ShouldRemoveHandlerWhenRefCountReachesZero + * @tc.desc: Test releasing all refs removes handler, next create gives new instance + */ +HWTEST_F(ExtensionAbilityThreadTest, + ReleaseWorkerThread_ShouldRemoveHandlerWhenRefCountReachesZero, Function | MediumTest | Level1) +{ + auto &mgr = ModularObjectWorkerManager::GetInstance(); + auto handler1 = mgr.GetOrCreateWorkerThread("test_release_key"); + ASSERT_NE(handler1, nullptr); + mgr.ReleaseWorkerThread("test_release_key"); + auto handler2 = mgr.GetOrCreateWorkerThread("test_release_key"); + ASSERT_NE(handler2, nullptr); + EXPECT_NE(handler1, handler2); + mgr.ReleaseWorkerThread("test_release_key"); +} + +/** + * @tc.name: ReleaseWorkerThread_ShouldNotCrashWhenKeyNotFound + * @tc.desc: Test releasing non-existent key does not crash + */ +HWTEST_F(ExtensionAbilityThreadTest, + ReleaseWorkerThread_ShouldNotCrashWhenKeyNotFound, Function | MediumTest | Level1) +{ + auto &mgr = ModularObjectWorkerManager::GetInstance(); + mgr.ReleaseWorkerThread("non_existent_key"); + EXPECT_TRUE(true); +} + +/** + * @tc.name: ReleaseWorkerThread_ShouldKeepHandlerWhenPartialRelease + * @tc.desc: Test ref counting: partial release keeps handler, full release removes it + */ +HWTEST_F(ExtensionAbilityThreadTest, + ReleaseWorkerThread_ShouldKeepHandlerWhenPartialRelease, Function | MediumTest | Level1) +{ + auto &mgr = ModularObjectWorkerManager::GetInstance(); + auto handler1 = mgr.GetOrCreateWorkerThread("test_refcount_key"); + ASSERT_NE(handler1, nullptr); + auto handler2 = mgr.GetOrCreateWorkerThread("test_refcount_key"); + EXPECT_EQ(handler1, handler2); + mgr.ReleaseWorkerThread("test_refcount_key"); + auto handler3 = mgr.GetOrCreateWorkerThread("test_refcount_key"); + EXPECT_EQ(handler1, handler3); + mgr.ReleaseWorkerThread("test_refcount_key"); + mgr.ReleaseWorkerThread("test_refcount_key"); +} + +/** + * @tc.name: GenerateInstanceId_ShouldReturnMonotonicallyIncreasingIds + * @tc.desc: Test instance IDs are unique and monotonically increasing + */ +HWTEST_F(ExtensionAbilityThreadTest, + GenerateInstanceId_ShouldReturnMonotonicallyIncreasingIds, Function | MediumTest | Level1) +{ + auto &mgr = ModularObjectWorkerManager::GetInstance(); + uint32_t id1 = mgr.GenerateInstanceId(); + uint32_t id2 = mgr.GenerateInstanceId(); + uint32_t id3 = mgr.GenerateInstanceId(); + EXPECT_LT(id1, id2); + EXPECT_LT(id2, id3); +} + +// Test extension that overrides GetAbilityHandler to provide a custom handler +class TestHandlerExtension : public AbilityRuntime::Extension { +public: + TestHandlerExtension() = default; + ~TestHandlerExtension() override = default; + + std::shared_ptr GetAbilityHandler( + const std::shared_ptr &abilityInfo) override + { + if (abilityInfo == nullptr) { + return nullptr; + } + auto runner = AppExecFwk::EventRunner::Create("TestHandlerThread"); + return std::make_shared(runner); + } +}; + +/** + * @tc.name: HandleAttach_ShouldUseExtensionHandlerWhenOverrideReturnsNonNull + * @tc.desc: Test HandleAttach uses extension-provided handler via GetAbilityHandler + */ +HWTEST_F(ExtensionAbilityThreadTest, + HandleAttach_ShouldUseExtensionHandlerWhenOverrideReturnsNonNull, Function | MediumTest | Level1) +{ + AppExecFwk::AbilityLoader::GetInstance().RegisterExtension("TestHandlerExtension", + [](const std::string &) -> AbilityRuntime::Extension * { + return new (std::nothrow) TestHandlerExtension(); + }); + + sptr thread = sptr::MakeSptr(); + ASSERT_NE(thread, nullptr); + + std::shared_ptr abilityInfo = std::make_shared(); + abilityInfo->name = "TestHandlerExtension"; + abilityInfo->type = AbilityType::EXTENSION; + abilityInfo->isNativeAbility = true; + abilityInfo->extensionAbilityType = AppExecFwk::ExtensionAbilityType::SERVICE; + + sptr token = sptr(new (std::nothrow) MockAbilityToken()); + ASSERT_NE(token, nullptr); + + std::shared_ptr application = std::make_shared(); + auto abilityRecord = std::make_shared(abilityInfo, token, nullptr, 0); + std::shared_ptr mainRunner = EventRunner::Create(abilityInfo->name); + + thread->Attach(application, abilityRecord, mainRunner, nullptr); + EXPECT_NE(thread->abilityHandler_, nullptr); +} + +/** + * @tc.name: HandleAttach_ShouldFallbackToDefaultWhenOverrideReturnsNull + * @tc.desc: Test HandleAttach falls back to default when GetAbilityHandler returns nullptr + */ +HWTEST_F(ExtensionAbilityThreadTest, + HandleAttach_ShouldFallbackToDefaultWhenOverrideReturnsNull, Function | MediumTest | Level1) +{ + AppExecFwk::AbilityLoader::GetInstance().RegisterExtension("DefaultHandlerExtension", + [](const std::string &) -> AbilityRuntime::Extension * { + return new (std::nothrow) AbilityRuntime::Extension(); + }); + + sptr thread = sptr::MakeSptr(); + ASSERT_NE(thread, nullptr); + + std::shared_ptr abilityInfo = std::make_shared(); + abilityInfo->name = "DefaultHandlerExtension"; + abilityInfo->type = AbilityType::EXTENSION; + abilityInfo->isNativeAbility = true; + abilityInfo->extensionAbilityType = AppExecFwk::ExtensionAbilityType::SERVICE; + + sptr token = sptr(new (std::nothrow) MockAbilityToken()); + ASSERT_NE(token, nullptr); + + std::shared_ptr application = std::make_shared(); + auto abilityRecord = std::make_shared(abilityInfo, token, nullptr, 0); + std::shared_ptr mainRunner = EventRunner::Create(abilityInfo->name); + + thread->Attach(application, abilityRecord, mainRunner, nullptr); + EXPECT_NE(thread->abilityHandler_, nullptr); +} + +/** + * @tc.number: ExtensionAbilityThread_CreateModalUIExtension_0100 + * @tc.name: CreateModalUIExtension + * @tc.desc: Test CreateModalUIExtension function when extensionImpl_ is nullptr + */ +HWTEST_F(ExtensionAbilityThreadTest, ExtensionAbilityThread_CreateModalUIExtension_0100, + Function | MediumTest | Level1) +{ + GTEST_LOG_(INFO) << "ExtensionAbilityThread_CreateModalUIExtension_0100 start"; + AbilityRuntime::ExtensionAbilityThread *extensionabilitythread = + new (std::nothrow) AbilityRuntime::ExtensionAbilityThread(); + EXPECT_NE(extensionabilitythread, nullptr); + EXPECT_EQ(extensionabilitythread->extensionImpl_, nullptr); + AAFwk::Want want; + int result = extensionabilitythread->CreateModalUIExtension(want); + EXPECT_EQ(result, ERR_INVALID_VALUE); + GTEST_LOG_(INFO) << "ExtensionAbilityThread_CreateModalUIExtension_0100 end"; +} + +/** + * @tc.number: ExtensionAbilityThread_CreateModalUIExtension_0200 + * @tc.name: CreateModalUIExtension + * @tc.desc: Test CreateModalUIExtension function when extensionImpl_ is not nullptr + */ +HWTEST_F(ExtensionAbilityThreadTest, ExtensionAbilityThread_CreateModalUIExtension_0200, + Function | MediumTest | Level1) +{ + GTEST_LOG_(INFO) << "ExtensionAbilityThread_CreateModalUIExtension_0200 start"; + AbilityRuntime::ExtensionAbilityThread *extensionabilitythread = + new (std::nothrow) AbilityRuntime::ExtensionAbilityThread(); + EXPECT_NE(extensionabilitythread, nullptr); + extensionabilitythread->extensionImpl_ = std::make_shared(); + EXPECT_NE(extensionabilitythread->extensionImpl_, nullptr); + AAFwk::Want want; + int result = extensionabilitythread->CreateModalUIExtension(want); + // ExtensionImpl::CreateModalUIExtension returns ERR_INVALID_VALUE when extension is not UIExtension + EXPECT_EQ(result, ERR_INVALID_VALUE); + GTEST_LOG_(INFO) << "ExtensionAbilityThread_CreateModalUIExtension_0200 end"; +} } // namespace AbilityRuntime } // namespace OHOS \ No newline at end of file diff --git a/test/unittest/frameworks_kits_ability_native_test/extension_impl_test.cpp b/test/unittest/frameworks_kits_ability_native_test/extension_impl_test.cpp index fc60ad3ac8..9c7bbc27b0 100644 --- a/test/unittest/frameworks_kits_ability_native_test/extension_impl_test.cpp +++ b/test/unittest/frameworks_kits_ability_native_test/extension_impl_test.cpp @@ -15,6 +15,8 @@ #include +#include "errors.h" + #define private public #define protected public #include "ability_handler.h" @@ -26,6 +28,10 @@ #include "event_runner.h" #include "mock_ability_token.h" #include "ohos_application.h" +#include "ui_extension_context.h" +#include "ui_extension.h" +#include "ui_extension_wrapper.h" +#include "want.h" namespace OHOS { namespace AppExecFwk { @@ -757,5 +763,159 @@ HWTEST_F(ExtensionImplTest, AaFwk_ExtensionImpl_3400, TestSize.Level1) impl->ScheduleAbilityRequestSuccess(requestId, element); GTEST_LOG_(INFO) << "AaFwk_ExtensionImpl_3400 end"; } + +/** + * @tc.number: AaFwk_ExtensionImpl_CreateModalUIExtension_0100 + * @tc.name: CreateModalUIExtension + * @tc.desc: extensionType_ is not UI extension type, return ERR_INVALID_VALUE. + */ +HWTEST_F(ExtensionImplTest, AaFwk_ExtensionImpl_CreateModalUIExtension_0100, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "AaFwk_ExtensionImpl_CreateModalUIExtension_0100 start"; + auto impl = std::make_shared(); + impl->extensionType_ = AppExecFwk::ExtensionAbilityType::SERVICE; + impl->extension_ = nullptr; + AAFwk::Want want; + int ret = impl->CreateModalUIExtension(want); + EXPECT_EQ(ret, ERR_INVALID_VALUE); + GTEST_LOG_(INFO) << "AaFwk_ExtensionImpl_CreateModalUIExtension_0100 end"; +} + +/** + * @tc.number: AaFwk_ExtensionImpl_CreateModalUIExtension_0200 + * @tc.name: CreateModalUIExtension + * @tc.desc: extension_ is nullptr, return ERR_INVALID_VALUE. + */ +HWTEST_F(ExtensionImplTest, AaFwk_ExtensionImpl_CreateModalUIExtension_0200, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "AaFwk_ExtensionImpl_CreateModalUIExtension_0200 start"; + auto impl = std::make_shared(); + impl->extensionType_ = AppExecFwk::ExtensionAbilityType::UI; + impl->extension_ = nullptr; + AAFwk::Want want; + int ret = impl->CreateModalUIExtension(want); + EXPECT_EQ(ret, ERR_INVALID_VALUE); + GTEST_LOG_(INFO) << "AaFwk_ExtensionImpl_CreateModalUIExtension_0200 end"; +} + +/** + * @tc.number: AaFwk_ExtensionImpl_CreateModalUIExtension_0400 + * @tc.name: CreateModalUIExtension + * @tc.desc: UIExtension Init'd but handler has no EventRunner, PostTask fails, return ERR_INVALID_VALUE. + */ +HWTEST_F(ExtensionImplTest, AaFwk_ExtensionImpl_CreateModalUIExtension_0400, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "AaFwk_ExtensionImpl_CreateModalUIExtension_0400 start"; + auto impl = std::make_shared(); + impl->extensionType_ = AppExecFwk::ExtensionAbilityType::UI; + + auto application = std::make_shared(); + std::shared_ptr info = std::make_shared(); + sptr token = new AppExecFwk::MockAbilityToken(); + auto record = std::make_shared(info, token, nullptr, 0); + std::shared_ptr runner; + auto handler = std::make_shared(runner); + + auto uiExtension = std::make_shared(); + uiExtension->Init(record, application, handler, token); + impl->extension_ = uiExtension; + + AAFwk::Want want; + int ret = impl->CreateModalUIExtension(want); + EXPECT_EQ(ret, -1); + GTEST_LOG_(INFO) << "AaFwk_ExtensionImpl_CreateModalUIExtension_0400 end"; +} + +/** + * @tc.number: AaFwk_ExtensionImpl_CreateModalUIExtension_0500 + * @tc.name: CreateModalUIExtension + * @tc.desc: UIExtension::CreateModalUIExtension call failed, return error code. + */ +HWTEST_F(ExtensionImplTest, AaFwk_ExtensionImpl_CreateModalUIExtension_0500, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "AaFwk_ExtensionImpl_CreateModalUIExtension_0500 start"; + auto impl = std::make_shared(); + impl->extensionType_ = AppExecFwk::ExtensionAbilityType::UI; + + auto uiExtension = std::make_shared(); + impl->extension_ = uiExtension; + + AAFwk::Want want; + int ret = impl->CreateModalUIExtension(want); + EXPECT_EQ(ret, -1); + GTEST_LOG_(INFO) << "AaFwk_ExtensionImpl_CreateModalUIExtension_0500 end"; +} +/** + * @tc.number: AaFwk_ExtensionImpl_CreateModalUIExtension_0600 + * @tc.name: CreateModalUIExtension + * @tc.desc: extensionType_ is UNSPECIFIED (not in UIExtension set), return ERR_INVALID_VALUE. + */ +HWTEST_F(ExtensionImplTest, AaFwk_ExtensionImpl_CreateModalUIExtension_0600, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "AaFwk_ExtensionImpl_CreateModalUIExtension_0600 start"; + auto impl = std::make_shared(); + impl->extensionType_ = AppExecFwk::ExtensionAbilityType::UNSPECIFIED; + auto extension = std::make_shared(); + impl->extension_ = extension; + AAFwk::Want want; + int ret = impl->CreateModalUIExtension(want); + EXPECT_EQ(ret, ERR_INVALID_VALUE); + GTEST_LOG_(INFO) << "AaFwk_ExtensionImpl_CreateModalUIExtension_0600 end"; +} + +/** + * @tc.number: AaFwk_ExtensionImpl_CreateModalUIExtension_0700 + * @tc.name: CreateModalUIExtension + * @tc.desc: extensionType_ is FORM (not in UIExtension set), extension_ is UIExtension, return ERR_INVALID_VALUE. + */ +HWTEST_F(ExtensionImplTest, AaFwk_ExtensionImpl_CreateModalUIExtension_0700, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "AaFwk_ExtensionImpl_CreateModalUIExtension_0700 start"; + auto impl = std::make_shared(); + impl->extensionType_ = AppExecFwk::ExtensionAbilityType::FORM; + auto extension = std::make_shared(); + impl->extension_ = extension; + AAFwk::Want want; + int ret = impl->CreateModalUIExtension(want); + EXPECT_EQ(ret, ERR_INVALID_VALUE); + GTEST_LOG_(INFO) << "AaFwk_ExtensionImpl_CreateModalUIExtension_0700 end"; +} + +/** + * @tc.number: AaFwk_ExtensionImpl_CreateModalUIExtension_0900 + * @tc.name: CreateModalUIExtension + * @tc.desc: extensionType_ is EMBEDDED_UI (in UIExtension set), extension_ is nullptr, return ERR_INVALID_VALUE. + * This covers line 551-553 with a different UIExtension subtype. + */ +HWTEST_F(ExtensionImplTest, AaFwk_ExtensionImpl_CreateModalUIExtension_0900, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "AaFwk_ExtensionImpl_CreateModalUIExtension_0900 start"; + auto impl = std::make_shared(); + impl->extensionType_ = AppExecFwk::ExtensionAbilityType::EMBEDDED_UI; + impl->extension_ = nullptr; + AAFwk::Want want; + int ret = impl->CreateModalUIExtension(want); + EXPECT_EQ(ret, ERR_INVALID_VALUE); + GTEST_LOG_(INFO) << "AaFwk_ExtensionImpl_CreateModalUIExtension_0900 end"; +} + +/** + * @tc.number: AaFwk_ExtensionImpl_CreateModalUIExtension_1000 + * @tc.name: CreateModalUIExtension + * @tc.desc: extensionType_ is SHARE (in UIExtension set), extension_ is nullptr, return ERR_INVALID_VALUE. + * This covers line 551-553 with SHARE subtype. + */ +HWTEST_F(ExtensionImplTest, AaFwk_ExtensionImpl_CreateModalUIExtension_1000, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "AaFwk_ExtensionImpl_CreateModalUIExtension_1000 start"; + auto impl = std::make_shared(); + impl->extensionType_ = AppExecFwk::ExtensionAbilityType::SHARE; + impl->extension_ = nullptr; + AAFwk::Want want; + int ret = impl->CreateModalUIExtension(want); + EXPECT_EQ(ret, ERR_INVALID_VALUE); + GTEST_LOG_(INFO) << "AaFwk_ExtensionImpl_CreateModalUIExtension_1000 end"; +} + } // namespace AppExecFwk } // namespace OHOS diff --git a/test/unittest/frameworks_kits_ability_native_test/extension_test.cpp b/test/unittest/frameworks_kits_ability_native_test/extension_test.cpp index 70b33d0229..23d09fd2f9 100644 --- a/test/unittest/frameworks_kits_ability_native_test/extension_test.cpp +++ b/test/unittest/frameworks_kits_ability_native_test/extension_test.cpp @@ -505,5 +505,45 @@ HWTEST_F(ExtensionTest, AaFwk_Extension_2800, Function | MediumTest | Level1) EXPECT_NE(extension_, nullptr); GTEST_LOG_(INFO) << "AaFwk_Extension_2800 end"; } +/** + * @tc.name: GetAbilityHandler_ShouldReturnNullptrWhenCalledOnBaseExtension + * @tc.desc: Base Extension GetAbilityHandler returns nullptr by default. + */ +HWTEST_F(ExtensionTest, GetAbilityHandler_ShouldReturnNullptrWhenCalledOnBaseExtension, + Function | MediumTest | Level1) +{ + auto info = std::make_shared(); + auto handler = extension_->GetAbilityHandler(info); + EXPECT_EQ(handler, nullptr); +} + +/** + * @tc.name: GetAbilityHandler_ShouldReturnNullptrWhenAbilityInfoIsNull + * @tc.desc: Base Extension GetAbilityHandler returns nullptr when abilityInfo is null. + */ +HWTEST_F(ExtensionTest, GetAbilityHandler_ShouldReturnNullptrWhenAbilityInfoIsNull, + Function | MediumTest | Level1) +{ + auto handler = extension_->GetAbilityHandler(nullptr); + EXPECT_EQ(handler, nullptr); +} + +/** + * @tc.number: AaFwk_Extension_2900 + * @tc.name: OnConnect + * @tc.desc: Incoming want with callback info, verify OnConnect with async callback successfully. + */ +HWTEST_F(ExtensionTest, AaFwk_Extension_2900, Function | MediumTest | Level1) +{ + GTEST_LOG_(INFO) << "AaFwk_Extension_2900 start"; + Want want; + want.SetElementName("DemoDeviceId", "DemoBundleName", "DemoAbilityName"); + AppExecFwk::AbilityTransactionCallbackInfo> *callbackInfo = nullptr; + bool isAsyncCallback = true; + auto remoteObject = extension_->OnConnect(want, callbackInfo, isAsyncCallback); + EXPECT_TRUE(remoteObject == nullptr); + EXPECT_FALSE(isAsyncCallback); + GTEST_LOG_(INFO) << "AaFwk_Extension_2900 end"; +} } // namespace AppExecFwk } // namespace OHOS diff --git a/test/unittest/frameworks_kits_ability_native_test/ui_ability_impl_test.cpp b/test/unittest/frameworks_kits_ability_native_test/ui_ability_impl_test.cpp index 17f0852f72..146cfc7d05 100644 --- a/test/unittest/frameworks_kits_ability_native_test/ui_ability_impl_test.cpp +++ b/test/unittest/frameworks_kits_ability_native_test/ui_ability_impl_test.cpp @@ -2632,5 +2632,167 @@ HWTEST_F(UIAbilityImplTest, AbilityRuntime_SetNativeModuleMetaData_005, TestSize GTEST_LOG_(INFO) << "AbilityRuntime_SetNativeModuleMetaData_005 end"; } +/* + * Feature: UIAbilityImpl + * Function: ExecuteInsightIntentMoveToForeground + * SubFunction: NA + * FunctionPoints: SilentForeground with PRE_FOREGROUND native state + * EnvConditions: NA + * CaseDescription: When localNativeState is INIT_PRE_FOREGROUND, ExecuteInsightIntentMoveToForeground + * sets silentForeground to true and transitions state to HALF_FOREGROUND. + */ +HWTEST_F(UIAbilityImplTest, AbilityRuntime_ExecuteInsightIntentMoveToForeground_PreForeground_0100, TestSize.Level1) +{ + auto abilityImpl = std::make_shared(); + std::shared_ptr pMocKUIAbility = std::make_shared(); + abilityImpl->ability_ = pMocKUIAbility; + abilityImpl->localNativeState_ = AbilityRuntime::LocalNativeState::INIT_PRE_FOREGROUND; + + Want want; + auto executeParam = std::make_shared(); + auto callback = std::make_unique(); + + abilityImpl->ExecuteInsightIntentMoveToForeground(want, executeParam, std::move(callback)); + + EXPECT_EQ(abilityImpl->localNativeState_, AbilityRuntime::LocalNativeState::HALF_FOREGROUND); + EXPECT_FALSE(abilityImpl->ability_->CheckIsSilentForeground()); +} + +/* + * Feature: UIAbilityImpl + * Function: ExecuteInsightIntentMoveToForeground + * SubFunction: NA + * FunctionPoints: SilentForeground preserved when not in PRE_FOREGROUND state + * EnvConditions: NA + * CaseDescription: When localNativeState is NONE, ExecuteInsightIntentMoveToForeground does not + * modify silentForeground state. + */ +HWTEST_F(UIAbilityImplTest, AbilityRuntime_ExecuteInsightIntentMoveToForeground_PreForeground_0300, TestSize.Level1) +{ + auto abilityImpl = std::make_shared(); + std::shared_ptr pMocKUIAbility = std::make_shared(); + + abilityImpl->ability_ = pMocKUIAbility; + + EXPECT_EQ(abilityImpl->localNativeState_, AbilityRuntime::LocalNativeState::NONE); + + Want want; + auto executeParam = std::make_shared(); + auto callback = std::make_unique(); + ASSERT_NE(callback, nullptr); + + abilityImpl->ExecuteInsightIntentMoveToForeground(want, executeParam, std::move(callback)); + + EXPECT_FALSE(abilityImpl->ability_->CheckIsSilentForeground()); +} + +/* + * Feature: UIAbilityImpl + * Function: ExecuteInsightIntentMoveToForeground + * SubFunction: NA + * FunctionPoints: Null ability_ early return + * EnvConditions: NA + * CaseDescription: When ability_ is nullptr, ExecuteInsightIntentMoveToForeground returns early + * without crash. + */ +HWTEST_F(UIAbilityImplTest, AbilityRuntime_ExecuteInsightIntentMoveToForeground_PreForeground_0400, TestSize.Level1) +{ + auto abilityImpl = std::make_shared(); + EXPECT_EQ(abilityImpl->ability_, nullptr); + + Want want; + auto executeParam = std::make_shared(); + auto callback = std::make_unique(); + + abilityImpl->ExecuteInsightIntentMoveToForeground(want, executeParam, std::move(callback)); + + EXPECT_EQ(abilityImpl->ability_, nullptr); +} +/** + * @tc.name: AbilityRuntime_Foreground_AbilityNullptr_0100 + * @tc.desc: Test Foreground when ability_ is nullptr, should return early without crash. + */ +HWTEST_F(UIAbilityImplTest, AbilityRuntime_Foreground_AbilityNullptr_0100, TestSize.Level1) +{ + auto impl = std::make_shared(); + EXPECT_EQ(impl->GetUIAbility(), nullptr); + Want want; + impl->Foreground(want); + EXPECT_FALSE(impl->notifyForegroundByWindow_); +} + +/** + * @tc.name: AbilityRuntime_Foreground_AbilityInfoNullptr_0100 + * @tc.desc: Test Foreground when ability exists but GetAbilityInfo() returns nullptr. + */ +HWTEST_F(UIAbilityImplTest, AbilityRuntime_Foreground_AbilityInfoNullptr_0100, TestSize.Level1) +{ + auto impl = std::make_shared(); + auto ability = std::make_shared(); + ability->useMockAbilityInfo_ = true; + impl->ability_ = ability; + Want want; + impl->Foreground(want); + EXPECT_FALSE(impl->notifyForegroundByWindow_); +} + +/** + * @tc.name: AbilityRuntime_Foreground_InitPreForeground_0100 + * @tc.desc: Test Foreground when localNativeState_ is INIT_PRE_FOREGROUND, + * should set HALF_FOREGROUND, call OnForeground silently, and set notifyForegroundByWindow_. + */ +HWTEST_F(UIAbilityImplTest, AbilityRuntime_Foreground_InitPreForeground_0100, TestSize.Level1) +{ + auto impl = std::make_shared(); + auto ability = std::make_shared(); + ability->useMockAbilityInfo_ = true; + ability->mockAbilityInfo_ = std::make_shared(); + impl->ability_ = ability; + impl->localNativeState_ = LocalNativeState::INIT_PRE_FOREGROUND; + Want want; + impl->Foreground(want); + EXPECT_TRUE(impl->notifyForegroundByWindow_); + EXPECT_EQ(impl->localNativeState_, LocalNativeState::HALF_FOREGROUND); +} + +/** + * @tc.name: AbilityRuntime_Foreground_Normal_Foreground_0100 + * @tc.desc: Test Foreground normal path with silent foreground, + * should set notifyForegroundByWindow_ to true. + */ +HWTEST_F(UIAbilityImplTest, AbilityRuntime_Foreground_Normal_Foreground_0100, TestSize.Level1) +{ + auto impl = std::make_shared(); + auto ability = std::make_shared(); + ability->useMockAbilityInfo_ = true; + ability->mockAbilityInfo_ = std::make_shared(); + ability->SetIsSilentForeground(true); + impl->ability_ = ability; + impl->localNativeState_ = LocalNativeState::HALF_FOREGROUND; + Want want; + impl->Foreground(want); + EXPECT_TRUE(impl->notifyForegroundByWindow_); + EXPECT_EQ(impl->localNativeState_, LocalNativeState::NONE); +} + +/** + * @tc.name: AbilityRuntime_Foreground_Normal_Foreground_0200 + * @tc.desc: Test Foreground normal path with non-silent foreground, + * should set notifyForegroundByAbility_ to true. + */ +HWTEST_F(UIAbilityImplTest, AbilityRuntime_Foreground_Normal_Foreground_0200, TestSize.Level1) +{ + auto impl = std::make_shared(); + auto ability = std::make_shared(); + ability->useMockAbilityInfo_ = true; + ability->mockAbilityInfo_ = std::make_shared(); + ability->SetIsSilentForeground(false); + impl->ability_ = ability; + impl->localNativeState_ = LocalNativeState::HALF_FOREGROUND; + Want want; + impl->Foreground(want); + EXPECT_TRUE(impl->notifyForegroundByAbility_); + EXPECT_EQ(impl->localNativeState_, LocalNativeState::NONE); +} } // namespace AppExecFwk } // namespace OHOS diff --git a/test/unittest/frameworks_kits_ability_native_test/ui_ability_thread_test.cpp b/test/unittest/frameworks_kits_ability_native_test/ui_ability_thread_test.cpp index a7a7fe9ac3..44ea1c6fe5 100644 --- a/test/unittest/frameworks_kits_ability_native_test/ui_ability_thread_test.cpp +++ b/test/unittest/frameworks_kits_ability_native_test/ui_ability_thread_test.cpp @@ -1389,10 +1389,9 @@ HWTEST_F(UIAbilityThreadTest, AbilityRuntime_ScheduleAbilityRequestSuccess_0200, HWTEST_F(UIAbilityThreadTest, AbilityRuntime_InitNativeThread_0100, Function | MediumTest | Level1) { GTEST_LOG_(INFO) << "AbilityRuntime_InitNativeThread_0100 start"; - AbilityRuntime::UIAbilityThread *abilitythread = new (std::nothrow) AbilityRuntime::UIAbilityThread(); - EXPECT_NE(abilitythread, nullptr); + auto abilityThread = std::make_shared(); std::shared_ptr abilityInfo = nullptr; - auto ret = abilitythread->InitNativeThread(abilityInfo); + auto ret = abilityThread->InitNativeThread(abilityInfo); EXPECT_EQ(ret, false); GTEST_LOG_(INFO) << "AbilityRuntime_InitNativeThread_0100 end"; } @@ -1405,12 +1404,11 @@ HWTEST_F(UIAbilityThreadTest, AbilityRuntime_InitNativeThread_0100, Function | M HWTEST_F(UIAbilityThreadTest, AbilityRuntime_InitNativeThread_0200, Function | MediumTest | Level1) { GTEST_LOG_(INFO) << "AbilityRuntime_InitNativeThread_0200 start"; - AbilityRuntime::UIAbilityThread *abilitythread = new (std::nothrow) AbilityRuntime::UIAbilityThread(); - EXPECT_NE(abilitythread, nullptr); + auto abilityThread = std::make_shared(); std::shared_ptr abilityInfo = std::make_shared(); abilityInfo->name = "MockUIAbility"; abilityInfo->type = AbilityType::PAGE; - auto ret = abilitythread->InitNativeThread(abilityInfo); + auto ret = abilityThread->InitNativeThread(abilityInfo); EXPECT_TRUE(ret); GTEST_LOG_(INFO) << "AbilityRuntime_InitNativeThread_0200 end"; } @@ -1423,8 +1421,7 @@ HWTEST_F(UIAbilityThreadTest, AbilityRuntime_InitNativeThread_0200, Function | M HWTEST_F(UIAbilityThreadTest, AbilityRuntime_InitNativeThread_0300, Function | MediumTest | Level1) { GTEST_LOG_(INFO) << "AbilityRuntime_InitNativeThread_0300 start"; - AbilityRuntime::UIAbilityThread *abilitythread = new (std::nothrow) AbilityRuntime::UIAbilityThread(); - EXPECT_NE(abilitythread, nullptr); + auto abilityThread = std::make_shared(); std::shared_ptr abilityInfo = std::make_shared(); abilityInfo->name = "MockUIAbility"; abilityInfo->type = AbilityType::PAGE; @@ -1432,8 +1429,8 @@ HWTEST_F(UIAbilityThreadTest, AbilityRuntime_InitNativeThread_0300, Function | M std::shared_ptr application = std::make_shared(); auto abilityRecord = std::make_shared(abilityInfo, token, nullptr, 0); std::shared_ptr mainRunner = EventRunner::Create(abilityInfo->name); - abilitythread->Attach(application, abilityRecord, mainRunner, nullptr); - auto ret = abilitythread->InitNativeThread(abilityInfo); + abilityThread->Attach(application, abilityRecord, mainRunner, nullptr); + auto ret = abilityThread->InitNativeThread(abilityInfo); EXPECT_EQ(ret, true); GTEST_LOG_(INFO) << "AbilityRuntime_InitNativeThread_0300 end"; } @@ -1446,8 +1443,7 @@ HWTEST_F(UIAbilityThreadTest, AbilityRuntime_InitNativeThread_0300, Function | M HWTEST_F(UIAbilityThreadTest, AbilityRuntime_InitNativeThread_0400, Function | MediumTest | Level1) { GTEST_LOG_(INFO) << "AbilityRuntime_InitNativeThread_0400 start"; - AbilityRuntime::UIAbilityThread *abilitythread = new (std::nothrow) AbilityRuntime::UIAbilityThread(); - EXPECT_NE(abilitythread, nullptr); + auto abilityThread = std::make_shared(); std::shared_ptr abilityInfo = std::make_shared(); abilityInfo->name = "MockUIAbility"; abilityInfo->type = AbilityType::PAGE; @@ -1455,10 +1451,10 @@ HWTEST_F(UIAbilityThreadTest, AbilityRuntime_InitNativeThread_0400, Function | M std::shared_ptr application = std::make_shared(); auto abilityRecord = std::make_shared(abilityInfo, token, nullptr, 0); std::shared_ptr mainRunner = EventRunner::Create(abilityInfo->name); - abilitythread->Attach(application, abilityRecord, mainRunner, nullptr); - abilitythread->currentAbility_ = nullptr; - abilitythread->abilityImpl_ = nullptr; - auto ret = abilitythread->InitNativeThread(abilityInfo); + abilityThread->Attach(application, abilityRecord, mainRunner, nullptr); + abilityThread->currentAbility_ = nullptr; + abilityThread->abilityImpl_ = nullptr; + auto ret = abilityThread->InitNativeThread(abilityInfo); EXPECT_EQ(ret, true); GTEST_LOG_(INFO) << "AbilityRuntime_InitNativeThread_0400 end"; } @@ -1471,8 +1467,7 @@ HWTEST_F(UIAbilityThreadTest, AbilityRuntime_InitNativeThread_0400, Function | M HWTEST_F(UIAbilityThreadTest, AbilityRuntime_InitNativeThread_0500, Function | MediumTest | Level1) { GTEST_LOG_(INFO) << "AbilityRuntime_InitNativeThread_0500 start"; - AbilityRuntime::UIAbilityThread *abilitythread = new (std::nothrow) AbilityRuntime::UIAbilityThread(); - EXPECT_NE(abilitythread, nullptr); + auto abilityThread = std::make_shared(); std::shared_ptr abilityInfo = std::make_shared(); abilityInfo->name = "MockUIAbility"; abilityInfo->type = AbilityType::PAGE; @@ -1480,16 +1475,30 @@ HWTEST_F(UIAbilityThreadTest, AbilityRuntime_InitNativeThread_0500, Function | M std::shared_ptr application = std::make_shared(); auto abilityRecord = std::make_shared(abilityInfo, token, nullptr, 0); std::shared_ptr mainRunner = EventRunner::Create(abilityInfo->name); - abilitythread->Attach(application, abilityRecord, mainRunner, nullptr); - abilitythread->currentAbility_ = std::make_shared(); - abilitythread->abilityImpl_ = std::make_shared(); - EXPECT_NE(abilitythread->currentAbility_, nullptr); - EXPECT_NE(abilitythread->abilityImpl_, nullptr); - auto ret = abilitythread->InitNativeThread(abilityInfo); + abilityThread->Attach(application, abilityRecord, mainRunner, nullptr); + abilityThread->currentAbility_ = std::make_shared(); + abilityThread->abilityImpl_ = std::make_shared(); + EXPECT_NE(abilityThread->currentAbility_, nullptr); + EXPECT_NE(abilityThread->abilityImpl_, nullptr); + auto ret = abilityThread->InitNativeThread(abilityInfo); EXPECT_EQ(ret, true); GTEST_LOG_(INFO) << "AbilityRuntime_InitNativeThread_0500 end"; } +/** + * @tc.number: AbilityRuntime_AttachInner_0100 + * @tc.name: AttachInner + * @tc.desc: Test AttachInner function with valid parameters + */ +HWTEST_F(UIAbilityThreadTest, AbilityRuntime_AttachInner_0100, Function | MediumTest | Level1) +{ + auto abilityThread = std::make_shared(); + abilityThread->abilityImpl_ = nullptr; + auto abilityRecord = std::make_shared(nullptr, nullptr, nullptr, 0); + abilityThread->AttachInner(nullptr, abilityRecord, nullptr); + EXPECT_NE(abilityThread->abilityImpl_, nullptr); +} + #ifdef SUPPORT_SCREEN /** * @tc.number: AbilityRuntime_GetUIAbility_0100 diff --git a/test/unittest/frameworks_kits_appkit_native_test/dump_runtime_helper_test.cpp b/test/unittest/frameworks_kits_appkit_native_test/dump_runtime_helper_test.cpp index 8cb3143987..968b9209b0 100644 --- a/test/unittest/frameworks_kits_appkit_native_test/dump_runtime_helper_test.cpp +++ b/test/unittest/frameworks_kits_appkit_native_test/dump_runtime_helper_test.cpp @@ -168,6 +168,47 @@ HWTEST_F(DumpRuntimeHelperTest, DumpMem_0200, Function | MediumTest | Level1) GTEST_LOG_(INFO) << "DumpRuntimeHelperTest DumpMem_0200 end"; } +/** + * @tc.number: DumpMem_0300 + * @tc.name: DumpMem + * @tc.desc: Test whether DumpMem and are called normally. + */ +HWTEST_F(DumpRuntimeHelperTest, DumpMem_0300, Function | MediumTest | Level1) +{ + GTEST_LOG_(INFO) << "DumpRuntimeHelperTest DumpMem_0300 start"; + std::shared_ptr application = std::make_shared(); + EXPECT_NE(application, nullptr); + OHOS::AppExecFwk::MemDumpInfo info; + info.pid = 1; + info.needLeakobj = false; + info.dumpType = MemDumpType::JSVM; + std::string dumpResult; + auto helper = std::make_shared(application); + helper->DumpMem(info, dumpResult); + GTEST_LOG_(INFO) << "DumpRuntimeHelperTest DumpMem_0300 end"; +} + +/** + * @tc.number: DumpMem_0400 + * @tc.name: DumpMem + * @tc.desc: Test whether DumpMem and are called normally. + */ +HWTEST_F(DumpRuntimeHelperTest, DumpMem_0400, Function | MediumTest | Level1) +{ + GTEST_LOG_(INFO) << "DumpRuntimeHelperTest DumpMem_0400 start"; + std::shared_ptr application = std::make_shared(); + EXPECT_NE(application, nullptr); + OHOS::AppExecFwk::MemDumpInfo info; + info.pid = 1; + info.needLeakobj = false; + info.dumpType = MemDumpType::ARKWEB_JS; + info.renderPid = 1234; + std::string dumpResult; + auto helper = std::make_shared(application); + helper->DumpMem(info, dumpResult); + GTEST_LOG_(INFO) << "DumpRuntimeHelperTest DumpMem_0400 end"; +} + /** * @tc.number: CheckOomdumpSwitch_0100 * @tc.name: CheckOomdumpSwitch diff --git a/test/unittest/implicit_start_processor_test/implicit_start_processor_test.cpp b/test/unittest/implicit_start_processor_test/implicit_start_processor_test.cpp index 8be07adbac..b91ce7ea40 100644 --- a/test/unittest/implicit_start_processor_test/implicit_start_processor_test.cpp +++ b/test/unittest/implicit_start_processor_test/implicit_start_processor_test.cpp @@ -1157,7 +1157,7 @@ HWTEST_F(ImplicitStartProcessorTest, FindExtensionInfo_001, TestSize.Level1) TAG_LOGI(AAFwkTag::TEST, "GetBundleName %{public}s", test.c_str()); test = want.GetElement().GetAbilityName(); TAG_LOGI(AAFwkTag::TEST, "GetAbilityName %{public}s", test.c_str()); - auto res = processor->FindExtensionInfo(want, flags, userId, appIndex, abilityInfo); + auto res = processor->FindExtensionInfo(elementName, flags, userId, appIndex, abilityInfo); EXPECT_EQ(res, RESOLVE_ABILITY_ERR); TAG_LOGI(AAFwkTag::TEST, "FindExtensionInfo_001 end"); } diff --git a/test/unittest/insight_intent/insight_intent_execute_manager_second_test/BUILD.gn b/test/unittest/insight_intent/insight_intent_execute_manager_second_test/BUILD.gn index bcca7ee3c3..16e344071b 100644 --- a/test/unittest/insight_intent/insight_intent_execute_manager_second_test/BUILD.gn +++ b/test/unittest/insight_intent/insight_intent_execute_manager_second_test/BUILD.gn @@ -50,8 +50,10 @@ ohos_unittest("insight_intent_execute_manager_second_test") { } deps = [ + "${ability_runtime_native_path}/ability/native:abilitykit_native", "${ability_runtime_native_path}/appkit:appkit_manager_helper", "${ability_runtime_services_path}/common:app_util", + "${ability_runtime_services_path}/common:perm_verification", ] external_deps = [ diff --git a/test/unittest/insight_intent/insight_intent_execute_manager_second_test/insight_intent_execute_manager_mock.cpp b/test/unittest/insight_intent/insight_intent_execute_manager_second_test/insight_intent_execute_manager_mock.cpp index 0aaeb63909..57a8344550 100644 --- a/test/unittest/insight_intent/insight_intent_execute_manager_second_test/insight_intent_execute_manager_mock.cpp +++ b/test/unittest/insight_intent/insight_intent_execute_manager_second_test/insight_intent_execute_manager_mock.cpp @@ -31,6 +31,10 @@ bool PermissionVerification::JudgeCallerIsAllowedToUseSystemAPI() const { return true; } +bool PermissionVerification::IsShellCall() const +{ + return false; +} } namespace AbilityRuntime { diff --git a/test/unittest/insight_intent/insight_intent_execute_manager_test/BUILD.gn b/test/unittest/insight_intent/insight_intent_execute_manager_test/BUILD.gn index 401cf3b2c7..3ed2538cd9 100644 --- a/test/unittest/insight_intent/insight_intent_execute_manager_test/BUILD.gn +++ b/test/unittest/insight_intent/insight_intent_execute_manager_test/BUILD.gn @@ -42,8 +42,10 @@ ohos_unittest("insight_intent_execute_manager_test") { } deps = [ + "${ability_runtime_native_path}/ability/native:abilitykit_native", "${ability_runtime_native_path}/appkit:appkit_manager_helper", "${ability_runtime_services_path}/common:app_util", + "${ability_runtime_services_path}/abilitymgr:abilityms", "${ability_runtime_services_path}/common:perm_verification", ] diff --git a/test/unittest/insight_intent/insight_intent_execute_manager_test/insight_intent_utils_mock.cpp b/test/unittest/insight_intent/insight_intent_execute_manager_test/insight_intent_utils_mock.cpp index 4f2bff111a..569b564beb 100644 --- a/test/unittest/insight_intent/insight_intent_execute_manager_test/insight_intent_utils_mock.cpp +++ b/test/unittest/insight_intent/insight_intent_execute_manager_test/insight_intent_utils_mock.cpp @@ -14,8 +14,16 @@ */ #include "insight_intent_utils.h" +#include "permission_verification.h" namespace OHOS { +namespace AAFwk { +bool PermissionVerification::IsShellCall() const +{ + return false; +} +} + namespace AbilityRuntime { uint32_t InsightIntentUtils::GetSrcEntry(const AppExecFwk::ElementName &elementName, const std::string &intentName, const AppExecFwk::ExecuteMode &executeMode, std::string &srcEntry, std::string *arkTSMode, int32_t userId, diff --git a/test/unittest/js_ability_context_test/BUILD.gn b/test/unittest/js_ability_context_test/BUILD.gn index df5b699b29..b6e8bc80be 100644 --- a/test/unittest/js_ability_context_test/BUILD.gn +++ b/test/unittest/js_ability_context_test/BUILD.gn @@ -28,6 +28,7 @@ ohos_unittest("js_ability_context_test") { debug = false } include_dirs = [ + "${ability_runtime_path}/interfaces/inner_api/ability_manager/include", "${ability_runtime_path}/interfaces/kits/native/ability/native", "${ability_runtime_path}/interfaces/kits/native/ability/native/ability_runtime", "mock_parse_requestcode.h", diff --git a/test/unittest/js_ability_context_test/js_ability_context_test.cpp b/test/unittest/js_ability_context_test/js_ability_context_test.cpp index 7befb3fc84..38ab2e6367 100644 --- a/test/unittest/js_ability_context_test/js_ability_context_test.cpp +++ b/test/unittest/js_ability_context_test/js_ability_context_test.cpp @@ -20,6 +20,7 @@ #include "ability_context.h" #include "ability_context_impl.h" #include "ability_business_error.h" +#include "ability_manager_errors.h" #include "errors.h" #include "hilog_wrapper.h" #define private public @@ -115,6 +116,13 @@ public: void DisconnectAbility(const AAFwk::Want &want, const sptr &connectCallback, int32_t accountId = -1) override {} + + virtual ErrCode StartSelfUIAbilityInChildProcess(const AAFwk::Want &want, + const std::string &specifiedFlag) override + { + GTEST_LOG_(INFO) << "StartSelfUIAbilityInChildProcess mock called, ret " << startSelfRet_; + return startSelfRet_; + } public: static void DoneConnect(int status) { @@ -130,9 +138,11 @@ public: callback_->OnAbilityDisconnectDone(element, 0); } void SetConnectResult(ErrCode code) { connectRet_ = code; } + void SetStartSelfResult(ErrCode code) { startSelfRet_ = code; } protected: static sptr callback_; ErrCode connectRet_ = ERR_OK; + ErrCode startSelfRet_ = ERR_OK; }; sptr MockAbilityContextImpl::callback_; @@ -601,5 +611,75 @@ HWTEST_F(AbilityContextTest, AbilityRuntime_AbilityContext_ContextType_0400, Tes GTEST_LOG_(INFO) << "AbilityRuntime_AbilityContext_ContextType_0400 end"; } +/** + * @tc.name: AbilityRuntime_AbilityContext_StartSelfUIAbilityInChildProcess_0100 + * @tc.desc: Test StartSelfUIAbilityInChildProcess with normal parameters + * @tc.type: FUNC + */ +HWTEST_F(AbilityContextTest, AbilityRuntime_AbilityContext_StartSelfUIAbilityInChildProcess_0100, TestSize.Level1) +{ + auto contextImpl = std::make_shared(); + AAFwk::Want want; + want.SetElementName("com.test.bundle", "TestAbility"); + std::string specifiedFlag = "testFlag"; + contextImpl->SetStartSelfResult(ERR_OK); + auto errCode = contextImpl->StartSelfUIAbilityInChildProcess(want, specifiedFlag); + EXPECT_EQ(errCode, ERR_OK); +} + +/** + * @tc.name: AbilityRuntime_AbilityContext_StartSelfUIAbilityInChildProcess_0200 + * @tc.desc: Test StartSelfUIAbilityInChildProcess with empty want + * @tc.type: FUNC + */ +HWTEST_F(AbilityContextTest, AbilityRuntime_AbilityContext_StartSelfUIAbilityInChildProcess_0200, TestSize.Level1) +{ + auto contextImpl = std::make_shared(); + AAFwk::Want want; + std::string specifiedFlag = ""; + contextImpl->SetStartSelfResult(ERR_INVALID_VALUE); + auto errCode = contextImpl->StartSelfUIAbilityInChildProcess(want, specifiedFlag); + EXPECT_EQ(errCode, ERR_INVALID_VALUE); +} + +/** + * @tc.name: AbilityRuntime_AbilityContext_StartSelfUIAbilityInChildProcess_0300 + * @tc.desc: Test StartSelfUIAbilityInChildProcess with specifiedFlag + * @tc.type: FUNC + */ +HWTEST_F(AbilityContextTest, AbilityRuntime_AbilityContext_StartSelfUIAbilityInChildProcess_0300, TestSize.Level1) +{ + auto contextImpl = std::make_shared(); + AAFwk::Want want; + want.SetElementName("com.test.bundle", "TestAbility"); + want.SetParam("testKey", std::string("testValue")); + std::string specifiedFlag = "specifiedFlag123"; + contextImpl->SetStartSelfResult(ERR_OK); + auto errCode = contextImpl->StartSelfUIAbilityInChildProcess(want, specifiedFlag); + EXPECT_EQ(errCode, ERR_OK); +} + +/** + * @tc.name: AbilityRuntime_AbilityContext_OnStartSelfUIAbilityInChildProcess_0100 + * @tc.desc: Test OnStartSelfUIAbilityInChildProcess JS binding + * @tc.type: FUNC + */ +HWTEST_F(AbilityContextTest, AbilityRuntime_AbilityContext_OnStartSelfUIAbilityInChildProcess_0100, TestSize.Level1) +{ + OHOS::AbilityRuntime::Runtime::Options options; + std::shared_ptr jsEnv = nullptr; + auto err = JsRuntimeLite::GetInstance().CreateJsEnv(options, jsEnv); + ASSERT_EQ(err, napi_status::napi_ok); + ASSERT_NE(jsEnv, nullptr); + napi_env env = reinterpret_cast(jsEnv->GetNativeEngine()); + + NapiCallbackInfo info{2}; + AAFwk::Want want; + info.argv[0] = OHOS::AppExecFwk::WrapWant(env, want); + napi_create_string_latin1(env, "testFlag", NAPI_AUTO_LENGTH, &info.argv[1]); + jsAbilityContext_->OnStartSelfUIAbilityInChildProcess(env, info); + + JsRuntimeLite::GetInstance().RemoveJsEnv(reinterpret_cast(jsEnv->GetNativeEngine())); +} } // namespace AAFwk } // namespace OHOS \ No newline at end of file diff --git a/test/unittest/js_ui_ability_test/js_ui_ability_test.cpp b/test/unittest/js_ui_ability_test/js_ui_ability_test.cpp index 750cbcdd6b..d5f06e7751 100644 --- a/test/unittest/js_ui_ability_test/js_ui_ability_test.cpp +++ b/test/unittest/js_ui_ability_test/js_ui_ability_test.cpp @@ -516,6 +516,90 @@ HWTEST_F(JsUiAbilityTest, JSUIAbility_OnStart_0600, TestSize.Level1) GTEST_LOG_(INFO) << "JSUIAbility_OnStart_0600 end"; } +/** + * @tc.name: JSUIAbility_OnStart_0700 + * @tc.desc: OnStart test with GAME_PRELAUNCH = true + * @tc.desc: Verify isGamePreLaunch_ is set to true when GAME_PRELAUNCH param is true + */ +HWTEST_F(JsUiAbilityTest, JSUIAbility_OnStart_0700, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "JSUIAbility_OnStart_0700 start"; + AbilityRuntime::Runtime::Options options; + options.lang = AbilityRuntime::Runtime::Language::JS; + auto runtime = AbilityRuntime::Runtime::Create(options); + auto jsRuntime = static_cast(runtime.get()); + auto ability = std::make_shared(*jsRuntime); + EXPECT_NE(ability, nullptr); + Want want; + want.SetParam(std::string("ohos.params.gamePrelaunch"), true); + napi_ref ref = nullptr; + auto env = jsRuntime->GetNapiEnv(); + napi_value value = OHOS::AppExecFwk::WrapWant(env, want); + napi_create_reference(env, value, 1, &ref); + ability->jsAbilityObj_ = std::unique_ptr( + reinterpret_cast(ref)); + EXPECT_NE(ability->jsAbilityObj_, nullptr); + + sptr sessionInfo = sptr::MakeSptr(); + EXPECT_NE(sessionInfo, nullptr); + ability->scene_ = std::make_shared(); + EXPECT_NE(ability->scene_, nullptr); + auto abilityContextImpl = std::make_shared(); + EXPECT_NE(abilityContextImpl, nullptr); + auto abilityInfo = std::make_shared(); + EXPECT_NE(abilityInfo, nullptr); + abilityContextImpl->SetAbilityInfo(abilityInfo); + ability->abilityContext_ = abilityContextImpl; + ability->abilityInfo_ = abilityInfo; + EXPECT_NE(ability->abilityInfo_, nullptr); + ability->OnStart(want, sessionInfo); + EXPECT_TRUE(ability->isGamePreLaunch_); + + GTEST_LOG_(INFO) << "JSUIAbility_OnStart_0700 end"; +} + +/** + * @tc.name: JSUIAbility_OnStart_0800 + * @tc.desc: OnStart test with GAME_PRELAUNCH = false + * @tc.desc: Verify isGamePreLaunch_ remains false when GAME_PRELAUNCH param is false + */ +HWTEST_F(JsUiAbilityTest, JSUIAbility_OnStart_0800, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "JSUIAbility_OnStart_0800 start"; + AbilityRuntime::Runtime::Options options; + options.lang = AbilityRuntime::Runtime::Language::JS; + auto runtime = AbilityRuntime::Runtime::Create(options); + auto jsRuntime = static_cast(runtime.get()); + auto ability = std::make_shared(*jsRuntime); + EXPECT_NE(ability, nullptr); + Want want; + want.SetParam(std::string("ohos.params.gamePrelaunch"), false); + napi_ref ref = nullptr; + auto env = jsRuntime->GetNapiEnv(); + napi_value value = OHOS::AppExecFwk::WrapWant(env, want); + napi_create_reference(env, value, 1, &ref); + ability->jsAbilityObj_ = std::unique_ptr( + reinterpret_cast(ref)); + EXPECT_NE(ability->jsAbilityObj_, nullptr); + + sptr sessionInfo = sptr::MakeSptr(); + EXPECT_NE(sessionInfo, nullptr); + ability->scene_ = std::make_shared(); + EXPECT_NE(ability->scene_, nullptr); + auto abilityContextImpl = std::make_shared(); + EXPECT_NE(abilityContextImpl, nullptr); + auto abilityInfo = std::make_shared(); + EXPECT_NE(abilityInfo, nullptr); + abilityContextImpl->SetAbilityInfo(abilityInfo); + ability->abilityContext_ = abilityContextImpl; + ability->abilityInfo_ = abilityInfo; + EXPECT_NE(ability->abilityInfo_, nullptr); + ability->OnStart(want, sessionInfo); + EXPECT_FALSE(ability->isGamePreLaunch_); + + GTEST_LOG_(INFO) << "JSUIAbility_OnStart_0800 end"; +} + /** * @tc.name: JSUIAbility_GetWindowStage_0100 * @tc.desc: GetWindowStage test diff --git a/test/unittest/js_ui_extension_context_test/BUILD.gn b/test/unittest/js_ui_extension_context_test/BUILD.gn index a38a1010a0..b8ecd03ec6 100644 --- a/test/unittest/js_ui_extension_context_test/BUILD.gn +++ b/test/unittest/js_ui_extension_context_test/BUILD.gn @@ -31,6 +31,7 @@ ohos_unittest("js_ui_extension_context_test") { "${ability_runtime_path}/interfaces/kits/native/ability/native", "${ability_runtime_path}/interfaces/kits/native/ability/native/ability_runtime", "${ability_runtime_path}/interfaces/kits/native/ability/native/ui_extension_ability", + "${ability_runtime_path}/interfaces/kits/native/ability/native/ui_extension_base", ] sources = [ "js_ui_extension_context_test.cpp" ] diff --git a/test/unittest/js_ui_extension_context_test/js_ui_extension_context_test.cpp b/test/unittest/js_ui_extension_context_test/js_ui_extension_context_test.cpp index ef5bd3b046..d8aaf5823f 100644 --- a/test/unittest/js_ui_extension_context_test/js_ui_extension_context_test.cpp +++ b/test/unittest/js_ui_extension_context_test/js_ui_extension_context_test.cpp @@ -205,7 +205,7 @@ void UIExtensionContextTest::TearDown() void UIExtensionContextTest::Connect(napi_value* argv, int32_t argc) { GTEST_LOG_(INFO) << "AbilityRuntime_AbilityContext_0100 start"; - auto func = [](napi_env env, napi_callback_info info) -> napi_value { + napi_callback func = [](napi_env env, napi_callback_info info) -> napi_value { JsUIExtensionContext::ConnectUIServiceExtension(env, info); napi_value result = nullptr; napi_get_undefined(env, &result); @@ -231,7 +231,7 @@ void UIExtensionContextTest::Connect(napi_value* argv, int32_t argc) void UIExtensionContextTest::Disconnect(napi_value* argv, int32_t argc) { - auto func = [](napi_env env, napi_callback_info info) -> napi_value { + napi_callback func = [](napi_env env, napi_callback_info info) -> napi_value { JsUIExtensionContext::DisconnectUIServiceExtension(env, info); napi_value result = nullptr; napi_get_undefined(env, &result); @@ -406,5 +406,920 @@ HWTEST_F(UIExtensionContextTest, AbilityRuntime_UIExtensionContext_0107, TestSiz auto displayId = want.GetIntParam(AAFwk::Want::PARAM_RESV_DISPLAY_ID, 0); EXPECT_EQ(displayId, 0); } + +// ==================== OnTerminateSelf Tests ==================== + +// OnTerminateSelf: non-embeddable mode (default screenMode), no callback +HWTEST_F(UIExtensionContextTest, AbilityRuntime_UIExtensionContext_OnTerminateSelf_0100, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "AbilityRuntime_UIExtensionContext_OnTerminateSelf_0100 start"; + HandleScope handleScope(env_); + napi_callback func = [](napi_env env, napi_callback_info info) -> napi_value { + return JsUIExtensionContext::TerminateSelf(env, info); + }; + + napi_value recv = nullptr; + napi_create_object(env_, &recv); + napi_status wrapret = napi_wrap(env_, recv, jsUIExtensionContext_.get(), + [](napi_env env, void* data, void* hint) {}, nullptr, nullptr); + EXPECT_EQ(wrapret, napi_ok); + + napi_value funcValue = nullptr; + napi_create_function(env_, "terminateSelf", NAPI_AUTO_LENGTH, func, nullptr, &funcValue); + napi_value funcResultValue = nullptr; + napi_value argv[] = {}; + napi_status status = napi_call_function(env_, recv, funcValue, ARGC_ZERO, argv, &funcResultValue); + EXPECT_EQ(status, napi_ok); + + ArkNativeEngine* engine = (ArkNativeEngine*)env_; + uv_loop_t* loop = engine->GetUVLoop(); + RunNowait(loop); + + GTEST_LOG_(INFO) << "AbilityRuntime_UIExtensionContext_OnTerminateSelf_0100 end"; +} + +// OnTerminateSelf: non-embeddable mode with callback +HWTEST_F(UIExtensionContextTest, AbilityRuntime_UIExtensionContext_OnTerminateSelf_0200, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "AbilityRuntime_UIExtensionContext_OnTerminateSelf_0200 start"; + HandleScope handleScope(env_); + TryCatch tryCatch(env_); + + napi_callback func = [](napi_env env, napi_callback_info info) -> napi_value { + return JsUIExtensionContext::TerminateSelf(env, info); + }; + + napi_value recv = nullptr; + napi_create_object(env_, &recv); + napi_status wrapret = napi_wrap(env_, recv, jsUIExtensionContext_.get(), + [](napi_env env, void* data, void* hint) {}, nullptr, nullptr); + EXPECT_EQ(wrapret, napi_ok); + + napi_value callbackObject = nullptr; + napi_create_function(env_, "callback", NAPI_AUTO_LENGTH, + [](napi_env env, napi_callback_info info) -> napi_value { + napi_value result = nullptr; + napi_get_undefined(env, &result); + return result; + }, nullptr, &callbackObject); + + napi_value funcValue = nullptr; + napi_create_function(env_, "terminateSelf", NAPI_AUTO_LENGTH, func, nullptr, &funcValue); + napi_value funcResultValue = nullptr; + napi_value argv[] = { callbackObject }; + napi_status status = napi_call_function(env_, recv, funcValue, ARGC_ONE, argv, &funcResultValue); + EXPECT_EQ(status, napi_ok); + + ArkNativeEngine* engine = (ArkNativeEngine*)env_; + uv_loop_t* loop = engine->GetUVLoop(); + RunNowait(loop); + + EXPECT_FALSE(tryCatch.HasCaught()); + if (tryCatch.HasCaught()) { + tryCatch.ClearException(); + } + if (!engine->lastException_.IsEmpty()) { + engine->lastException_.Empty(); + } + GTEST_LOG_(INFO) << "AbilityRuntime_UIExtensionContext_OnTerminateSelf_0200 end"; +} + +// OnTerminateSelf: embeddable mode (EMBEDDED_FULL_SCREEN_MODE), no callback +HWTEST_F(UIExtensionContextTest, AbilityRuntime_UIExtensionContext_OnTerminateSelf_0300, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "AbilityRuntime_UIExtensionContext_OnTerminateSelf_0300 start"; + HandleScope handleScope(env_); + + // Set screen mode to embeddable + abilityContextImpl_->SetScreenMode(1); // EMBEDDED_FULL_SCREEN_MODE + + napi_callback func = [](napi_env env, napi_callback_info info) -> napi_value { + return JsUIExtensionContext::TerminateSelf(env, info); + }; + + napi_value recv = nullptr; + napi_create_object(env_, &recv); + napi_status wrapret = napi_wrap(env_, recv, jsUIExtensionContext_.get(), + [](napi_env env, void* data, void* hint) {}, nullptr, nullptr); + EXPECT_EQ(wrapret, napi_ok); + + napi_value funcValue = nullptr; + napi_create_function(env_, "terminateSelf", NAPI_AUTO_LENGTH, func, nullptr, &funcValue); + napi_value funcResultValue = nullptr; + napi_value argv[] = {}; + napi_status status = napi_call_function(env_, recv, funcValue, ARGC_ZERO, argv, &funcResultValue); + EXPECT_EQ(status, napi_ok); + + // Verify embeddable mode triggered + EXPECT_EQ(abilityContextImpl_->GetScreenMode(), 1); + + ArkNativeEngine* engine = (ArkNativeEngine*)env_; + uv_loop_t* loop = engine->GetUVLoop(); + RunNowait(loop); + + GTEST_LOG_(INFO) << "AbilityRuntime_UIExtensionContext_OnTerminateSelf_0300 end"; +} + +// OnTerminateSelf: embeddable mode (EMBEDDED_HALF_SCREEN_MODE), with callback +HWTEST_F(UIExtensionContextTest, AbilityRuntime_UIExtensionContext_OnTerminateSelf_0400, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "AbilityRuntime_UIExtensionContext_OnTerminateSelf_0400 start"; + HandleScope handleScope(env_); + TryCatch tryCatch(env_); + + // Set screen mode to embeddable half screen + abilityContextImpl_->SetScreenMode(2); // EMBEDDED_HALF_SCREEN_MODE + + napi_callback func = [](napi_env env, napi_callback_info info) -> napi_value { + return JsUIExtensionContext::TerminateSelf(env, info); + }; + + napi_value recv = nullptr; + napi_create_object(env_, &recv); + napi_status wrapret = napi_wrap(env_, recv, jsUIExtensionContext_.get(), + [](napi_env env, void* data, void* hint) {}, nullptr, nullptr); + EXPECT_EQ(wrapret, napi_ok); + + napi_value callbackObject = nullptr; + napi_create_function(env_, "callback", NAPI_AUTO_LENGTH, + [](napi_env env, napi_callback_info info) -> napi_value { + napi_value result = nullptr; + napi_get_undefined(env, &result); + return result; + }, nullptr, &callbackObject); + + napi_value funcValue = nullptr; + napi_create_function(env_, "terminateSelf", NAPI_AUTO_LENGTH, func, nullptr, &funcValue); + napi_value funcResultValue = nullptr; + napi_value argv[] = { callbackObject }; + napi_status status = napi_call_function(env_, recv, funcValue, ARGC_ONE, argv, &funcResultValue); + EXPECT_EQ(status, napi_ok); + + EXPECT_EQ(abilityContextImpl_->GetScreenMode(), 2); + + ArkNativeEngine* engine = (ArkNativeEngine*)env_; + uv_loop_t* loop = engine->GetUVLoop(); + RunNowait(loop); + + EXPECT_FALSE(tryCatch.HasCaught()); + if (tryCatch.HasCaught()) { + tryCatch.ClearException(); + } + if (!engine->lastException_.IsEmpty()) { + engine->lastException_.Empty(); + } + GTEST_LOG_(INFO) << "AbilityRuntime_UIExtensionContext_OnTerminateSelf_0400 end"; +} + +// ==================== HandleTerminateSelfInEmbeddableMode Tests ==================== + +// HandleTerminateSelfInEmbeddableMode: context is null +HWTEST_F(UIExtensionContextTest, AbilityRuntime_UIExtensionContext_HandleTerminateSelfEmbeddable_0100, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "HandleTerminateSelfEmbeddable_0100 start"; + HandleScope handleScope(env_); + TryCatch tryCatch(env_); + + // Create a JsUIExtensionContext with null context + std::shared_ptr nullContext; + auto jsCtx = std::make_shared(nullContext); + jsCtx->context_.reset(); // ensure weak_ptr is expired + + NapiCallbackInfo napiInfo; + napiInfo.argc = ARGC_ZERO; + napi_value result = jsCtx->OnTerminateSelf(env_, napiInfo); + EXPECT_NE(result, nullptr); + + // Promise should reject because context is null + ArkNativeEngine* engine = (ArkNativeEngine*)env_; + uv_loop_t* loop = engine->GetUVLoop(); + RunNowait(loop); + + EXPECT_FALSE(tryCatch.HasCaught()); + if (tryCatch.HasCaught()) { + tryCatch.ClearException(); + } + if (!engine->lastException_.IsEmpty()) { + engine->lastException_.Empty(); + } + GTEST_LOG_(INFO) << "HandleTerminateSelfEmbeddable_0100 end"; +} + +// HandleTerminateSelfInEmbeddableMode: context valid, screenMode embeddable +HWTEST_F(UIExtensionContextTest, AbilityRuntime_UIExtensionContext_HandleTerminateSelfEmbeddable_0200, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "HandleTerminateSelfEmbeddable_0200 start"; + HandleScope handleScope(env_); + + // Set embeddable screen mode + abilityContextImpl_->SetScreenMode(1); // EMBEDDED_FULL_SCREEN_MODE + + napi_callback func = [](napi_env env, napi_callback_info info) -> napi_value { + return JsUIExtensionContext::TerminateSelf(env, info); + }; + + napi_value recv = nullptr; + napi_create_object(env_, &recv); + napi_status wrapret = napi_wrap(env_, recv, jsUIExtensionContext_.get(), + [](napi_env env, void* data, void* hint) {}, nullptr, nullptr); + EXPECT_EQ(wrapret, napi_ok); + + napi_value funcValue = nullptr; + napi_create_function(env_, "terminateSelf", NAPI_AUTO_LENGTH, func, nullptr, &funcValue); + napi_value funcResultValue = nullptr; + napi_value argv[] = {}; + napi_status status = napi_call_function(env_, recv, funcValue, ARGC_ZERO, argv, &funcResultValue); + EXPECT_EQ(status, napi_ok); + EXPECT_NE(funcResultValue, nullptr); + + ArkNativeEngine* engine = (ArkNativeEngine*)env_; + uv_loop_t* loop = engine->GetUVLoop(); + RunNowait(loop); + + GTEST_LOG_(INFO) << "HandleTerminateSelfEmbeddable_0200 end"; +} + +// ==================== HandleTerminateSelfInNonEmbeddableMode Tests ==================== + +// HandleTerminateSelfInNonEmbeddableMode: context is null +HWTEST_F(UIExtensionContextTest, HandleTerminateSelfNonEmbeddable_0100, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "HandleTerminateSelfNonEmbeddable_0100 start"; + HandleScope handleScope(env_); + TryCatch tryCatch(env_); + + // Create JsUIExtensionContext with expired weak_ptr + std::shared_ptr tempCtx = std::make_shared(); + auto jsCtx = std::make_shared(tempCtx); + tempCtx.reset(); // release the shared_ptr, weak_ptr expires + + NapiCallbackInfo napiInfo; + napiInfo.argc = ARGC_ZERO; + napi_value result = jsCtx->OnTerminateSelf(env_, napiInfo); + EXPECT_NE(result, nullptr); + + ArkNativeEngine* engine = (ArkNativeEngine*)env_; + uv_loop_t* loop = engine->GetUVLoop(); + RunNowait(loop); + + EXPECT_FALSE(tryCatch.HasCaught()); + if (tryCatch.HasCaught()) { + tryCatch.ClearException(); + } + if (!engine->lastException_.IsEmpty()) { + engine->lastException_.Empty(); + } + GTEST_LOG_(INFO) << "HandleTerminateSelfNonEmbeddable_0100 end"; +} + +// HandleTerminateSelfInNonEmbeddableMode: context valid, TerminateSelf succeeds +HWTEST_F(UIExtensionContextTest, HandleTerminateSelfNonEmbeddable_0200, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "HandleTerminateSelfNonEmbeddable_0200 start"; + HandleScope handleScope(env_); + TryCatch tryCatch(env_); + + // Default screenMode is non-embeddable (IDLE_SCREEN_MODE = -1) + EXPECT_NE(abilityContextImpl_->GetScreenMode(), 1); + EXPECT_NE(abilityContextImpl_->GetScreenMode(), 2); + + napi_callback func = [](napi_env env, napi_callback_info info) -> napi_value { + return JsUIExtensionContext::TerminateSelf(env, info); + }; + + napi_value recv = nullptr; + napi_create_object(env_, &recv); + napi_status wrapret = napi_wrap(env_, recv, jsUIExtensionContext_.get(), + [](napi_env env, void* data, void* hint) {}, nullptr, nullptr); + EXPECT_EQ(wrapret, napi_ok); + + napi_value funcValue = nullptr; + napi_create_function(env_, "terminateSelf", NAPI_AUTO_LENGTH, func, nullptr, &funcValue); + napi_value funcResultValue = nullptr; + napi_value argv[] = {}; + napi_status status = napi_call_function(env_, recv, funcValue, ARGC_ZERO, argv, &funcResultValue); + EXPECT_EQ(status, napi_ok); + EXPECT_NE(funcResultValue, nullptr); + + ArkNativeEngine* engine = (ArkNativeEngine*)env_; + uv_loop_t* loop = engine->GetUVLoop(); + RunNowait(loop); + + EXPECT_FALSE(tryCatch.HasCaught()); + if (tryCatch.HasCaught()) { + tryCatch.ClearException(); + } + if (!engine->lastException_.IsEmpty()) { + engine->lastException_.Empty(); + } + GTEST_LOG_(INFO) << "HandleTerminateSelfNonEmbeddable_0200 end"; +} + +// ==================== OnTerminateSelfWithResult Tests ==================== + +// OnTerminateSelfWithResult: argc == 0, ThrowTooFewParametersError +HWTEST_F(UIExtensionContextTest, AbilityRuntime_UIExtensionContext_OnTerminateSelfWithResult_0100, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "OnTerminateSelfWithResult_0100 start"; + HandleScope handleScope(env_); + TryCatch tryCatch(env_); + + napi_callback func = [](napi_env env, napi_callback_info info) -> napi_value { + return JsUIExtensionContext::TerminateSelfWithResult(env, info); + }; + + napi_value recv = nullptr; + napi_create_object(env_, &recv); + napi_status wrapret = napi_wrap(env_, recv, jsUIExtensionContext_.get(), + [](napi_env env, void* data, void* hint) {}, nullptr, nullptr); + EXPECT_EQ(wrapret, napi_ok); + + napi_value funcValue = nullptr; + napi_create_function(env_, "terminateSelfWithResult", NAPI_AUTO_LENGTH, func, nullptr, &funcValue); + napi_value funcResultValue = nullptr; + napi_value argv[] = {}; + napi_status status = napi_call_function(env_, recv, funcValue, ARGC_ZERO, argv, &funcResultValue); + EXPECT_EQ(status, napi_pending_exception); + + EXPECT_TRUE(tryCatch.HasCaught()); + tryCatch.ClearException(); + ArkNativeEngine* engine = (ArkNativeEngine*)env_; + if (!engine->lastException_.IsEmpty()) { + engine->lastException_.Empty(); + } + GTEST_LOG_(INFO) << "OnTerminateSelfWithResult_0100 end"; +} + +// OnTerminateSelfWithResult: invalid ability result param, ThrowInvalidParamError +HWTEST_F(UIExtensionContextTest, AbilityRuntime_UIExtensionContext_OnTerminateSelfWithResult_0200, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "OnTerminateSelfWithResult_0200 start"; + HandleScope handleScope(env_); + TryCatch tryCatch(env_); + + napi_callback func = [](napi_env env, napi_callback_info info) -> napi_value { + return JsUIExtensionContext::TerminateSelfWithResult(env, info); + }; + + napi_value recv = nullptr; + napi_create_object(env_, &recv); + napi_status wrapret = napi_wrap(env_, recv, jsUIExtensionContext_.get(), + [](napi_env env, void* data, void* hint) {}, nullptr, nullptr); + EXPECT_EQ(wrapret, napi_ok); + + napi_value undef = nullptr; + napi_get_undefined(env_, &undef); + napi_value argv[] = { undef }; + napi_value funcValue = nullptr; + napi_create_function(env_, "terminateSelfWithResult", NAPI_AUTO_LENGTH, func, nullptr, &funcValue); + napi_value funcResultValue = nullptr; + napi_status status = napi_call_function(env_, recv, funcValue, ARGC_ONE, argv, &funcResultValue); + EXPECT_EQ(status, napi_pending_exception); + + EXPECT_TRUE(tryCatch.HasCaught()); + tryCatch.ClearException(); + ArkNativeEngine* engine = (ArkNativeEngine*)env_; + if (!engine->lastException_.IsEmpty()) { + engine->lastException_.Empty(); + } + GTEST_LOG_(INFO) << "OnTerminateSelfWithResult_0200 end"; +} + +// OnTerminateSelfWithResult: valid result, non-embeddable mode +HWTEST_F(UIExtensionContextTest, AbilityRuntime_UIExtensionContext_OnTerminateSelfWithResult_0300, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "OnTerminateSelfWithResult_0300 start"; + HandleScope handleScope(env_); + TryCatch tryCatch(env_); + + napi_callback func = [](napi_env env, napi_callback_info info) -> napi_value { + return JsUIExtensionContext::TerminateSelfWithResult(env, info); + }; + + napi_value recv = nullptr; + napi_create_object(env_, &recv); + napi_status wrapret = napi_wrap(env_, recv, jsUIExtensionContext_.get(), + [](napi_env env, void* data, void* hint) {}, nullptr, nullptr); + EXPECT_EQ(wrapret, napi_ok); + + // Create ability result: { resultCode: 0, want: {} } + napi_value resultObject = nullptr; + napi_create_object(env_, &resultObject); + napi_value resultCode = nullptr; + napi_create_int32(env_, 0, &resultCode); + napi_set_named_property(env_, resultObject, "resultCode", resultCode); + AAFwk::Want want; + napi_value jsWant = AppExecFwk::CreateJsWant(env_, want); + napi_set_named_property(env_, resultObject, "want", jsWant); + + napi_value funcValue = nullptr; + napi_create_function(env_, "terminateSelfWithResult", NAPI_AUTO_LENGTH, func, nullptr, &funcValue); + napi_value funcResultValue = nullptr; + napi_value argv[] = { resultObject }; + napi_status status = napi_call_function(env_, recv, funcValue, ARGC_ONE, argv, &funcResultValue); + EXPECT_EQ(status, napi_ok); + + ArkNativeEngine* engine = (ArkNativeEngine*)env_; + uv_loop_t* loop = engine->GetUVLoop(); + RunNowait(loop); + + EXPECT_FALSE(tryCatch.HasCaught()); + if (tryCatch.HasCaught()) { + tryCatch.ClearException(); + } + if (!engine->lastException_.IsEmpty()) { + engine->lastException_.Empty(); + } + GTEST_LOG_(INFO) << "OnTerminateSelfWithResult_0300 end"; +} + +// OnTerminateSelfWithResult: valid result, embeddable mode (EMBEDDED_FULL_SCREEN_MODE) +HWTEST_F(UIExtensionContextTest, AbilityRuntime_UIExtensionContext_OnTerminateSelfWithResult_0400, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "OnTerminateSelfWithResult_0400 start"; + HandleScope handleScope(env_); + TryCatch tryCatch(env_); + + // Set embeddable screen mode + abilityContextImpl_->SetScreenMode(1); // EMBEDDED_FULL_SCREEN_MODE + + napi_callback func = [](napi_env env, napi_callback_info info) -> napi_value { + return JsUIExtensionContext::TerminateSelfWithResult(env, info); + }; + + napi_value recv = nullptr; + napi_create_object(env_, &recv); + napi_status wrapret = napi_wrap(env_, recv, jsUIExtensionContext_.get(), + [](napi_env env, void* data, void* hint) {}, nullptr, nullptr); + EXPECT_EQ(wrapret, napi_ok); + + // Create ability result + napi_value resultObject = nullptr; + napi_create_object(env_, &resultObject); + napi_value resultCode = nullptr; + napi_create_int32(env_, 0, &resultCode); + napi_set_named_property(env_, resultObject, "resultCode", resultCode); + AAFwk::Want want; + napi_value jsWant = AppExecFwk::CreateJsWant(env_, want); + napi_set_named_property(env_, resultObject, "want", jsWant); + + napi_value funcValue = nullptr; + napi_create_function(env_, "terminateSelfWithResult", NAPI_AUTO_LENGTH, func, nullptr, &funcValue); + napi_value funcResultValue = nullptr; + napi_value argv[] = { resultObject }; + napi_status status = napi_call_function(env_, recv, funcValue, ARGC_ONE, argv, &funcResultValue); + EXPECT_EQ(status, napi_ok); + EXPECT_NE(funcResultValue, nullptr); + + EXPECT_EQ(abilityContextImpl_->GetScreenMode(), 1); + + ArkNativeEngine* engine = (ArkNativeEngine*)env_; + uv_loop_t* loop = engine->GetUVLoop(); + RunNowait(loop); + + EXPECT_FALSE(tryCatch.HasCaught()); + if (tryCatch.HasCaught()) { + tryCatch.ClearException(); + } + if (!engine->lastException_.IsEmpty()) { + engine->lastException_.Empty(); + } + GTEST_LOG_(INFO) << "OnTerminateSelfWithResult_0400 end"; +} + +// OnTerminateSelfWithResult: valid result, embeddable mode with callback +HWTEST_F(UIExtensionContextTest, AbilityRuntime_UIExtensionContext_OnTerminateSelfWithResult_0500, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "OnTerminateSelfWithResult_0500 start"; + HandleScope handleScope(env_); + TryCatch tryCatch(env_); + + // Set embeddable screen mode + abilityContextImpl_->SetScreenMode(2); // EMBEDDED_HALF_SCREEN_MODE + + napi_callback func = [](napi_env env, napi_callback_info info) -> napi_value { + return JsUIExtensionContext::TerminateSelfWithResult(env, info); + }; + + napi_value recv = nullptr; + napi_create_object(env_, &recv); + napi_status wrapret = napi_wrap(env_, recv, jsUIExtensionContext_.get(), + [](napi_env env, void* data, void* hint) {}, nullptr, nullptr); + EXPECT_EQ(wrapret, napi_ok); + + // Create ability result + napi_value resultObject = nullptr; + napi_create_object(env_, &resultObject); + napi_value resultCode = nullptr; + napi_create_int32(env_, 0, &resultCode); + napi_set_named_property(env_, resultObject, "resultCode", resultCode); + AAFwk::Want want; + napi_value jsWant = AppExecFwk::CreateJsWant(env_, want); + napi_set_named_property(env_, resultObject, "want", jsWant); + + // Create callback + napi_value callbackFunc = nullptr; + napi_create_function(env_, "callback", NAPI_AUTO_LENGTH, + [](napi_env env, napi_callback_info info) -> napi_value { + napi_value result = nullptr; + napi_get_undefined(env, &result); + return result; + }, nullptr, &callbackFunc); + + napi_value funcValue = nullptr; + napi_create_function(env_, "terminateSelfWithResult", NAPI_AUTO_LENGTH, func, nullptr, &funcValue); + napi_value funcResultValue = nullptr; + napi_value argv[] = { resultObject, callbackFunc }; + napi_status status = napi_call_function(env_, recv, funcValue, ARGC_TWO, argv, &funcResultValue); + EXPECT_EQ(status, napi_ok); + + EXPECT_EQ(abilityContextImpl_->GetScreenMode(), 2); + + ArkNativeEngine* engine = (ArkNativeEngine*)env_; + uv_loop_t* loop = engine->GetUVLoop(); + RunNowait(loop); + + EXPECT_FALSE(tryCatch.HasCaught()); + if (tryCatch.HasCaught()) { + tryCatch.ClearException(); + } + if (!engine->lastException_.IsEmpty()) { + engine->lastException_.Empty(); + } + GTEST_LOG_(INFO) << "OnTerminateSelfWithResult_0500 end"; +} + +// OnTerminateSelfWithResult: valid result, non-embeddable mode with callback +HWTEST_F(UIExtensionContextTest, AbilityRuntime_UIExtensionContext_OnTerminateSelfWithResult_0600, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "OnTerminateSelfWithResult_0600 start"; + HandleScope handleScope(env_); + TryCatch tryCatch(env_); + + // Default non-embeddable mode + napi_callback func = [](napi_env env, napi_callback_info info) -> napi_value { + return JsUIExtensionContext::TerminateSelfWithResult(env, info); + }; + + napi_value recv = nullptr; + napi_create_object(env_, &recv); + napi_status wrapret = napi_wrap(env_, recv, jsUIExtensionContext_.get(), + [](napi_env env, void* data, void* hint) {}, nullptr, nullptr); + EXPECT_EQ(wrapret, napi_ok); + + // Create ability result + napi_value resultObject = nullptr; + napi_create_object(env_, &resultObject); + napi_value resultCode = nullptr; + napi_create_int32(env_, 0, &resultCode); + napi_set_named_property(env_, resultObject, "resultCode", resultCode); + AAFwk::Want want; + napi_value jsWant = AppExecFwk::CreateJsWant(env_, want); + napi_set_named_property(env_, resultObject, "want", jsWant); + + // Create callback + napi_value callbackFunc = nullptr; + napi_create_function(env_, "callback", NAPI_AUTO_LENGTH, + [](napi_env env, napi_callback_info info) -> napi_value { + napi_value result = nullptr; + napi_get_undefined(env, &result); + return result; + }, nullptr, &callbackFunc); + + napi_value funcValue = nullptr; + napi_create_function(env_, "terminateSelfWithResult", NAPI_AUTO_LENGTH, func, nullptr, &funcValue); + napi_value funcResultValue = nullptr; + napi_value argv[] = { resultObject, callbackFunc }; + napi_status status = napi_call_function(env_, recv, funcValue, ARGC_TWO, argv, &funcResultValue); + EXPECT_EQ(status, napi_ok); + + ArkNativeEngine* engine = (ArkNativeEngine*)env_; + uv_loop_t* loop = engine->GetUVLoop(); + RunNowait(loop); + + EXPECT_FALSE(tryCatch.HasCaught()); + if (tryCatch.HasCaught()) { + tryCatch.ClearException(); + } + if (!engine->lastException_.IsEmpty()) { + engine->lastException_.Empty(); + } + GTEST_LOG_(INFO) << "OnTerminateSelfWithResult_0600 end"; +} + +// ==================== HandleTerminateSelfWithResultInEmbeddableMode Tests ==================== + +// HandleTerminateSelfWithResultInEmbeddableMode: context is null +HWTEST_F(UIExtensionContextTest, TerminateSelfWithResultEmbeddable_0100, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "TerminateSelfWithResultEmbeddable_0100 start"; + HandleScope handleScope(env_); + TryCatch tryCatch(env_); + + // Create JsUIExtensionContext with expired weak_ptr + std::shared_ptr tempCtx = std::make_shared(); + auto jsCtx = std::make_shared(tempCtx); + tempCtx.reset(); + + // Set embeddable screen mode won't work on expired ptr, but the function will try + NapiCallbackInfo napiInfo; + napiInfo.argc = ARGC_ZERO; + + // Direct call - context_ is expired so isEmbeddable returns false, + // which goes to NonEmbeddableMode. To test embeddable with null context, + // we need to have context alive but set to embeddable + std::shared_ptr ctx = std::make_shared(); + ctx->SetScreenMode(1); // EMBEDDED_FULL_SCREEN_MODE + auto jsCtxEmbeddable = std::make_shared(ctx); + + // Now release the context so it becomes null + ctx.reset(); + + napi_value result = jsCtxEmbeddable->OnTerminateSelf(env_, napiInfo); + EXPECT_NE(result, nullptr); + + ArkNativeEngine* engine = (ArkNativeEngine*)env_; + uv_loop_t* loop = engine->GetUVLoop(); + RunNowait(loop); + + EXPECT_FALSE(tryCatch.HasCaught()); + if (tryCatch.HasCaught()) { + tryCatch.ClearException(); + } + if (!engine->lastException_.IsEmpty()) { + engine->lastException_.Empty(); + } + GTEST_LOG_(INFO) << "TerminateSelfWithResultEmbeddable_0100 end"; +} + +// HandleTerminateSelfWithResultInEmbeddableMode: context valid, ConvertTo succeeds +HWTEST_F(UIExtensionContextTest, TerminateSelfWithResultEmbeddable_0200, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "TerminateSelfWithResultEmbeddable_0200 start"; + HandleScope handleScope(env_); + TryCatch tryCatch(env_); + + abilityContextImpl_->SetScreenMode(1); // EMBEDDED_FULL_SCREEN_MODE + + napi_callback func = [](napi_env env, napi_callback_info info) -> napi_value { + return JsUIExtensionContext::TerminateSelfWithResult(env, info); + }; + + napi_value recv = nullptr; + napi_create_object(env_, &recv); + napi_status wrapret = napi_wrap(env_, recv, jsUIExtensionContext_.get(), + [](napi_env env, void* data, void* hint) {}, nullptr, nullptr); + EXPECT_EQ(wrapret, napi_ok); + + // Create ability result + napi_value resultObject = nullptr; + napi_create_object(env_, &resultObject); + napi_value resultCode = nullptr; + napi_create_int32(env_, 100, &resultCode); + napi_set_named_property(env_, resultObject, "resultCode", resultCode); + AAFwk::Want want; + napi_value jsWant = AppExecFwk::CreateJsWant(env_, want); + napi_set_named_property(env_, resultObject, "want", jsWant); + + napi_value funcValue = nullptr; + napi_create_function(env_, "terminateSelfWithResult", NAPI_AUTO_LENGTH, func, nullptr, &funcValue); + napi_value funcResultValue = nullptr; + napi_value argv[] = { resultObject }; + napi_status status = napi_call_function(env_, recv, funcValue, ARGC_ONE, argv, &funcResultValue); + EXPECT_EQ(status, napi_ok); + EXPECT_NE(funcResultValue, nullptr); + + ArkNativeEngine* engine = (ArkNativeEngine*)env_; + uv_loop_t* loop = engine->GetUVLoop(); + RunNowait(loop); + + EXPECT_FALSE(tryCatch.HasCaught()); + if (tryCatch.HasCaught()) { + tryCatch.ClearException(); + } + if (!engine->lastException_.IsEmpty()) { + engine->lastException_.Empty(); + } + GTEST_LOG_(INFO) << "TerminateSelfWithResultEmbeddable_0200 end"; +} + +// ==================== HandleTerminateSelfWithResultInNonEmbeddableMode Tests ==================== + +// HandleTerminateSelfWithResultInNonEmbeddableMode: basic call +HWTEST_F(UIExtensionContextTest, TerminateSelfWithResultNonEmbeddable_0100, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "TerminateSelfWithResultNonEmbeddable_0100 start"; + HandleScope handleScope(env_); + TryCatch tryCatch(env_); + + // Default non-embeddable mode + napi_callback func = [](napi_env env, napi_callback_info info) -> napi_value { + return JsUIExtensionContext::TerminateSelfWithResult(env, info); + }; + + napi_value recv = nullptr; + napi_create_object(env_, &recv); + napi_status wrapret = napi_wrap(env_, recv, jsUIExtensionContext_.get(), + [](napi_env env, void* data, void* hint) {}, nullptr, nullptr); + EXPECT_EQ(wrapret, napi_ok); + + // Create ability result with non-zero resultCode + napi_value resultObject = nullptr; + napi_create_object(env_, &resultObject); + napi_value resultCode = nullptr; + napi_create_int32(env_, -1, &resultCode); + napi_set_named_property(env_, resultObject, "resultCode", resultCode); + AAFwk::Want want; + napi_value jsWant = AppExecFwk::CreateJsWant(env_, want); + napi_set_named_property(env_, resultObject, "want", jsWant); + + napi_value funcValue = nullptr; + napi_create_function(env_, "terminateSelfWithResult", NAPI_AUTO_LENGTH, func, nullptr, &funcValue); + napi_value funcResultValue = nullptr; + napi_value argv[] = { resultObject }; + napi_status status = napi_call_function(env_, recv, funcValue, ARGC_ONE, argv, &funcResultValue); + EXPECT_EQ(status, napi_ok); + EXPECT_NE(funcResultValue, nullptr); + + ArkNativeEngine* engine = (ArkNativeEngine*)env_; + uv_loop_t* loop = engine->GetUVLoop(); + RunNowait(loop); + + EXPECT_FALSE(tryCatch.HasCaught()); + if (tryCatch.HasCaught()) { + tryCatch.ClearException(); + } + if (!engine->lastException_.IsEmpty()) { + engine->lastException_.Empty(); + } + GTEST_LOG_(INFO) << "TerminateSelfWithResultNonEmbeddable_0100 end"; +} + +// HandleTerminateSelfWithResultInNonEmbeddableMode: with callback param +HWTEST_F(UIExtensionContextTest, TerminateSelfWithResultNonEmbeddable_0200, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "TerminateSelfWithResultNonEmbeddable_0200 start"; + HandleScope handleScope(env_); + TryCatch tryCatch(env_); + + // Default non-embeddable mode + napi_callback func = [](napi_env env, napi_callback_info info) -> napi_value { + return JsUIExtensionContext::TerminateSelfWithResult(env, info); + }; + + napi_value recv = nullptr; + napi_create_object(env_, &recv); + napi_status wrapret = napi_wrap(env_, recv, jsUIExtensionContext_.get(), + [](napi_env env, void* data, void* hint) {}, nullptr, nullptr); + EXPECT_EQ(wrapret, napi_ok); + + // Create ability result + napi_value resultObject = nullptr; + napi_create_object(env_, &resultObject); + napi_value resultCode = nullptr; + napi_create_int32(env_, 0, &resultCode); + napi_set_named_property(env_, resultObject, "resultCode", resultCode); + AAFwk::Want want; + napi_value jsWant = AppExecFwk::CreateJsWant(env_, want); + napi_set_named_property(env_, resultObject, "want", jsWant); + + // Create callback + napi_value callbackFunc = nullptr; + napi_create_function(env_, "callback", NAPI_AUTO_LENGTH, + [](napi_env env, napi_callback_info info) -> napi_value { + napi_value result = nullptr; + napi_get_undefined(env, &result); + return result; + }, nullptr, &callbackFunc); + + napi_value funcValue = nullptr; + napi_create_function(env_, "terminateSelfWithResult", NAPI_AUTO_LENGTH, func, nullptr, &funcValue); + napi_value funcResultValue = nullptr; + napi_value argv[] = { resultObject, callbackFunc }; + napi_status status = napi_call_function(env_, recv, funcValue, ARGC_TWO, argv, &funcResultValue); + EXPECT_EQ(status, napi_ok); + + ArkNativeEngine* engine = (ArkNativeEngine*)env_; + uv_loop_t* loop = engine->GetUVLoop(); + RunNowait(loop); + RunNowait(loop); + + EXPECT_FALSE(tryCatch.HasCaught()); + if (tryCatch.HasCaught()) { + tryCatch.ClearException(); + } + if (!engine->lastException_.IsEmpty()) { + engine->lastException_.Empty(); + } + GTEST_LOG_(INFO) << "TerminateSelfWithResultNonEmbeddable_0200 end"; +} + +// HandleTerminateSelfInEmbeddableMode: context valid, embeddable with callback + +HWTEST_F(UIExtensionContextTest, AbilityRuntime_UIExtensionContext_HandleTerminateSelfEmbeddable_0300, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "HandleTerminateSelfEmbeddable_0300 start"; + HandleScope handleScope(env_); + TryCatch tryCatch(env_); + + // Set embeddable screen mode + abilityContextImpl_->SetScreenMode(1); // EMBEDDED_FULL_SCREEN_MODE + + napi_callback func = [](napi_env env, napi_callback_info info) -> napi_value { + return JsUIExtensionContext::TerminateSelf(env, info); + }; + + napi_value recv = nullptr; + napi_create_object(env_, &recv); + napi_status wrapret = napi_wrap(env_, recv, jsUIExtensionContext_.get(), + [](napi_env env, void* data, void* hint) {}, nullptr, nullptr); + EXPECT_EQ(wrapret, napi_ok); + + // Create callback + napi_value callbackFunc = nullptr; + napi_create_function(env_, "callback", NAPI_AUTO_LENGTH, + [](napi_env env, napi_callback_info info) -> napi_value { + napi_value result = nullptr; + napi_get_undefined(env, &result); + return result; + }, nullptr, &callbackFunc); + + napi_value funcValue = nullptr; + napi_create_function(env_, "terminateSelf", NAPI_AUTO_LENGTH, func, nullptr, &funcValue); + napi_value funcResultValue = nullptr; + napi_value argv[] = { callbackFunc }; + napi_status status = napi_call_function(env_, recv, funcValue, ARGC_ONE, argv, &funcResultValue); + EXPECT_EQ(status, napi_ok); + EXPECT_NE(funcResultValue, nullptr); + + ArkNativeEngine* engine = (ArkNativeEngine*)env_; + uv_loop_t* loop = engine->GetUVLoop(); + RunNowait(loop); + + EXPECT_FALSE(tryCatch.HasCaught()); + if (tryCatch.HasCaught()) { + tryCatch.ClearException(); + } + if (!engine->lastException_.IsEmpty()) { + engine->lastException_.Empty(); + } + GTEST_LOG_(INFO) << "HandleTerminateSelfEmbeddable_0300 end"; +} + +// ==================== HandleTerminateSelfWithResultInEmbeddableMode: context null path ==================== + +HWTEST_F(UIExtensionContextTest, TerminateSelfWithResultEmbeddable_0300, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "TerminateSelfWithResultEmbeddable_0300 start"; + HandleScope handleScope(env_); + TryCatch tryCatch(env_); + + // Create context with embeddable mode then release it + std::shared_ptr ctx = std::make_shared(); + ctx->SetScreenMode(1); // EMBEDDED_FULL_SCREEN_MODE + auto jsCtx = std::make_shared(ctx); + ctx.reset(); // release, weak_ptr expires + + napi_callback func = [](napi_env env, napi_callback_info info) -> napi_value { + return JsUIExtensionContext::TerminateSelfWithResult(env, info); + }; + + napi_value recv = nullptr; + napi_create_object(env_, &recv); + napi_status wrapret = napi_wrap(env_, recv, jsCtx.get(), + [](napi_env env, void* data, void* hint) {}, nullptr, nullptr); + EXPECT_EQ(wrapret, napi_ok); + + // Create ability result + napi_value resultObject = nullptr; + napi_create_object(env_, &resultObject); + napi_value resultCode = nullptr; + napi_create_int32(env_, 0, &resultCode); + napi_set_named_property(env_, resultObject, "resultCode", resultCode); + AAFwk::Want want; + napi_value jsWant = AppExecFwk::CreateJsWant(env_, want); + napi_set_named_property(env_, resultObject, "want", jsWant); + + napi_value funcValue = nullptr; + napi_create_function(env_, "terminateSelfWithResult", NAPI_AUTO_LENGTH, func, nullptr, &funcValue); + napi_value funcResultValue = nullptr; + napi_value argv[] = { resultObject }; + napi_status status = napi_call_function(env_, recv, funcValue, ARGC_ONE, argv, &funcResultValue); + EXPECT_EQ(status, napi_ok); + + ArkNativeEngine* engine = (ArkNativeEngine*)env_; + uv_loop_t* loop = engine->GetUVLoop(); + RunNowait(loop); + + // Context is expired, goes through NonEmbeddable path which will also get null context + EXPECT_FALSE(tryCatch.HasCaught()); + if (tryCatch.HasCaught()) { + tryCatch.ClearException(); + } + if (!engine->lastException_.IsEmpty()) { + engine->lastException_.Empty(); + } + GTEST_LOG_(INFO) << "TerminateSelfWithResultEmbeddable_0300 end"; +} } // namespace AAFwk } // namespace OHOS \ No newline at end of file diff --git a/test/unittest/modular_object_connect_test/BUILD.gn b/test/unittest/modular_object_connect_test/BUILD.gn new file mode 100644 index 0000000000..43c10f9435 --- /dev/null +++ b/test/unittest/modular_object_connect_test/BUILD.gn @@ -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. + +import("//build/test.gni") +import("//foundation/ability/ability_runtime/ability_runtime.gni") + +module_output_path = "ability_runtime/ability_runtime/abilitymgr" + +ohos_unittest("modular_object_connect_test") { + module_out_path = module_output_path + sanitize = { + cfi = true + cfi_cross_dso = true + debug = false + blocklist = "../../cfi_blocklist.txt" + } + branch_protector_ret = "pac_ret" + + include_dirs = [ + "${ability_runtime_test_path}/mock/services_abilitymgr_test/libs/system_ability_mock", + "${ability_runtime_test_path}/mock/frameworks_kits_ability_native_test/include", + "${ability_runtime_test_path}/mock/mock_sa_call", + "${ability_runtime_test_path}/mock/task_handler_wrap_mock/include", + "${ability_runtime_services_path}/abilitymgr/include/modular_object", + "${ability_runtime_path}/interfaces/inner_api/ability_manager/include", + ] + + sources = [ + # add mock file + "${ability_runtime_test_path}/mock/services_abilitymgr_test/libs/appexecfwk_core/src/appmgr/mock_app_scheduler.cpp", + "${ability_runtime_test_path}/mock/task_handler_wrap_mock/src/mock_task_handler_wrap.cpp", + "mock_modular_object_manager.cpp", + "modular_object_connect_test.cpp", + ] + + configs = [ + "${ability_runtime_services_path}/abilitymgr:abilityms_config", + "${ability_runtime_test_path}/mock/services_abilitymgr_test:aafwk_mock_config", + ] + cflags = [ + "-Dprivate=public", + "-Dprotected=public", + ] + if (target_cpu == "arm") { + cflags += [ "-DBINDER_IPC_32BIT" ] + } + deps = [ + "${ability_runtime_innerkits_path}/ability_manager:ability_connect_callback_stub", + "${ability_runtime_innerkits_path}/ability_manager:ability_manager", + "${ability_runtime_innerkits_path}/deps_wrapper:ability_deps_wrapper", + "${ability_runtime_native_path}/ability/native:abilitykit_native", + "${ability_runtime_services_path}/abilitymgr:abilityms", + "${ability_runtime_services_path}/common:perm_verification", + "${ability_runtime_services_path}/common:task_handler_wrap", + "${ability_runtime_path}/utils/server/startup:startup_util", + "${ability_runtime_test_path}/mock/services_abilitymgr_test/libs/aakit:aakit_mock", + "${ability_runtime_test_path}/mock/services_abilitymgr_test/libs/appexecfwk_core:appexecfwk_appmgr_mock", + "${ability_runtime_test_path}/mock/services_abilitymgr_test/libs/appexecfwk_core:appexecfwk_bundlemgr_mock", + ] + + external_deps = [ + "ability_base:want", + "ability_base:zuri", + "access_token:libaccesstoken_sdk", + "access_token:libnativetoken", + "access_token:libtoken_setproc", + "cJSON:cjson", + "c_utils:utils", + "common_event_service:cesfwk_innerkits", + "ffrt:libffrt", + "googletest:gmock_main", + "googletest:gtest_main", + "hilog:libhilog", + "hisysevent:libhisysevent", + "init:libbeget_proxy", + "ipc:ipc_core", + "napi:ace_napi", + "safwk:system_ability_fwk", + "samgr:samgr_proxy", + "selinux_adapter:librestorecon", + ] + + if (background_task_mgr_continuous_task_enable) { + external_deps += [ "background_task_mgr:bgtaskmgr_innerkits" ] + } +} + +group("unittest") { + testonly = true + deps = [ ":modular_object_connect_test" ] +} diff --git a/test/unittest/modular_object_connect_test/mock_modular_object_manager.cpp b/test/unittest/modular_object_connect_test/mock_modular_object_manager.cpp new file mode 100644 index 0000000000..fe4e981db5 --- /dev/null +++ b/test/unittest/modular_object_connect_test/mock_modular_object_manager.cpp @@ -0,0 +1,59 @@ +/* + * 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 "modular_object_manager.h" + +#include "modular_object_extension_info.h" + +// Global mock config for test control (in global namespace for extern access from test) +static std::vector g_mockConfigs; +static bool g_mockReturnError = false; + +void SetMockModularObjectConfigs(const std::vector &configs) +{ + g_mockConfigs = configs; + g_mockReturnError = false; +} + +void SetMockModularObjectConfigError() +{ + g_mockConfigs.clear(); + g_mockReturnError = true; +} + +void ClearMockModularObjectConfig() +{ + g_mockConfigs.clear(); + g_mockReturnError = false; +} + +namespace OHOS { +namespace AbilityRuntime { + +ModularObjectManager::ModularObjectManager() = default; +ModularObjectManager::~ModularObjectManager() = default; + +int32_t ModularObjectManager::QuerySelfModularObjectExtensionInfos(int32_t userId, const std::string &bundleName, + int32_t appIndex, std::vector &infos) +{ + if (g_mockReturnError) { + return -1; + } + infos = g_mockConfigs; + return 0; +} + +} // namespace AbilityRuntime +} // namespace OHOS diff --git a/test/unittest/modular_object_connect_test/modular_object_connect_test.cpp b/test/unittest/modular_object_connect_test/modular_object_connect_test.cpp new file mode 100644 index 0000000000..827b8bd5c7 --- /dev/null +++ b/test/unittest/modular_object_connect_test/modular_object_connect_test.cpp @@ -0,0 +1,698 @@ +/* + * 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 +#include +#include + +#include "ability_connect_manager.h" +#include "caller_info.h" +#include "connection_record.h" + +#include "hilog_tag_wrapper.h" +#include "mock_ability_connect_callback.h" +#include "mock_sa_call.h" +#include "mock_task_handler_wrap.h" +#include "modular_object_extension_info.h" +#include "modular_object_utils.h" + +using namespace testing::ext; +using namespace OHOS::AppExecFwk; + +// Declare mock control functions (defined in mock_modular_object_manager.cpp) +extern void ClearMockModularObjectConfig(); +extern void SetMockModularObjectConfigs( + const std::vector &configs); +extern void SetMockModularObjectConfigError(); + +namespace OHOS { +namespace AAFwk { +namespace { +const std::string TEST_BUNDLE = "com.test.modular"; +const std::string TEST_MODULE = "entry"; +const std::string TEST_ABILITY = "TestModularObjectExt"; +const std::string TEST_DEVICE = "device"; +const std::string TEST_APP = "testApp"; +} + +class ModularObjectConnectTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); + + AbilityRequest MakeModularObjectRequest(const std::string &bundleName = TEST_BUNDLE, + const std::string &abilityName = TEST_ABILITY); + + std::shared_ptr connectManager_; +}; + +void ModularObjectConnectTest::SetUpTestCase(void) {} +void ModularObjectConnectTest::TearDownTestCase(void) {} + +void ModularObjectConnectTest::SetUp() +{ + connectManager_ = std::make_shared(0); + auto taskHandler = MockTaskHandlerWrap::CreateQueueHandler("ModularObjectConnectTest"); + connectManager_->SetTaskHandler(taskHandler); +} + +void ModularObjectConnectTest::TearDown() +{ + connectManager_ = nullptr; + ClearMockModularObjectConfig(); +} + +AbilityRequest ModularObjectConnectTest::MakeModularObjectRequest( + const std::string &bundleName, const std::string &abilityName) +{ + AbilityRequest request; + ElementName element(TEST_DEVICE, bundleName, TEST_MODULE, abilityName); + request.want.SetElement(element); + request.abilityInfo.name = abilityName; + request.abilityInfo.bundleName = bundleName; + request.abilityInfo.moduleName = TEST_MODULE; + request.abilityInfo.type = AbilityType::EXTENSION; + request.abilityInfo.extensionAbilityType = AppExecFwk::ExtensionAbilityType::MODULAR_OBJECT; + request.abilityInfo.deviceId = TEST_DEVICE; + request.abilityInfo.applicationInfo.bundleName = bundleName; + request.abilityInfo.applicationInfo.name = TEST_APP; + request.appInfo = request.abilityInfo.applicationInfo; + return request; +} + +// Every connect generates a unique key via RequestIdUtil + +/** + * @tc.name: GetServiceKey_ModularObject_001 + * @tc.desc: Test MODULAR_OBJECT type generates unique key with requestId suffix + * @tc.type: FUNC + */ +HWTEST_F(ModularObjectConnectTest, GetServiceKey_ModularObject_001, TestSize.Level1) +{ + auto request = MakeModularObjectRequest(); + std::string key1 = AbilityConnectManager::GetServiceKey(request); + std::string key2 = AbilityConnectManager::GetServiceKey(request); + // Each call generates a unique requestId, so keys should differ + EXPECT_NE(key1, key2); + // Both should contain the base URI prefix + EXPECT_NE(key1.find("com.test.modular"), std::string::npos); + EXPECT_NE(key2.find("com.test.modular"), std::string::npos); +} + +/** + * @tc.name: GetServiceKey_ModularObject_002 + * @tc.desc: Test MODULAR_OBJECT key format is baseUri_requestId + * @tc.type: FUNC + */ +HWTEST_F(ModularObjectConnectTest, GetServiceKey_ModularObject_002, TestSize.Level1) +{ + auto request = MakeModularObjectRequest(); + std::string key = AbilityConnectManager::GetServiceKey(request); + // Key should contain underscore separator between baseUri and requestId + auto pos = key.rfind('_'); + EXPECT_NE(pos, std::string::npos); + // requestId part should not be empty + EXPECT_LT(pos, key.size() - 1); +} + +// Verify processName is set correctly based on launchMode + processMode + +/** + * @tc.name: ProcessName_CrossProcess_Bundle_001 + * @tc.desc: Test CROSS_PROCESS BUNDLE mode sets processName to bundleName:extensionTypeName + * @tc.type: FUNC + */ +HWTEST_F(ModularObjectConnectTest, ProcessName_CrossProcess_Bundle_001, TestSize.Level1) +{ + std::string extensionTypeName = "modularObject"; + std::string process = TEST_BUNDLE + ":" + extensionTypeName; // BUNDLE mode + EXPECT_EQ(process, "com.test.modular:modularObject"); +} + +/** + * @tc.name: ProcessName_CrossProcess_Type_001 + * @tc.desc: Test CROSS_PROCESS TYPE mode sets processName to bundleName:abilityName + * @tc.type: FUNC + */ +HWTEST_F(ModularObjectConnectTest, ProcessName_CrossProcess_Type_001, TestSize.Level1) +{ + std::string process = TEST_BUNDLE + ":" + TEST_ABILITY; // TYPE mode + EXPECT_EQ(process, "com.test.modular:TestModularObjectExt"); +} + +/** + * @tc.name: ProcessName_CrossProcess_Instance_001 + * @tc.desc: Test CROSS_PROCESS INSTANCE mode sets unique processName with recordId + * @tc.type: FUNC + */ +HWTEST_F(ModularObjectConnectTest, ProcessName_CrossProcess_Instance_001, TestSize.Level1) +{ + int32_t recordId = 42; + std::string process = TEST_BUNDLE + ":" + TEST_ABILITY + ":" + std::to_string(recordId); + EXPECT_EQ(process, "com.test.modular:TestModularObjectExt:42"); + // Different recordId → different processName → different process + int32_t recordId2 = 43; + std::string process2 = TEST_BUNDLE + ":" + TEST_ABILITY + ":" + std::to_string(recordId2); + EXPECT_NE(process, process2); +} + +// Verify thread keys follow BUNDLE/TYPE/INSTANCE patterns + +/** + * @tc.name: ThreadKey_Bundle_001 + * @tc.desc: Test BUNDLE threadMode uses bundleName as key + * @tc.type: FUNC + */ +HWTEST_F(ModularObjectConnectTest, ThreadKey_Bundle_001, TestSize.Level1) +{ + std::string threadKey = TEST_BUNDLE; // BUNDLE mode + EXPECT_EQ(threadKey, TEST_BUNDLE); +} + +/** + * @tc.name: ThreadKey_Type_001 + * @tc.desc: Test TYPE threadMode uses bundleName_abilityName as key + * @tc.type: FUNC + */ +HWTEST_F(ModularObjectConnectTest, ThreadKey_Type_001, TestSize.Level1) +{ + std::string threadKey = TEST_BUNDLE + "_" + TEST_ABILITY; // TYPE mode + EXPECT_EQ(threadKey, "com.test.modular_TestModularObjectExt"); +} + +/** + * @tc.name: ThreadKey_Instance_001 + * @tc.desc: Test INSTANCE threadMode uses bundleName_abilityName_atomicId as key + * @tc.type: FUNC + */ +HWTEST_F(ModularObjectConnectTest, ThreadKey_Instance_001, TestSize.Level1) +{ + uint32_t instanceId = 7; + std::string threadKey = TEST_BUNDLE + "_" + TEST_ABILITY + "_" + std::to_string(instanceId); + EXPECT_EQ(threadKey, "com.test.modular_TestModularObjectExt_7"); + // Different instanceId → different key → different thread + uint32_t instanceId2 = 8; + std::string threadKey2 = TEST_BUNDLE + "_" + TEST_ABILITY + "_" + std::to_string(instanceId2); + EXPECT_NE(threadKey, threadKey2); +} + +/** + * @tc.name: ThreadKey_Type_Reuse_001 + * @tc.desc: Test TYPE mode same abilityName produces same key (thread reuse) + * @tc.type: FUNC + */ +HWTEST_F(ModularObjectConnectTest, ThreadKey_Type_Reuse_001, TestSize.Level1) +{ + // Same bundle + ability → same key → thread reuse + std::string key1 = TEST_BUNDLE + "_" + TEST_ABILITY; + std::string key2 = TEST_BUNDLE + "_" + TEST_ABILITY; + EXPECT_EQ(key1, key2); +} + +/** + * @tc.name: ThreadKey_Type_DifferentAbility_001 + * @tc.desc: Test TYPE mode different abilityName produces different key + * @tc.type: FUNC + */ +HWTEST_F(ModularObjectConnectTest, ThreadKey_Type_DifferentAbility_001, TestSize.Level1) +{ + std::string key1 = TEST_BUNDLE + "_AbilityA"; + std::string key2 = TEST_BUNDLE + "_AbilityB"; + EXPECT_NE(key1, key2); +} + +/** + * @tc.name: ThreadKey_Bundle_Reuse_001 + * @tc.desc: Test BUNDLE mode different abilities same bundle produce same key + * @tc.type: FUNC + */ +HWTEST_F(ModularObjectConnectTest, ThreadKey_Bundle_Reuse_001, TestSize.Level1) +{ + std::string key1 = TEST_BUNDLE; // AbilityA + std::string key2 = TEST_BUNDLE; // AbilityB + EXPECT_EQ(key1, key2); // Same bundle → same thread +} + +// Tests that requestId stored in BaseExtensionRecord can reconstruct full serviceKey + +/** + * @tc.name: RequestId_Reconstruction_001 + * @tc.desc: Test requestId in record can reconstruct full serviceKey + * @tc.type: FUNC + */ +HWTEST_F(ModularObjectConnectTest, RequestId_Reconstruction_001, TestSize.Level1) +{ + // Simulate: connect creates key = baseUri + "_" + requestId + std::string baseUri = "device#com.test.modular#TestModularObjectExt#entry"; + std::string requestId = "12345"; + std::string fullKey = baseUri + "_" + requestId; + + // Simulate: disconnect reconstructs from record member + // GetURI() returns baseUri, GetRequestId() returns requestId + std::string reconstructedUri = baseUri; + std::string reconstructedRequestId = requestId; + std::string reconstructedKey = reconstructedUri + "_" + reconstructedRequestId; + EXPECT_EQ(fullKey, reconstructedKey); +} + +/** + * @tc.name: RequestId_Reconstruction_002 + * @tc.desc: Test empty requestId does not append underscore + * @tc.type: FUNC + */ +HWTEST_F(ModularObjectConnectTest, RequestId_Reconstruction_002, TestSize.Level1) +{ + std::string baseUri = "device#com.test.modular#TestModularObjectExt#entry"; + std::string requestId = ""; // empty + std::string key = baseUri; + if (!requestId.empty()) { + key = key + "_" + requestId; + } + EXPECT_EQ(key, baseUri); +} + +/** + * @tc.name: RequestId_SetGet_001 + * @tc.desc: Test SetRequestId and GetRequestId + * @tc.type: FUNC + */ +HWTEST_F(ModularObjectConnectTest, RequestId_SetGet_001, TestSize.Level1) +{ + auto request = MakeModularObjectRequest(); + auto service = BaseExtensionRecord::CreateBaseExtensionRecord(request); + ASSERT_NE(service, nullptr); + + // Default is empty + EXPECT_EQ(service->GetRequestId(), ""); + + // Set and get + service->SetRequestId("99999"); + EXPECT_EQ(service->GetRequestId(), "99999"); +} + + +/** + * @tc.name: ModularObjectExtensionInfo_001 + * @tc.desc: Test default values of ModularObjectExtensionInfo + * @tc.type: FUNC + */ +HWTEST_F(ModularObjectConnectTest, ModularObjectExtensionInfo_001, TestSize.Level1) +{ + ModularObjectExtensionInfo info; + EXPECT_EQ(info.launchMode, MoeLaunchMode::IN_PROCESS); + EXPECT_EQ(info.processMode, MoeProcessMode::BUNDLE); + EXPECT_EQ(info.threadMode, MoeThreadMode::BUNDLE); + EXPECT_FALSE(info.isDisabled); + EXPECT_EQ(info.appIndex, 0); + EXPECT_TRUE(info.bundleName.empty()); + EXPECT_TRUE(info.abilityName.empty()); +} + +/** + * @tc.name: ModularObjectExtensionInfo_002 + * @tc.desc: Test setting and reading all fields + * @tc.type: FUNC + */ +HWTEST_F(ModularObjectConnectTest, ModularObjectExtensionInfo_002, TestSize.Level1) +{ + ModularObjectExtensionInfo info; + info.bundleName = TEST_BUNDLE; + info.moduleName = TEST_MODULE; + info.abilityName = TEST_ABILITY; + info.appIndex = 1; + info.launchMode = MoeLaunchMode::CROSS_PROCESS; + info.processMode = MoeProcessMode::INSTANCE; + info.threadMode = MoeThreadMode::TYPE; + info.isDisabled = true; + + EXPECT_EQ(info.bundleName, TEST_BUNDLE); + EXPECT_EQ(info.moduleName, TEST_MODULE); + EXPECT_EQ(info.abilityName, TEST_ABILITY); + EXPECT_EQ(info.appIndex, 1); + EXPECT_EQ(info.launchMode, MoeLaunchMode::CROSS_PROCESS); + EXPECT_EQ(info.processMode, MoeProcessMode::INSTANCE); + EXPECT_EQ(info.threadMode, MoeThreadMode::TYPE); + EXPECT_TRUE(info.isDisabled); +} + + +/** + * @tc.name: EnumBoundary_001 + * @tc.desc: Test MoeLaunchMode enum values + * @tc.type: FUNC + */ +HWTEST_F(ModularObjectConnectTest, EnumBoundary_001, TestSize.Level1) +{ + EXPECT_EQ(static_cast(MoeLaunchMode::IN_PROCESS), 0); + EXPECT_EQ(static_cast(MoeLaunchMode::CROSS_PROCESS), 1); +} + +/** + * @tc.name: EnumBoundary_002 + * @tc.desc: Test MoeProcessMode enum values + * @tc.type: FUNC + */ +HWTEST_F(ModularObjectConnectTest, EnumBoundary_002, TestSize.Level1) +{ + EXPECT_EQ(static_cast(MoeProcessMode::BUNDLE), 0); + EXPECT_EQ(static_cast(MoeProcessMode::TYPE), 1); + EXPECT_EQ(static_cast(MoeProcessMode::INSTANCE), 2); +} + +/** + * @tc.name: EnumBoundary_003 + * @tc.desc: Test MoeThreadMode enum values + * @tc.type: FUNC + */ +HWTEST_F(ModularObjectConnectTest, EnumBoundary_003, TestSize.Level1) +{ + EXPECT_EQ(static_cast(MoeThreadMode::BUNDLE), 0); + EXPECT_EQ(static_cast(MoeThreadMode::TYPE), 1); + EXPECT_EQ(static_cast(MoeThreadMode::INSTANCE), 2); +} + +/** + * @tc.name: GetOrCreateServiceRecord_001 + * @tc.desc: Test creating record for MODULAR_OBJECT without DB config + * @tc.type: FUNC + */ +HWTEST_F(ModularObjectConnectTest, GetOrCreateServiceRecord_001, TestSize.Level1) +{ + ModularObjectExtensionInfo info; + info.bundleName = TEST_BUNDLE; + info.abilityName = TEST_ABILITY; + info.launchMode = MoeLaunchMode::CROSS_PROCESS; + info.processMode = MoeProcessMode::BUNDLE; + SetMockModularObjectConfigs({info}); + + auto request = MakeModularObjectRequest(); + std::shared_ptr targetService = nullptr; + bool isLoadedAbility = false; + + connectManager_->GetOrCreateServiceRecord(request, true, targetService, isLoadedAbility); + ASSERT_NE(targetService, nullptr); + EXPECT_FALSE(isLoadedAbility); +} + +/** + * @tc.name: GetOrCreateServiceRecord_002 + * @tc.desc: Test second call creates different record (unique serviceKey) + * @tc.type: FUNC + */ +HWTEST_F(ModularObjectConnectTest, GetOrCreateServiceRecord_002, TestSize.Level1) +{ + ModularObjectExtensionInfo info; + info.bundleName = TEST_BUNDLE; + info.abilityName = TEST_ABILITY; + info.launchMode = MoeLaunchMode::CROSS_PROCESS; + info.processMode = MoeProcessMode::BUNDLE; + SetMockModularObjectConfigs({info}); + + auto request1 = MakeModularObjectRequest(); + std::shared_ptr service1 = nullptr; + bool loaded1 = false; + connectManager_->GetOrCreateServiceRecord(request1, true, service1, loaded1); + ASSERT_NE(service1, nullptr); + EXPECT_FALSE(loaded1); + + auto request2 = MakeModularObjectRequest(); + std::shared_ptr service2 = nullptr; + bool loaded2 = false; + connectManager_->GetOrCreateServiceRecord(request2, true, service2, loaded2); + ASSERT_NE(service2, nullptr); + // MODULAR_OBJECT generates unique serviceKey each time via requestId, + // so second call creates a new record (isLoadedAbility = false) + EXPECT_FALSE(loaded2); + EXPECT_NE(service1, service2); +} + + +/** + * @tc.name: RemoveServiceFromMapSafe_001 + * @tc.desc: Test removing a service key that exists + * @tc.type: FUNC + */ +HWTEST_F(ModularObjectConnectTest, RemoveServiceFromMapSafe_001, TestSize.Level1) +{ + ModularObjectExtensionInfo info; + info.bundleName = TEST_BUNDLE; + info.abilityName = TEST_ABILITY; + info.launchMode = MoeLaunchMode::CROSS_PROCESS; + info.processMode = MoeProcessMode::BUNDLE; + SetMockModularObjectConfigs({info}); + + auto request = MakeModularObjectRequest(); + std::shared_ptr targetService = nullptr; + bool isLoadedAbility = false; + connectManager_->GetOrCreateServiceRecord(request, true, targetService, isLoadedAbility); + ASSERT_NE(targetService, nullptr); + + std::string serviceKey = connectManager_->GetServiceKey(request); + connectManager_->RemoveServiceFromMapSafe(serviceKey); + + auto found = connectManager_->GetServiceRecordByElementName(serviceKey); + EXPECT_EQ(found, nullptr); +} + +/** + * @tc.name: ThreeLayer_IN_PROCESS_TYPE_001 + * @tc.desc: Test IN_PROCESS + TYPE: same process, thread key = bundleName_abilityName + * @tc.type: FUNC + */ +HWTEST_F(ModularObjectConnectTest, ThreeLayer_IN_PROCESS_TYPE_001, TestSize.Level1) +{ + // Layer 2: processName + std::string processName = TEST_BUNDLE; + EXPECT_EQ(processName, TEST_BUNDLE); + + // Layer 3: thread key + std::string threadKey = TEST_BUNDLE + "_" + TEST_ABILITY; + EXPECT_EQ(threadKey, "com.test.modular_TestModularObjectExt"); +} + +/** + * @tc.name: ThreeLayer_CROSS_PROCESS_INSTANCE_INSTANCE_001 + * @tc.desc: Test CROSS_PROCESS + INSTANCE + INSTANCE: new process, new thread + * @tc.type: FUNC + */ +HWTEST_F(ModularObjectConnectTest, ThreeLayer_CROSS_PROCESS_INSTANCE_INSTANCE_001, TestSize.Level1) +{ + int32_t recordId1 = 100; + int32_t recordId2 = 101; + + // Layer 2: processName unique per record + std::string process1 = TEST_BUNDLE + ":" + TEST_ABILITY + ":" + std::to_string(recordId1); + std::string process2 = TEST_BUNDLE + ":" + TEST_ABILITY + ":" + std::to_string(recordId2); + EXPECT_NE(process1, process2); + + // Layer 3: thread key unique per instance + uint32_t instanceId1 = 10; + uint32_t instanceId2 = 11; + std::string thread1 = TEST_BUNDLE + "_" + TEST_ABILITY + "_" + std::to_string(instanceId1); + std::string thread2 = TEST_BUNDLE + "_" + TEST_ABILITY + "_" + std::to_string(instanceId2); + EXPECT_NE(thread1, thread2); +} + +/** + * @tc.name: ThreeLayer_CROSS_PROCESS_BUNDLE_BUNDLE_001 + * @tc.desc: Test CROSS_PROCESS + BUNDLE + BUNDLE: shared process (with extensionTypeName suffix), shared thread + * @tc.type: FUNC + */ +HWTEST_F(ModularObjectConnectTest, ThreeLayer_CROSS_PROCESS_BUNDLE_BUNDLE_001, TestSize.Level1) +{ + // Layer 2: same processName (bundleName:extensionTypeName, different from UIAbility) + std::string extensionTypeName = "modularObject"; + std::string process = TEST_BUNDLE + ":" + extensionTypeName; + EXPECT_EQ(process, "com.test.modular:modularObject"); + + // Layer 3: same thread key + std::string threadKey = TEST_BUNDLE; + EXPECT_EQ(threadKey, TEST_BUNDLE); +} + + +/** + * @tc.name: AtomicInstanceId_001 + * @tc.desc: Test atomic counter increments correctly + * @tc.type: FUNC + */ +HWTEST_F(ModularObjectConnectTest, AtomicInstanceId_001, TestSize.Level1) +{ + std::atomic counter{0}; + uint32_t id1 = counter.fetch_add(1); + uint32_t id2 = counter.fetch_add(1); + uint32_t id3 = counter.fetch_add(1); + EXPECT_EQ(id1, 0u); + EXPECT_EQ(id2, 1u); + EXPECT_EQ(id3, 2u); +} + +// Tests the call path: GetOrCreateServiceRecord → SetupNewRecord + +/** + * @tc.name: CheckModularObjectLimits_ShouldReturnOkWhenNotModularObject + * @tc.desc: Non-MODULAR_OBJECT type returns ERR_OK immediately + */ +HWTEST_F(ModularObjectConnectTest, + CheckModularObjectLimits_ShouldReturnOkWhenNotModularObject, TestSize.Level1) +{ + AbilityRequest request; + request.abilityInfo.extensionAbilityType = AppExecFwk::ExtensionAbilityType::SERVICE; + auto ret = connectManager_->CheckModularObjectLimits(request); + EXPECT_EQ(ret, ERR_OK); +} + +/** + * @tc.name: CheckModularObjectLimits_ShouldReturnOkWhenBelowLimits + * @tc.desc: Instance and connection counts below limits return ERR_OK + */ +HWTEST_F(ModularObjectConnectTest, + CheckModularObjectLimits_ShouldReturnOkWhenBelowLimits, TestSize.Level1) +{ + auto request = MakeModularObjectRequest(); + auto ret = connectManager_->CheckModularObjectLimits(request); + EXPECT_EQ(ret, ERR_OK); +} + +/** + * @tc.name: CheckModularObjectLimits_ShouldReturnErrorWhenInstanceLimitReached + * @tc.desc: Instance limit hit returns ERR_MOE_INSTANCE_LIMIT + */ +HWTEST_F(ModularObjectConnectTest, + CheckModularObjectLimits_ShouldReturnErrorWhenInstanceLimitReached, TestSize.Level1) +{ + auto request = MakeModularObjectRequest(); + std::string baseKey = std::string(TEST_DEVICE) + "/" + TEST_BUNDLE + "/" + TEST_MODULE + "/" + TEST_ABILITY; + for (int i = 0; i < 20; i++) { + auto service = BaseExtensionRecord::CreateBaseExtensionRecord(request); + ASSERT_NE(service, nullptr); + connectManager_->CallAddToServiceMap(baseKey + "_" + std::to_string(i), service); + } + auto ret = connectManager_->CheckModularObjectLimits(request); + EXPECT_EQ(ret, ERR_MOE_INSTANCE_LIMIT); +} + +/** + * @tc.name: CheckModularObjectLimits_ShouldReturnErrorWhenConnectionLimitReached + * @tc.desc: Connection limit hit returns ERR_MOE_CONNECTION_LIMIT via single-snapshot path + */ +HWTEST_F(ModularObjectConnectTest, + CheckModularObjectLimits_ShouldReturnErrorWhenConnectionLimitReached, TestSize.Level1) +{ + auto request = MakeModularObjectRequest(); + std::string baseKey = std::string(TEST_DEVICE) + "/" + TEST_BUNDLE + "/" + TEST_MODULE + "/" + TEST_ABILITY; + int32_t callingPid = static_cast(getpid()); + // 5 connections from callingPid across 5 service records → connection limit + for (int i = 0; i < 5; i++) { + auto service = BaseExtensionRecord::CreateBaseExtensionRecord(request); + ASSERT_NE(service, nullptr); + auto connRecord = ConnectionRecord::CreateConnectionRecord( + nullptr, service, nullptr, connectManager_); + ASSERT_NE(connRecord, nullptr); + auto callerInfo = std::make_shared(); + callerInfo->callerPid = callingPid; + connRecord->AttachCallerInfo(callerInfo); + service->AddConnectRecordToList(connRecord); + connectManager_->CallAddToServiceMap(baseKey + "_" + std::to_string(i), service); + } + auto ret = connectManager_->CheckModularObjectLimits(request); + EXPECT_EQ(ret, ERR_MOE_CONNECTION_LIMIT); +} + +/** + * @tc.name: CheckModularObjectLimits_ShouldReturnConnectionErrorViaSnapshot + * @tc.desc: CheckModularObjectLimits detects connection limit via single-snapshot path + */ +HWTEST_F(ModularObjectConnectTest, + CheckModularObjectLimits_ShouldReturnConnectionErrorViaSnapshot, TestSize.Level1) +{ + auto request = MakeModularObjectRequest(); + std::string baseKey = std::string(TEST_DEVICE) + "/" + TEST_BUNDLE + "/" + TEST_MODULE + "/" + TEST_ABILITY; + // Use getpid() to match IPCSkeleton::GetCallingPid() in test environment + int32_t callingPid = static_cast(getpid()); + // 5 connections from callingPid across 2 service records → connection limit + for (int batch = 0; batch < 2; batch++) { + auto service = BaseExtensionRecord::CreateBaseExtensionRecord(request); + ASSERT_NE(service, nullptr); + int count = (batch == 0) ? 3 : 2; + for (int i = 0; i < count; i++) { + auto connRecord = ConnectionRecord::CreateConnectionRecord( + nullptr, service, nullptr, connectManager_); + ASSERT_NE(connRecord, nullptr); + auto callerInfo = std::make_shared(); + callerInfo->callerPid = callingPid; + connRecord->AttachCallerInfo(callerInfo); + service->AddConnectRecordToList(connRecord); + } + connectManager_->CallAddToServiceMap( + baseKey + "_snap" + std::to_string(batch), service); + } + // CheckModularObjectLimits uses single-snapshot, should detect connection limit + auto ret = connectManager_->CheckModularObjectLimits(request); + EXPECT_EQ(ret, ERR_MOE_CONNECTION_LIMIT); +} + +/** + * @tc.name: HandleExtensionSetup_ShouldReturnOkWhenNotModularObject + * @tc.desc: Non-MODULAR_OBJECT type returns ERR_OK directly without calling SetupNewRecord + */ +HWTEST_F(ModularObjectConnectTest, + HandleExtensionSetup_ShouldReturnOkWhenNotModularObject, TestSize.Level1) +{ + AbilityRequest request; + request.abilityInfo.extensionAbilityType = AppExecFwk::ExtensionAbilityType::SERVICE; + auto service = BaseExtensionRecord::CreateBaseExtensionRecord(request); + auto ret = connectManager_->HandleExtensionSetup(request, service, "key_123"); + EXPECT_EQ(ret, ERR_OK); +} + +/** + * @tc.name: HandleExtensionSetup_ShouldDelegateToSetupNewRecordWhenModularObject + * @tc.desc: MODULAR_OBJECT delegates to SetupNewRecord and propagates success + */ +HWTEST_F(ModularObjectConnectTest, + HandleExtensionSetup_ShouldDelegateToSetupNewRecordWhenModularObject, TestSize.Level1) +{ + ModularObjectExtensionInfo info; + info.bundleName = TEST_BUNDLE; + info.abilityName = TEST_ABILITY; + info.launchMode = MoeLaunchMode::CROSS_PROCESS; + info.processMode = MoeProcessMode::BUNDLE; + SetMockModularObjectConfigs({info}); + + auto request = MakeModularObjectRequest(); + auto service = BaseExtensionRecord::CreateBaseExtensionRecord(request); + auto ret = connectManager_->HandleExtensionSetup(request, service, "key_456"); + EXPECT_EQ(ret, ERR_OK); +} + +/** + * @tc.name: HandleExtensionSetup_ShouldPropagateErrorWhenSetupNewRecordFails + * @tc.desc: MODULAR_OBJECT propagates SetupNewRecord failure + */ +HWTEST_F(ModularObjectConnectTest, + HandleExtensionSetup_ShouldPropagateErrorWhenSetupNewRecordFails, TestSize.Level1) +{ + SetMockModularObjectConfigError(); + + auto request = MakeModularObjectRequest(); + auto service = BaseExtensionRecord::CreateBaseExtensionRecord(request); + auto ret = connectManager_->HandleExtensionSetup(request, service, "key_789"); + EXPECT_NE(ret, ERR_OK); +} +} // AAFwk +} // OHOS \ No newline at end of file diff --git a/test/unittest/modular_object_connection_manager_test/modular_object_connection_manager_test.cpp b/test/unittest/modular_object_connection_manager_test/modular_object_connection_manager_test.cpp index cd7bc79fea..4a8730890d 100644 --- a/test/unittest/modular_object_connection_manager_test/modular_object_connection_manager_test.cpp +++ b/test/unittest/modular_object_connection_manager_test/modular_object_connection_manager_test.cpp @@ -67,7 +67,6 @@ public: void SetUp() override { AAFwk::AbilityManagerClient::Reset(); - // Clean up any leftover state by disconnecting auto &mgr = ModularObjectConnectionManager::GetInstance(); auto cb1 = sptr::MakeSptr(); mgr.DisconnectModularObjectExtension(cb1); @@ -75,6 +74,100 @@ public: void TearDown() override {} }; +// ==================== ModularObjectConnectionInfo::operator< ==================== + +HWTEST_F(ModularObjectConnectionManagerTest, OperatorLess_SameConnectionSameReceiver, TestSize.Level1) +{ + auto conn = sptr::MakeSptr(); + auto want = BuildWant("bundle", "module", "Ability"); + ModularObjectConnectionInfo infoA(conn, want.GetOperation()); + ModularObjectConnectionInfo infoB(conn, want.GetOperation()); + EXPECT_FALSE(infoA < infoB); + EXPECT_FALSE(infoB < infoA); +} + +HWTEST_F(ModularObjectConnectionManagerTest, OperatorLess_SameConnectionDifferentBundle, TestSize.Level1) +{ + auto conn = sptr::MakeSptr(); + ModularObjectConnectionInfo infoA(conn, BuildWant("bundleA", "module", "Ability").GetOperation()); + ModularObjectConnectionInfo infoB(conn, BuildWant("bundleB", "module", "Ability").GetOperation()); + EXPECT_TRUE(infoA < infoB); + EXPECT_FALSE(infoB < infoA); +} + +HWTEST_F(ModularObjectConnectionManagerTest, OperatorLess_SameConnectionDifferentModule, TestSize.Level1) +{ + auto conn = sptr::MakeSptr(); + ModularObjectConnectionInfo infoA(conn, BuildWant("bundle", "moduleA", "Ability").GetOperation()); + ModularObjectConnectionInfo infoB(conn, BuildWant("bundle", "moduleB", "Ability").GetOperation()); + EXPECT_TRUE(infoA < infoB); + EXPECT_FALSE(infoB < infoA); +} + +HWTEST_F(ModularObjectConnectionManagerTest, OperatorLess_SameConnectionDifferentAbility, TestSize.Level1) +{ + auto conn = sptr::MakeSptr(); + ModularObjectConnectionInfo infoA(conn, BuildWant("bundle", "module", "AbilityA").GetOperation()); + ModularObjectConnectionInfo infoB(conn, BuildWant("bundle", "module", "AbilityB").GetOperation()); + EXPECT_TRUE(infoA < infoB); + EXPECT_FALSE(infoB < infoA); +} + +HWTEST_F(ModularObjectConnectionManagerTest, OperatorLess_DifferentConnectionSameReceiver, TestSize.Level1) +{ + auto connA = sptr::MakeSptr(); + auto connB = sptr::MakeSptr(); + auto op = BuildWant("bundle", "module", "Ability").GetOperation(); + ModularObjectConnectionInfo infoA(connA, op); + ModularObjectConnectionInfo infoB(connB, op); + // Different pointers, same receiver — order determined by pointer alone + if (connA.GetRefPtr() < connB.GetRefPtr()) { + EXPECT_TRUE(infoA < infoB); + EXPECT_FALSE(infoB < infoA); + } else { + EXPECT_TRUE(infoB < infoA); + EXPECT_FALSE(infoA < infoB); + } +} + +HWTEST_F(ModularObjectConnectionManagerTest, OperatorLess_MixedConnAndAbilityNoViolation, TestSize.Level1) +{ + // This was the bug: conn and abilityName have conflicting ordering + // e.g. conn_A < conn_B but abilityB < abilityA + auto connA = sptr::MakeSptr(); + auto connB = sptr::MakeSptr(); + + // Ensure connA < connB by pointer value + auto ptrA = reinterpret_cast(connA.GetRefPtr()); + auto ptrB = reinterpret_cast(connB.GetRefPtr()); + auto lowConn = (ptrA < ptrB) ? connA : connB; + auto highConn = (ptrA < ptrB) ? connB : connA; + + // lowConn has lower pointer, but higher abilityName ("AbilityB") + // highConn has higher pointer, but lower abilityName ("AbilityA") + ModularObjectConnectionInfo infoLow(lowConn, BuildWant("bundle", "module", "AbilityB").GetOperation()); + ModularObjectConnectionInfo infoHigh(highConn, BuildWant("bundle", "module", "AbilityA").GetOperation()); + + // Must NOT have both a < b AND b < a + bool lowLT = infoLow < infoHigh; + bool highLT = infoHigh < infoLow; + EXPECT_FALSE(lowLT && highLT); +} + +HWTEST_F(ModularObjectConnectionManagerTest, OperatorLess_Transitivity, TestSize.Level1) +{ + auto conn = sptr::MakeSptr(); + ModularObjectConnectionInfo infoA(conn, BuildWant("bundle", "module", "A").GetOperation()); + ModularObjectConnectionInfo infoB(conn, BuildWant("bundle", "module", "B").GetOperation()); + ModularObjectConnectionInfo infoC(conn, BuildWant("bundle", "module", "C").GetOperation()); + EXPECT_TRUE(infoA < infoB); + EXPECT_TRUE(infoB < infoC); + EXPECT_TRUE(infoA < infoC); + EXPECT_FALSE(infoC < infoA); + EXPECT_FALSE(infoC < infoB); + EXPECT_FALSE(infoB < infoA); +} + // ==================== ConnectModularObjectExtension ==================== HWTEST_F(ModularObjectConnectionManagerTest, Connect_001, TestSize.Level1) diff --git a/test/unittest/modular_object_extension_ability_test/mock/include/ability_runtime_common.h b/test/unittest/modular_object_extension_ability_test/mock/include/ability_runtime_common.h index 38a42af5d9..8f60663e60 100644 --- a/test/unittest/modular_object_extension_ability_test/mock/include/ability_runtime_common.h +++ b/test/unittest/modular_object_extension_ability_test/mock/include/ability_runtime_common.h @@ -34,7 +34,7 @@ typedef enum { ABILITY_RUNTIME_ERROR_CODE_NO_RUNNING_ABILITIES_WITH_UI = 16000170, ABILITY_RUNTIME_ERROR_CODE_UPPER_RATE_LIMIT = 16000171, ABILITY_RUNTIME_ERROR_CODE_UPPER_CONNECTION_NUMBER_LIMIT = 16000172, - ABILITY_RUNTIME_ERROR_CODE_INVALID_DISTRIBUTION_TYPE = 16000176, + ABILITY_RUNTIME_ERROR_CODE_CROSS_APP_IN_PROCESS = 16000176, } AbilityRuntime_ErrorCode; typedef struct AbilityRuntime_Context *AbilityRuntime_ContextHandle; diff --git a/test/unittest/modular_object_extension_context_capi_test/mock/include/ability_runtime_common.h b/test/unittest/modular_object_extension_context_capi_test/mock/include/ability_runtime_common.h index 7466f2c085..c40f1f35d6 100644 --- a/test/unittest/modular_object_extension_context_capi_test/mock/include/ability_runtime_common.h +++ b/test/unittest/modular_object_extension_context_capi_test/mock/include/ability_runtime_common.h @@ -34,7 +34,7 @@ typedef enum { ABILITY_RUNTIME_ERROR_CODE_NO_RUNNING_ABILITIES_WITH_UI = 16000170, ABILITY_RUNTIME_ERROR_CODE_UPPER_RATE_LIMIT = 16000171, ABILITY_RUNTIME_ERROR_CODE_UPPER_CONNECTION_NUMBER_LIMIT = 16000172, - ABILITY_RUNTIME_ERROR_CODE_INVALID_DISTRIBUTION_TYPE = 16000176, + ABILITY_RUNTIME_ERROR_CODE_CROSS_APP_IN_PROCESS = 16000176, } AbilityRuntime_ErrorCode; typedef struct AbilityRuntime_Context *AbilityRuntime_ContextHandle; diff --git a/test/unittest/modular_object_extension_context_capi_test/mock/include/ipc_cparcel.h b/test/unittest/modular_object_extension_context_capi_test/mock/include/ipc_cparcel.h index f2220d18c0..d99a427425 100644 --- a/test/unittest/modular_object_extension_context_capi_test/mock/include/ipc_cparcel.h +++ b/test/unittest/modular_object_extension_context_capi_test/mock/include/ipc_cparcel.h @@ -16,6 +16,10 @@ #ifndef MOCK_IPC_CPARCEL_H #define MOCK_IPC_CPARCEL_H +struct OHIPCParcel { + int dummy; +}; + struct OHIPCRemoteStub { int dummy; }; diff --git a/test/unittest/modular_object_extension_context_capi_test/mock/include/ipc_cremote_object.h b/test/unittest/modular_object_extension_context_capi_test/mock/include/ipc_cremote_object.h new file mode 100644 index 0000000000..15a9b46e39 --- /dev/null +++ b/test/unittest/modular_object_extension_context_capi_test/mock/include/ipc_cremote_object.h @@ -0,0 +1,39 @@ +/* + * 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 MOCK_IPC_CREMOTE_OBJECT_H +#define MOCK_IPC_CREMOTE_OBJECT_H + +#include + +#include "ipc_cparcel.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef int (*OH_OnRemoteRequestCallback)(uint32_t code, const OHIPCParcel *data, + OHIPCParcel *reply, void *userData); +typedef void (*OH_OnRemoteDestroyCallback)(void *userData); + +OHIPCRemoteStub* OH_IPCRemoteStub_Create(const char *descriptor, OH_OnRemoteRequestCallback requestCallback, + OH_OnRemoteDestroyCallback destroyCallback, void *userData); +void OH_IPCRemoteStub_Destroy(OHIPCRemoteStub *stub); + +#ifdef __cplusplus +} +#endif + +#endif // MOCK_IPC_CREMOTE_OBJECT_H \ No newline at end of file diff --git a/cli_tool_framework/interfaces/cli_tool/IExecToolCallback.idl b/test/unittest/modular_object_extension_context_capi_test/mock/include/ipc_error_code.h similarity index 79% rename from cli_tool_framework/interfaces/cli_tool/IExecToolCallback.idl rename to test/unittest/modular_object_extension_context_capi_test/mock/include/ipc_error_code.h index dfeea8cac6..0b4e1359dd 100644 --- a/cli_tool_framework/interfaces/cli_tool/IExecToolCallback.idl +++ b/test/unittest/modular_object_extension_context_capi_test/mock/include/ipc_error_code.h @@ -13,8 +13,9 @@ * limitations under the License. */ -sequenceable CliSessionInfo..OHOS.CliTool.CliSessionInfo; +#ifndef MOCK_IPC_ERROR_CODE_H +#define MOCK_IPC_ERROR_CODE_H -interface OHOS.CliTool.IExecToolCallback { - [oneway] void SendResult([in] CliSessionInfo session); -} +constexpr int OH_IPC_INNER_ERROR = -1; + +#endif // MOCK_IPC_ERROR_CODE_H \ No newline at end of file diff --git a/test/unittest/modular_object_extension_context_capi_test/mock/include/mock_types.h b/test/unittest/modular_object_extension_context_capi_test/mock/include/mock_types.h index 7e2fc0f38e..71985cad4b 100644 --- a/test/unittest/modular_object_extension_context_capi_test/mock/include/mock_types.h +++ b/test/unittest/modular_object_extension_context_capi_test/mock/include/mock_types.h @@ -16,11 +16,29 @@ #ifndef MOCK_TYPES_H #define MOCK_TYPES_H +#include +#include + namespace OHOS { namespace AAFwk { class Want {}; class StartOptions {}; } // namespace AAFwk + +namespace AppExecFwk { +class EventHandler { +public: + bool PostSyncTask(const std::function &task, const char *name) + { + (void)name; + if (!task) { + return false; + } + task(); + return true; + } +}; +} // namespace AppExecFwk } // namespace OHOS #endif // MOCK_TYPES_H diff --git a/test/unittest/modular_object_extension_context_capi_test/mock/include/modular_object_extension_context.h b/test/unittest/modular_object_extension_context_capi_test/mock/include/modular_object_extension_context.h index 97bfcd5aac..978368bb54 100644 --- a/test/unittest/modular_object_extension_context_capi_test/mock/include/modular_object_extension_context.h +++ b/test/unittest/modular_object_extension_context_capi_test/mock/include/modular_object_extension_context.h @@ -17,6 +17,7 @@ #define MOCK_MODULAR_OBJECT_EXTENSION_CONTEXT_H #include "ability_runtime_common.h" +#include "ipc_cremote_object.h" struct AbilityBase_Want; typedef struct AbilityBase_Want AbilityBase_Want; @@ -42,6 +43,13 @@ AbilityRuntime_ErrorCode OH_AbilityRuntime_ModObjExtensionContext_StartSelfUIAbi AbilityRuntime_ErrorCode OH_AbilityRuntime_ModObjExtensionContext_TerminateSelf( OH_AbilityRuntime_ModObjExtensionContextHandle context); +OHIPCRemoteStub* OH_AbilityRuntime_ModObjExtensionContext_CreateIPCRemoteStub( + OH_AbilityRuntime_ModObjExtensionContextHandle context, const char *descriptor, + OH_OnRemoteRequestCallback requestCallback, OH_OnRemoteDestroyCallback destroyCallback, void *userData); + +void OH_AbilityRuntime_ModObjExtensionContext_DestroyIPCRemoteStub( + OH_AbilityRuntime_ModObjExtensionContextHandle context, OHIPCRemoteStub *stub); + #ifdef __cplusplus } #endif diff --git a/test/unittest/modular_object_extension_context_capi_test/mock/include/modular_object_extension_context_impl.h b/test/unittest/modular_object_extension_context_capi_test/mock/include/modular_object_extension_context_impl.h index e7afb09e60..b0a7c63027 100644 --- a/test/unittest/modular_object_extension_context_capi_test/mock/include/modular_object_extension_context_impl.h +++ b/test/unittest/modular_object_extension_context_capi_test/mock/include/modular_object_extension_context_impl.h @@ -29,6 +29,12 @@ public: static ErrCode g_startSelfWithOptionsResult; static ErrCode g_terminateResult; + std::shared_ptr GetEventHandler() const + { + static auto handler = std::make_shared(); + return handler; + } + ErrCode StartSelfUIAbility(const AAFwk::Want &want) const { return g_startSelfResult; } ErrCode StartSelfUIAbilityWithStartOptions(const AAFwk::Want &want, const AAFwk::StartOptions &options) const { return g_startSelfWithOptionsResult; } diff --git a/test/unittest/modular_object_extension_context_capi_test/modular_object_extension_context_capi_test.cpp b/test/unittest/modular_object_extension_context_capi_test/modular_object_extension_context_capi_test.cpp index 6d686834ab..7ee0fc3545 100644 --- a/test/unittest/modular_object_extension_context_capi_test/modular_object_extension_context_capi_test.cpp +++ b/test/unittest/modular_object_extension_context_capi_test/modular_object_extension_context_capi_test.cpp @@ -15,6 +15,8 @@ #include +#include + #include "modular_object_extension_context.h" #include "modular_object_extension_types.h" #include "modular_object_extension_context_impl.h" @@ -37,6 +39,33 @@ ErrCode ModularObjectExtensionContext::g_terminateResult = ERR_OK; int OHOS::AAFwk::CWantManager::g_transformResult = 0; AbilityRuntime_ErrorCode g_checkWantResult = ABILITY_RUNTIME_ERROR_CODE_NO_ERROR; +namespace { +bool g_remoteStubCreateSuccess = true; +int g_remoteStubDestroyCount = 0; +OHIPCRemoteStub *g_lastDestroyedStub = nullptr; +const char *g_lastCreateDescriptor = nullptr; +OH_OnRemoteRequestCallback g_lastRequestCallback = nullptr; +OH_OnRemoteDestroyCallback g_lastDestroyCallback = nullptr; +void *g_lastRemoteStubUserData = nullptr; +OHIPCRemoteStub g_mockRemoteStub {}; +} // namespace + +extern "C" OHIPCRemoteStub* OH_IPCRemoteStub_Create(const char *descriptor, + OH_OnRemoteRequestCallback requestCallback, OH_OnRemoteDestroyCallback destroyCallback, void *userData) +{ + g_lastCreateDescriptor = descriptor; + g_lastRequestCallback = requestCallback; + g_lastDestroyCallback = destroyCallback; + g_lastRemoteStubUserData = userData; + return g_remoteStubCreateSuccess ? &g_mockRemoteStub : nullptr; +} + +extern "C" void OH_IPCRemoteStub_Destroy(OHIPCRemoteStub *stub) +{ + g_lastDestroyedStub = stub; + ++g_remoteStubDestroyCount; +} + AbilityRuntime_ErrorCode CheckWant(AbilityBase_Want *want) { return g_checkWantResult; @@ -53,10 +82,33 @@ public: OHOS::AbilityRuntime::ModularObjectExtensionContext::g_terminateResult = ERR_OK; OHOS::AAFwk::CWantManager::g_transformResult = 0; g_checkWantResult = ABILITY_RUNTIME_ERROR_CODE_NO_ERROR; + g_remoteStubCreateSuccess = true; + g_remoteStubDestroyCount = 0; + g_lastDestroyedStub = nullptr; + g_lastCreateDescriptor = nullptr; + g_lastRequestCallback = nullptr; + g_lastDestroyCallback = nullptr; + g_lastRemoteStubUserData = nullptr; } void TearDown() override {} }; +namespace { +int MockRemoteRequest(uint32_t code, const OHIPCParcel *data, OHIPCParcel *reply, void *userData) +{ + (void)code; + (void)data; + (void)reply; + (void)userData; + return 0; +} + +void MockRemoteDestroy(void *userData) +{ + (void)userData; +} +} // namespace + // ==================== GetBaseContext ==================== HWTEST_F(ModularObjectExtensionContextCapiTest, GetBaseContext_NullContext_001, TestSize.Level1) @@ -326,3 +378,120 @@ HWTEST_F(ModularObjectExtensionContextCapiTest, TerminateSelf_Error_001, TestSiz EXPECT_EQ(ret, ABILITY_RUNTIME_ERROR_CODE_INTERNAL); GTEST_LOG_(INFO) << "TerminateSelf_Error_001 end"; } + +// ==================== CreateIPCRemoteStub ==================== + +HWTEST_F(ModularObjectExtensionContextCapiTest, CreateIPCRemoteStub_NullDescriptor_001, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "CreateIPCRemoteStub_NullDescriptor_001 start"; + auto stub = OH_AbilityRuntime_ModObjExtensionContext_CreateIPCRemoteStub( + nullptr, nullptr, MockRemoteRequest, MockRemoteDestroy, nullptr); + EXPECT_EQ(stub, nullptr); + GTEST_LOG_(INFO) << "CreateIPCRemoteStub_NullDescriptor_001 end"; +} + +HWTEST_F(ModularObjectExtensionContextCapiTest, CreateIPCRemoteStub_NullRequestCallback_001, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "CreateIPCRemoteStub_NullRequestCallback_001 start"; + auto stub = OH_AbilityRuntime_ModObjExtensionContext_CreateIPCRemoteStub( + nullptr, "descriptor", nullptr, MockRemoteDestroy, nullptr); + EXPECT_EQ(stub, nullptr); + GTEST_LOG_(INFO) << "CreateIPCRemoteStub_NullRequestCallback_001 end"; +} + +HWTEST_F(ModularObjectExtensionContextCapiTest, CreateIPCRemoteStub_WrongType_001, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "CreateIPCRemoteStub_WrongType_001 start"; + auto ctx = std::make_shared(); + ctx->type = OHOS::AppExecFwk::ExtensionAbilityType::SERVICE; + auto cppCtx = std::make_shared(); + ctx->context = cppCtx; + auto stub = OH_AbilityRuntime_ModObjExtensionContext_CreateIPCRemoteStub( + ctx.get(), "descriptor", MockRemoteRequest, MockRemoteDestroy, nullptr); + EXPECT_EQ(stub, nullptr); + GTEST_LOG_(INFO) << "CreateIPCRemoteStub_WrongType_001 end"; +} + +HWTEST_F(ModularObjectExtensionContextCapiTest, CreateIPCRemoteStub_ExpiredContext_001, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "CreateIPCRemoteStub_ExpiredContext_001 start"; + auto ctx = std::make_shared(); + ctx->type = OHOS::AppExecFwk::ExtensionAbilityType::MODULAR_OBJECT; + auto stub = OH_AbilityRuntime_ModObjExtensionContext_CreateIPCRemoteStub( + ctx.get(), "descriptor", MockRemoteRequest, MockRemoteDestroy, nullptr); + EXPECT_EQ(stub, nullptr); + GTEST_LOG_(INFO) << "CreateIPCRemoteStub_ExpiredContext_001 end"; +} + +HWTEST_F(ModularObjectExtensionContextCapiTest, CreateIPCRemoteStub_CreateFail_001, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "CreateIPCRemoteStub_CreateFail_001 start"; + auto ctx = std::make_shared(); + ctx->type = OHOS::AppExecFwk::ExtensionAbilityType::MODULAR_OBJECT; + auto cppCtx = std::make_shared(); + ctx->context = cppCtx; + g_remoteStubCreateSuccess = false; + auto stub = OH_AbilityRuntime_ModObjExtensionContext_CreateIPCRemoteStub( + ctx.get(), "descriptor", MockRemoteRequest, MockRemoteDestroy, nullptr); + EXPECT_EQ(stub, nullptr); + EXPECT_STREQ(g_lastCreateDescriptor, "descriptor"); + EXPECT_NE(g_lastRequestCallback, nullptr); + EXPECT_NE(g_lastDestroyCallback, nullptr); + EXPECT_NE(g_lastRemoteStubUserData, nullptr); + GTEST_LOG_(INFO) << "CreateIPCRemoteStub_CreateFail_001 end"; +} + +HWTEST_F(ModularObjectExtensionContextCapiTest, CreateIPCRemoteStub_Success_001, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "CreateIPCRemoteStub_Success_001 start"; + int userData = 42; + auto ctx = std::make_shared(); + ctx->type = OHOS::AppExecFwk::ExtensionAbilityType::MODULAR_OBJECT; + auto cppCtx = std::make_shared(); + ctx->context = cppCtx; + auto stub = OH_AbilityRuntime_ModObjExtensionContext_CreateIPCRemoteStub( + ctx.get(), "descriptor", MockRemoteRequest, MockRemoteDestroy, &userData); + EXPECT_EQ(stub, &g_mockRemoteStub); + EXPECT_STREQ(g_lastCreateDescriptor, "descriptor"); + EXPECT_NE(g_lastRequestCallback, nullptr); + EXPECT_NE(g_lastDestroyCallback, nullptr); + EXPECT_NE(g_lastRemoteStubUserData, nullptr); + GTEST_LOG_(INFO) << "CreateIPCRemoteStub_Success_001 end"; +} + +// ==================== DestroyIPCRemoteStub ==================== + +HWTEST_F(ModularObjectExtensionContextCapiTest, DestroyIPCRemoteStub_NullStub_001, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "DestroyIPCRemoteStub_NullStub_001 start"; + OH_AbilityRuntime_ModObjExtensionContext_DestroyIPCRemoteStub(nullptr, nullptr); + EXPECT_EQ(g_remoteStubDestroyCount, 1); + EXPECT_EQ(g_lastDestroyedStub, nullptr); + GTEST_LOG_(INFO) << "DestroyIPCRemoteStub_NullStub_001 end"; +} + +HWTEST_F(ModularObjectExtensionContextCapiTest, DestroyIPCRemoteStub_Success_001, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "DestroyIPCRemoteStub_Success_001 start"; + OHIPCRemoteStub stub {}; + OH_AbilityRuntime_ModObjExtensionContext_DestroyIPCRemoteStub(nullptr, &stub); + EXPECT_EQ(g_remoteStubDestroyCount, 1); + EXPECT_EQ(g_lastDestroyedStub, &stub); + GTEST_LOG_(INFO) << "DestroyIPCRemoteStub_Success_001 end"; +} + +HWTEST_F(ModularObjectExtensionContextCapiTest, DestroyIPCRemoteStub_ContextAndStubNotNull_001, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "DestroyIPCRemoteStub_ContextAndStubNotNull_001 start"; + auto ctx = std::make_shared(); + ctx->type = OHOS::AppExecFwk::ExtensionAbilityType::MODULAR_OBJECT; + auto cppCtx = std::make_shared(); + ctx->context = cppCtx; + OHIPCRemoteStub stub {}; + + OH_AbilityRuntime_ModObjExtensionContext_DestroyIPCRemoteStub(ctx.get(), &stub); + + EXPECT_EQ(g_remoteStubDestroyCount, 1); + EXPECT_EQ(g_lastDestroyedStub, &stub); + GTEST_LOG_(INFO) << "DestroyIPCRemoteStub_ContextAndStubNotNull_001 end"; +} diff --git a/test/unittest/modular_object_extension_context_impl_event_handler_test/BUILD.gn b/test/unittest/modular_object_extension_context_impl_event_handler_test/BUILD.gn new file mode 100644 index 0000000000..8c32a276ae --- /dev/null +++ b/test/unittest/modular_object_extension_context_impl_event_handler_test/BUILD.gn @@ -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/ohos.gni") +import("//build/test.gni") +import("//foundation/ability/ability_runtime/ability_runtime.gni") + +ohos_unittest("modular_object_extension_context_impl_event_handler_test") { + module_out_path = "ability_runtime/ability_runtime/modular_object_extension_context_impl_event_handler_test" + sanitize = { + cfi = true + cfi_cross_dso = true + debug = false + blocklist = "../../cfi_blocklist.txt" + } + sources = [ + "modular_object_extension_context_impl_event_handler_test.cpp", + "${ability_runtime_path}/frameworks/native/ability/native/modular_object_extension/modular_object_extension_context_impl.cpp", + ] + include_dirs = [ + "mock/include", + ] + external_deps = [ + "c_utils:utils", + "googletest:gtest_main", + "hilog:libhilog", + "hitrace:hitrace_meter", + "ipc:ipc_core", + ] +} + +group("unittest") { + testonly = true + deps = [ ":modular_object_extension_context_impl_event_handler_test" ] +} diff --git a/test/unittest/modular_object_extension_context_impl_event_handler_test/mock/include/ability_manager_client.h b/test/unittest/modular_object_extension_context_impl_event_handler_test/mock/include/ability_manager_client.h new file mode 100644 index 0000000000..84b78cbc51 --- /dev/null +++ b/test/unittest/modular_object_extension_context_impl_event_handler_test/mock/include/ability_manager_client.h @@ -0,0 +1,19 @@ +/* + * 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 MOCK_ABILITY_MANAGER_CLIENT_H +#define MOCK_ABILITY_MANAGER_CLIENT_H + +#endif // MOCK_ABILITY_MANAGER_CLIENT_H diff --git a/test/unittest/modular_object_extension_context_impl_event_handler_test/mock/include/errors.h b/test/unittest/modular_object_extension_context_impl_event_handler_test/mock/include/errors.h new file mode 100644 index 0000000000..e8e1ec698d --- /dev/null +++ b/test/unittest/modular_object_extension_context_impl_event_handler_test/mock/include/errors.h @@ -0,0 +1,23 @@ +/* + * 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 MOCK_ERRORS_H +#define MOCK_ERRORS_H + +#include +using ErrCode = int32_t; +constexpr ErrCode ERR_OK = 0; + +#endif // MOCK_ERRORS_H diff --git a/test/unittest/modular_object_extension_context_impl_event_handler_test/mock/include/extension_context.h b/test/unittest/modular_object_extension_context_impl_event_handler_test/mock/include/extension_context.h new file mode 100644 index 0000000000..99c299e09a --- /dev/null +++ b/test/unittest/modular_object_extension_context_impl_event_handler_test/mock/include/extension_context.h @@ -0,0 +1,112 @@ +/* + * 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 MOCK_EXTENSION_CONTEXT_H +#define MOCK_EXTENSION_CONTEXT_H + +#include +#include "iremote_broker.h" +#include "refbase.h" +#include "iremote_object.h" +#include "errors.h" + +namespace OHOS { +namespace AbilityRuntime { + +class Context : public std::enable_shared_from_this { +public: + Context() = default; + virtual ~Context() = default; +}; + +} // namespace AbilityRuntime + +namespace AppExecFwk { +class EventHandler : public std::enable_shared_from_this { +public: + virtual ~EventHandler() = default; +}; +} // namespace AppExecFwk + +namespace AAFwk { +class Want {}; + +class StartOptions {}; + +class AbilityManagerClient { +public: + static std::shared_ptr GetInstance() + { + static auto instance = std::make_shared(); + return instance; + } + + ErrCode StartSelfUIAbility(const Want &want) + { + return g_startSelfUIAbilityResult; + } + + ErrCode StartSelfUIAbilityWithToken(const Want &want, const sptr &token) + { + return g_startSelfUIAbilityResult; + } + + ErrCode StartSelfUIAbilityWithStartOptions(const Want &want, const StartOptions &options) + { + return g_startSelfUIAbilityWithStartOptionsResult; + } + + ErrCode StartSelfUIAbilityWithStartOptionsAndToken( + const Want &want, const StartOptions &options, const sptr &token) + { + return g_startSelfUIAbilityWithStartOptionsResult; + } + + ErrCode TerminateAbility(const sptr &token, int32_t resultCode, const Want *resultWant) + { + g_terminateCalled = true; + g_lastToken = token.GetRefPtr(); + return g_terminateResult; + } + + static ErrCode g_startSelfUIAbilityResult; + static ErrCode g_startSelfUIAbilityWithStartOptionsResult; + static ErrCode g_terminateResult; + static bool g_terminateCalled; + static IRemoteObject *g_lastToken; + + static void Reset() + { + g_startSelfUIAbilityResult = ERR_OK; + g_startSelfUIAbilityWithStartOptionsResult = ERR_OK; + g_terminateResult = ERR_OK; + g_terminateCalled = false; + g_lastToken = nullptr; + } +}; + +} // namespace AAFwk + +namespace AbilityRuntime { + +class ExtensionContext : public Context { +public: + sptr token_; +}; + +} // namespace AbilityRuntime +} // namespace OHOS + +#endif // MOCK_EXTENSION_CONTEXT_H diff --git a/test/unittest/modular_object_extension_context_impl_event_handler_test/mock/include/hilog_tag_wrapper.h b/test/unittest/modular_object_extension_context_impl_event_handler_test/mock/include/hilog_tag_wrapper.h new file mode 100644 index 0000000000..f4178e3fba --- /dev/null +++ b/test/unittest/modular_object_extension_context_impl_event_handler_test/mock/include/hilog_tag_wrapper.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2025 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_AAFWK_HILOG_TAG_WRAPPER_H +#define OHOS_AAFWK_HILOG_TAG_WRAPPER_H + +namespace OHOS::AAFwk { +enum class AAFwkLogTag : uint32_t { + DEFAULT = 0xD001300, +}; +} + +using AAFwkTag = OHOS::AAFwk::AAFwkLogTag; + +#define TAG_LOGD(tag, fmt, ...) ((void)0) +#define TAG_LOGI(tag, fmt, ...) ((void)0) +#define TAG_LOGW(tag, fmt, ...) ((void)0) +#define TAG_LOGE(tag, fmt, ...) ((void)0) +#define TAG_LOGF(tag, fmt, ...) ((void)0) + +#endif diff --git a/test/unittest/modular_object_extension_context_impl_event_handler_test/mock/include/hitrace_meter.h b/test/unittest/modular_object_extension_context_impl_event_handler_test/mock/include/hitrace_meter.h new file mode 100644 index 0000000000..0a3347899b --- /dev/null +++ b/test/unittest/modular_object_extension_context_impl_event_handler_test/mock/include/hitrace_meter.h @@ -0,0 +1,22 @@ +/* + * 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 MOCK_HITRACE_METER_H +#define MOCK_HITRACE_METER_H + +#define HITRACE_METER_NAME(tag, name) +#define HITRACE_TAG_ABILITY_MANAGER 0 + +#endif // MOCK_HITRACE_METER_H diff --git a/test/unittest/modular_object_extension_context_impl_event_handler_test/mock/include/modular_object_extension_context_impl.h b/test/unittest/modular_object_extension_context_impl_event_handler_test/mock/include/modular_object_extension_context_impl.h new file mode 100644 index 0000000000..ad0926e0fa --- /dev/null +++ b/test/unittest/modular_object_extension_context_impl_event_handler_test/mock/include/modular_object_extension_context_impl.h @@ -0,0 +1,53 @@ +/* + * 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 MOCK_MODULAR_OBJECT_EXTENSION_CONTEXT_IMPL_H +#define MOCK_MODULAR_OBJECT_EXTENSION_CONTEXT_IMPL_H + +#include +#include + +#include "extension_context.h" + +namespace OHOS { +namespace AbilityRuntime { + +class ModularObjectExtensionContext : public ExtensionContext { +public: + static const size_t CONTEXT_TYPE_ID; + + // Pure declarations — definitions come from real .cpp (linked via BUILD.gn) + void SetEventHandler(const std::shared_ptr &handler); + + std::shared_ptr GetEventHandler() const; + + // Pure declarations — definitions come from real .cpp (linked via BUILD.gn) + ErrCode StartSelfUIAbility(const AAFwk::Want &want) const; + + ErrCode StartSelfUIAbilityWithStartOptions(const AAFwk::Want &want, + const AAFwk::StartOptions &startOptions) const; + + ErrCode TerminateSelf(); + + bool IsContext(size_t contextTypeId) { return contextTypeId == CONTEXT_TYPE_ID; } + +private: + std::weak_ptr handler_; +}; + +} // namespace AbilityRuntime +} // namespace OHOS + +#endif // MOCK_MODULAR_OBJECT_EXTENSION_CONTEXT_IMPL_H diff --git a/test/unittest/modular_object_extension_context_impl_event_handler_test/mock/include/want.h b/test/unittest/modular_object_extension_context_impl_event_handler_test/mock/include/want.h new file mode 100644 index 0000000000..09040fc870 --- /dev/null +++ b/test/unittest/modular_object_extension_context_impl_event_handler_test/mock/include/want.h @@ -0,0 +1,26 @@ +/* + * 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 MOCK_WANT_H +#define MOCK_WANT_H + +typedef struct AbilityBase_Element { + char *bundleName; + char *moduleName; + char *abilityName; +} AbilityBase_Element; + +typedef struct AbilityBase_Want AbilityBase_Want; +#endif // MOCK_WANT_H diff --git a/test/unittest/modular_object_extension_context_impl_event_handler_test/modular_object_extension_context_impl_event_handler_test.cpp b/test/unittest/modular_object_extension_context_impl_event_handler_test/modular_object_extension_context_impl_event_handler_test.cpp new file mode 100644 index 0000000000..1d4d708060 --- /dev/null +++ b/test/unittest/modular_object_extension_context_impl_event_handler_test/modular_object_extension_context_impl_event_handler_test.cpp @@ -0,0 +1,113 @@ +/* + * 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 + +#include "modular_object_extension_context_impl.h" +#include "extension_context.h" + +using namespace testing::ext; + +namespace OHOS { +class MockRemoteObject : public IRemoteObject { +public: + explicit MockRemoteObject(std::u16string desc) : IRemoteObject(desc) {} + int GetObjectRefCount() override { return 1; } + int SendRequest(uint32_t code, MessageParcel &data, MessageParcel &reply, MessageOption &option) override + { + return 0; + } + bool AddDeathRecipient(const sptr &recipient) override { return true; } + bool RemoveDeathRecipient(const sptr &recipient) override { return true; } + int Dump(int fd, const std::vector &args) override { return 0; } +}; +} // namespace OHOS + +namespace OHOS { +namespace AbilityRuntime { + +class ModularObjectExtensionContextImplEventHandlerTest : public testing::Test { +public: + static void SetUpTestCase() {} + static void TearDownTestCase() {} + void SetUp() override {} + void TearDown() override {} +}; + +class MockEventHandler : public AppExecFwk::EventHandler { +}; + +// ==================== SetEventHandler / GetEventHandler ==================== +// These tests target the REAL implementation in: +// frameworks/native/ability/native/modular_object_extension/modular_object_extension_context_impl.cpp + +HWTEST_F(ModularObjectExtensionContextImplEventHandlerTest, GetEventHandler_DefaultNull_001, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "GetEventHandler_DefaultNull_001 start"; + auto context = std::make_shared(); + EXPECT_EQ(context->GetEventHandler(), nullptr); + GTEST_LOG_(INFO) << "GetEventHandler_DefaultNull_001 end"; +} + +HWTEST_F(ModularObjectExtensionContextImplEventHandlerTest, SetEventHandler_GetSameHandler_001, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "SetEventHandler_GetSameHandler_001 start"; + auto context = std::make_shared(); + auto handler = std::make_shared(); + context->SetEventHandler(handler); + EXPECT_EQ(context->GetEventHandler(), handler); + GTEST_LOG_(INFO) << "SetEventHandler_GetSameHandler_001 end"; +} + +HWTEST_F(ModularObjectExtensionContextImplEventHandlerTest, GetEventHandler_HandlerExpired_001, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "GetEventHandler_HandlerExpired_001 start"; + auto context = std::make_shared(); + auto handler = std::make_shared(); + context->SetEventHandler(handler); + handler.reset(); + EXPECT_EQ(context->GetEventHandler(), nullptr); + GTEST_LOG_(INFO) << "GetEventHandler_HandlerExpired_001 end"; +} + +HWTEST_F(ModularObjectExtensionContextImplEventHandlerTest, SetEventHandler_NullHandler_001, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "SetEventHandler_NullHandler_001 start"; + auto context = std::make_shared(); + context->SetEventHandler(nullptr); + EXPECT_EQ(context->GetEventHandler(), nullptr); + GTEST_LOG_(INFO) << "SetEventHandler_NullHandler_001 end"; +} + +HWTEST_F(ModularObjectExtensionContextImplEventHandlerTest, SetEventHandler_Overwrite_001, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "SetEventHandler_Overwrite_001 start"; + auto context = std::make_shared(); + auto handler1 = std::make_shared(); + auto handler2 = std::make_shared(); + + context->SetEventHandler(handler1); + EXPECT_EQ(context->GetEventHandler(), handler1); + + context->SetEventHandler(handler2); + EXPECT_EQ(context->GetEventHandler(), handler2); + + handler1.reset(); + EXPECT_NE(context->GetEventHandler(), nullptr); + GTEST_LOG_(INFO) << "SetEventHandler_Overwrite_001 end"; +} + +} // namespace AbilityRuntime +} // namespace OHOS diff --git a/test/unittest/modular_object_extension_context_impl_test/mock/include/extension_context.h b/test/unittest/modular_object_extension_context_impl_test/mock/include/extension_context.h index 3ec47f0f80..b245e41a4b 100644 --- a/test/unittest/modular_object_extension_context_impl_test/mock/include/extension_context.h +++ b/test/unittest/modular_object_extension_context_impl_test/mock/include/extension_context.h @@ -33,6 +33,13 @@ public: } // namespace AbilityRuntime +namespace AppExecFwk { +class EventHandler : public std::enable_shared_from_this { +public: + virtual ~EventHandler() = default; +}; +} // namespace AppExecFwk + namespace AAFwk { class Want {}; @@ -86,6 +93,13 @@ namespace AbilityRuntime { class ExtensionContext : public Context { public: sptr token_; + +protected: + virtual bool IsContext(size_t contextTypeId) + { + (void)contextTypeId; + return false; + } }; } // namespace AbilityRuntime diff --git a/test/unittest/modular_object_extension_context_impl_test/mock/include/modular_object_extension_context_impl.h b/test/unittest/modular_object_extension_context_impl_test/mock/include/modular_object_extension_context_impl.h index bfed546773..dfa3129491 100644 --- a/test/unittest/modular_object_extension_context_impl_test/mock/include/modular_object_extension_context_impl.h +++ b/test/unittest/modular_object_extension_context_impl_test/mock/include/modular_object_extension_context_impl.h @@ -26,6 +26,16 @@ class ModularObjectExtensionContext : public ExtensionContext { public: static const size_t CONTEXT_TYPE_ID; + void SetEventHandler(const std::shared_ptr &handler) + { + handler_ = handler; + } + + std::shared_ptr GetEventHandler() const + { + return handler_.lock(); + } + ErrCode StartSelfUIAbility(const AAFwk::Want &want) const { return AAFwk::AbilityManagerClient::GetInstance()->StartSelfUIAbility(want); @@ -43,6 +53,9 @@ public: } bool IsContext(size_t contextTypeId) { return contextTypeId == CONTEXT_TYPE_ID; } + +private: + std::weak_ptr handler_; }; } // namespace AbilityRuntime diff --git a/test/unittest/modular_object_extension_context_impl_test/modular_object_extension_context_impl_test.cpp b/test/unittest/modular_object_extension_context_impl_test/modular_object_extension_context_impl_test.cpp index 2ec8727b86..228b980f80 100644 --- a/test/unittest/modular_object_extension_context_impl_test/modular_object_extension_context_impl_test.cpp +++ b/test/unittest/modular_object_extension_context_impl_test/modular_object_extension_context_impl_test.cpp @@ -64,6 +64,40 @@ public: void TearDown() override {} }; +class MockEventHandler : public AppExecFwk::EventHandler { +}; + +// ==================== EventHandler ==================== + +HWTEST_F(ModularObjectExtensionContextImplTest, GetEventHandler_DefaultNull_001, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "GetEventHandler_DefaultNull_001 start"; + auto context = std::make_shared(); + EXPECT_EQ(context->GetEventHandler(), nullptr); + GTEST_LOG_(INFO) << "GetEventHandler_DefaultNull_001 end"; +} + +HWTEST_F(ModularObjectExtensionContextImplTest, SetEventHandler_GetSameHandler_001, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "SetEventHandler_GetSameHandler_001 start"; + auto context = std::make_shared(); + auto handler = std::make_shared(); + context->SetEventHandler(handler); + EXPECT_EQ(context->GetEventHandler(), handler); + GTEST_LOG_(INFO) << "SetEventHandler_GetSameHandler_001 end"; +} + +HWTEST_F(ModularObjectExtensionContextImplTest, GetEventHandler_HandlerExpired_001, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "GetEventHandler_HandlerExpired_001 start"; + auto context = std::make_shared(); + auto handler = std::make_shared(); + context->SetEventHandler(handler); + handler.reset(); + EXPECT_EQ(context->GetEventHandler(), nullptr); + GTEST_LOG_(INFO) << "GetEventHandler_HandlerExpired_001 end"; +} + // ==================== StartSelfUIAbility ==================== HWTEST_F(ModularObjectExtensionContextImplTest, StartSelfUIAbility_Success_001, TestSize.Level1) diff --git a/test/unittest/modular_object_extension_test/BUILD.gn b/test/unittest/modular_object_extension_test/BUILD.gn index da0c90ad12..bc7ce64d4d 100644 --- a/test/unittest/modular_object_extension_test/BUILD.gn +++ b/test/unittest/modular_object_extension_test/BUILD.gn @@ -26,15 +26,18 @@ ohos_unittest("modular_object_extension_test") { sources = [ "modular_object_extension_test.cpp", "${ability_runtime_path}/frameworks/native/ability/native/modular_object_extension/modular_object_extension.cpp", + "${ability_runtime_path}/frameworks/native/ability/native/modular_object_worker_manager.cpp", ] include_dirs = [ "mock/include", + "${ability_runtime_path}/interfaces/kits/native/ability/native", "${ability_runtime_path}/frameworks/c/ability_runtime/include", "${ability_runtime_services_path}/common/include", ] cflags = [ "-Dprivate=public" ] external_deps = [ "c_utils:utils", + "eventhandler:libeventhandler", "googletest:gtest_main", "hilog:libhilog", "ipc:ipc_core", diff --git a/test/unittest/modular_object_extension_test/mock/include/ability_handler.h b/test/unittest/modular_object_extension_test/mock/include/ability_handler.h new file mode 100644 index 0000000000..4bad5369a0 --- /dev/null +++ b/test/unittest/modular_object_extension_test/mock/include/ability_handler.h @@ -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. + */ + +#ifndef OHOS_ABILITY_RUNTIME_ABILITY_HANDLER_H +#define OHOS_ABILITY_RUNTIME_ABILITY_HANDLER_H + +#include +#include + +namespace OHOS { +namespace AppExecFwk { + +class EventRunner { +public: + static std::shared_ptr Create(const std::string &name) + { + return std::make_shared(); + } +}; + +class EventHandler { +public: + EventHandler() = default; + explicit EventHandler(const std::shared_ptr &) {} +}; + +class AbilityHandler : public EventHandler { +public: + AbilityHandler() = default; + explicit AbilityHandler(const std::shared_ptr &runner) : EventHandler(runner) {} + std::shared_ptr GetEventRunner() const { return nullptr; } +}; + +class InnerEvent { +public: + class Pointer {}; +}; + +} // namespace AppExecFwk +} // namespace OHOS + +#endif // OHOS_ABILITY_RUNTIME_ABILITY_HANDLER_H diff --git a/test/unittest/modular_object_extension_test/mock/include/event_handler.h b/test/unittest/modular_object_extension_test/mock/include/event_handler.h new file mode 100644 index 0000000000..23e9c0fee6 --- /dev/null +++ b/test/unittest/modular_object_extension_test/mock/include/event_handler.h @@ -0,0 +1,21 @@ +/* + * 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 MOCK_EVENT_HANDLER_H +#define MOCK_EVENT_HANDLER_H + +// Empty stub - definitions are in mock ability_handler.h + +#endif // MOCK_EVENT_HANDLER_H diff --git a/test/unittest/modular_object_extension_test/mock/include/extension.h b/test/unittest/modular_object_extension_test/mock/include/extension.h index 301b95afc0..3aaa05bc5f 100644 --- a/test/unittest/modular_object_extension_test/mock/include/extension.h +++ b/test/unittest/modular_object_extension_test/mock/include/extension.h @@ -18,6 +18,8 @@ #include #include +#include +#include "ability_handler.h" #include "mock_types.h" #include "refbase.h" #include "iremote_object.h" @@ -27,20 +29,35 @@ namespace AbilityRuntime { class AbilityLocalRecord {}; class OHOSApplication {}; -class AbilityHandler {}; + +} // namespace AbilityRuntime + +namespace AppExecFwk { + +struct Metadata { + std::string name; + std::string value; +}; struct AbilityInfo { std::string srcEntrance; std::string moduleName; std::string bundleName; std::string name; + std::vector metadata; }; +} // namespace AppExecFwk + +namespace AbilityRuntime { + class Extension : public std::enable_shared_from_this { public: Extension() = default; virtual ~Extension() = default; + using AbilityHandler = AppExecFwk::AbilityHandler; + virtual void Init(const std::shared_ptr &, const std::shared_ptr &, std::shared_ptr &, @@ -51,7 +68,13 @@ public: virtual sptr OnConnect(const AAFwk::Want &want) { return nullptr; } virtual void OnDisconnect(const AAFwk::Want &want) {} - std::shared_ptr abilityInfo_; + virtual std::shared_ptr GetAbilityHandler( + const std::shared_ptr &) + { + return nullptr; + } + + std::shared_ptr abilityInfo_; }; } // namespace AbilityRuntime diff --git a/test/unittest/modular_object_extension_test/mock/include/modular_object_extension.h b/test/unittest/modular_object_extension_test/mock/include/modular_object_extension.h index 8779f9d025..061ea48780 100644 --- a/test/unittest/modular_object_extension_test/mock/include/modular_object_extension.h +++ b/test/unittest/modular_object_extension_test/mock/include/modular_object_extension.h @@ -18,6 +18,7 @@ #include "extension_base.h" #include "modular_object_extension_context_impl.h" +#include "modular_object_worker_manager.h" #include "modular_object_extension_types.h" #ifdef __cplusplus @@ -59,6 +60,9 @@ public: sptr OnConnect(const AAFwk::Want &want) override; void OnDisconnect(const AAFwk::Want &want) override; + std::shared_ptr GetAbilityHandler( + const std::shared_ptr &abilityInfo) override; + private: bool LoadNativeExtensionModule(); bool BuildCWant(const AAFwk::Want &want, AbilityBase_Want &cWant, AbilityBase_Element &element) const; @@ -67,6 +71,7 @@ private: std::shared_ptr moeInstance_; std::shared_ptr moeContext_; + std::string threadKey_; }; } // namespace AbilityRuntime diff --git a/test/unittest/modular_object_extension_test/mock/include/modular_object_extension_context_impl.h b/test/unittest/modular_object_extension_test/mock/include/modular_object_extension_context_impl.h index ba35759e66..654653ee5b 100644 --- a/test/unittest/modular_object_extension_test/mock/include/modular_object_extension_context_impl.h +++ b/test/unittest/modular_object_extension_test/mock/include/modular_object_extension_context_impl.h @@ -26,6 +26,11 @@ namespace AbilityRuntime { class ModularObjectExtensionContext : public Context { public: ModularObjectExtensionContext() = default; + + void SetEventHandler(const std::shared_ptr &handler) + { + (void)handler; + } }; } // namespace AbilityRuntime diff --git a/test/unittest/modular_object_extension_test/mock/include/modular_object_extension_info.h b/test/unittest/modular_object_extension_test/mock/include/modular_object_extension_info.h new file mode 100644 index 0000000000..7cc7c42a58 --- /dev/null +++ b/test/unittest/modular_object_extension_test/mock/include/modular_object_extension_info.h @@ -0,0 +1,36 @@ +/* + * 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_MODULAR_OBJECT_EXTENSION_INFO_H +#define OHOS_ABILITY_RUNTIME_MODULAR_OBJECT_EXTENSION_INFO_H + +namespace OHOS { +namespace AAFwk { + +enum class MoeLaunchMode : int32_t { + IN_PROCESS = 0, + CROSS_PROCESS = 1, +}; + +enum class MoeThreadMode : int32_t { + BUNDLE = 0, + TYPE = 1, + INSTANCE = 2, +}; + +} // namespace AAFwk +} // namespace OHOS + +#endif // OHOS_ABILITY_RUNTIME_MODULAR_OBJECT_EXTENSION_INFO_H diff --git a/test/unittest/modular_object_extension_test/mock/include/modular_object_worker_manager.h b/test/unittest/modular_object_extension_test/mock/include/modular_object_worker_manager.h new file mode 100644 index 0000000000..8c287a2175 --- /dev/null +++ b/test/unittest/modular_object_extension_test/mock/include/modular_object_worker_manager.h @@ -0,0 +1,53 @@ +/* + * 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_MODULAR_OBJECT_WORKER_MANAGER_H +#define OHOS_ABILITY_RUNTIME_MODULAR_OBJECT_WORKER_MANAGER_H + +#include +#include +#include +#include + +#include "ability_handler.h" +#include "event_handler.h" + +namespace OHOS { +namespace AbilityRuntime { + +struct WorkerEntry { + std::shared_ptr handler; + uint32_t refCount = 0; +}; + +class ModularObjectWorkerManager { +public: + ModularObjectWorkerManager() = default; + ~ModularObjectWorkerManager() = default; + static ModularObjectWorkerManager &GetInstance(); + + std::shared_ptr GetOrCreateWorkerThread(const std::string &threadKey); + void ReleaseWorkerThread(const std::string &threadKey); + uint32_t GenerateInstanceId(); + +private: + std::mutex workerMutex_; + std::unordered_map workerMap_; + std::atomic instanceId_{0}; +}; + +} // namespace AbilityRuntime +} // namespace OHOS +#endif // OHOS_ABILITY_RUNTIME_MODULAR_OBJECT_WORKER_MANAGER_H diff --git a/test/unittest/modular_object_extension_test/modular_object_extension_test.cpp b/test/unittest/modular_object_extension_test/modular_object_extension_test.cpp index 1631131630..291e15bc10 100644 --- a/test/unittest/modular_object_extension_test/modular_object_extension_test.cpp +++ b/test/unittest/modular_object_extension_test/modular_object_extension_test.cpp @@ -16,6 +16,7 @@ #include #include "modular_object_extension.h" +#include "modular_object_worker_manager.h" #include "native_runtime.h" #include "want_manager.h" @@ -350,3 +351,231 @@ HWTEST_F(ModularObjectExtensionTest, CreateAndInitContext_ReturnsNullptr_001, Te EXPECT_EQ(ret, nullptr); GTEST_LOG_(INFO) << "CreateAndInitContext_ReturnsNullptr_001 end"; } + +// ==================== GetAbilityHandler ==================== + +HWTEST_F(ModularObjectExtensionTest, + GetAbilityHandler_ShouldReturnNullptrWhenAbilityInfoIsNull, TestSize.Level1) +{ + auto ext = std::make_shared(); + auto handler = ext->GetAbilityHandler(nullptr); + EXPECT_EQ(handler, nullptr); + EXPECT_TRUE(ext->threadKey_.empty()); +} + +HWTEST_F(ModularObjectExtensionTest, + GetAbilityHandler_ShouldUseDefaultBundleKeyWhenNoMetadata, TestSize.Level1) +{ + auto ext = std::make_shared(); + auto abilityInfo = std::make_shared(); + abilityInfo->bundleName = "com.test"; + abilityInfo->name = "TestExt"; + auto handler = ext->GetAbilityHandler(abilityInfo); + ASSERT_NE(handler, nullptr); + EXPECT_EQ(ext->threadKey_, "com.test"); + ModularObjectWorkerManager::GetInstance().ReleaseWorkerThread(ext->threadKey_); +} + +HWTEST_F(ModularObjectExtensionTest, + GetAbilityHandler_ShouldUseBundleKeyWhenThreadModeIsBundle, TestSize.Level1) +{ + auto ext = std::make_shared(); + auto abilityInfo = std::make_shared(); + abilityInfo->bundleName = "com.test"; + abilityInfo->name = "TestExt"; + AppExecFwk::Metadata meta; + meta.name = "threadMode"; + meta.value = "BUNDLE"; + abilityInfo->metadata.push_back(meta); + auto handler = ext->GetAbilityHandler(abilityInfo); + ASSERT_NE(handler, nullptr); + EXPECT_EQ(ext->threadKey_, "com.test"); + ModularObjectWorkerManager::GetInstance().ReleaseWorkerThread(ext->threadKey_); +} + +HWTEST_F(ModularObjectExtensionTest, + GetAbilityHandler_ShouldUseInstanceKeyWhenThreadModeIsInstance, TestSize.Level1) +{ + auto ext = std::make_shared(); + auto abilityInfo = std::make_shared(); + abilityInfo->bundleName = "com.test"; + abilityInfo->name = "TestExt"; + AppExecFwk::Metadata meta; + meta.name = "threadMode"; + meta.value = "INSTANCE"; + abilityInfo->metadata.push_back(meta); + auto handler = ext->GetAbilityHandler(abilityInfo); + ASSERT_NE(handler, nullptr); + EXPECT_EQ(ext->threadKey_.substr(0, strlen("com.test_TestExt_")), "com.test_TestExt_"); + ModularObjectWorkerManager::GetInstance().ReleaseWorkerThread(ext->threadKey_); +} + +HWTEST_F(ModularObjectExtensionTest, + GetAbilityHandler_ShouldUseTypeKeyWhenThreadModeIsType, TestSize.Level1) +{ + auto ext = std::make_shared(); + auto abilityInfo = std::make_shared(); + abilityInfo->bundleName = "com.test"; + abilityInfo->name = "TestExt"; + AppExecFwk::Metadata meta; + meta.name = "threadMode"; + meta.value = "TYPE"; + abilityInfo->metadata.push_back(meta); + auto handler = ext->GetAbilityHandler(abilityInfo); + ASSERT_NE(handler, nullptr); + EXPECT_EQ(ext->threadKey_, "com.test_TestExt"); + ModularObjectWorkerManager::GetInstance().ReleaseWorkerThread(ext->threadKey_); +} + +HWTEST_F(ModularObjectExtensionTest, + GetAbilityHandler_ShouldUseDefaultBundleKeyWhenMetadataIsNotThreadMode, TestSize.Level1) +{ + auto ext = std::make_shared(); + auto abilityInfo = std::make_shared(); + abilityInfo->bundleName = "com.test"; + abilityInfo->name = "TestExt"; + AppExecFwk::Metadata meta; + meta.name = "launchMode"; + meta.value = "CROSS_PROCESS"; + abilityInfo->metadata.push_back(meta); + auto handler = ext->GetAbilityHandler(abilityInfo); + ASSERT_NE(handler, nullptr); + EXPECT_EQ(ext->threadKey_, "com.test"); + ModularObjectWorkerManager::GetInstance().ReleaseWorkerThread(ext->threadKey_); +} + +HWTEST_F(ModularObjectExtensionTest, + GetAbilityHandler_ShouldFindThreadModeAmongMultipleMetadata, TestSize.Level1) +{ + auto ext = std::make_shared(); + auto abilityInfo = std::make_shared(); + abilityInfo->bundleName = "com.test"; + abilityInfo->name = "TestExt"; + AppExecFwk::Metadata meta1; + meta1.name = "launchMode"; + meta1.value = "CROSS_PROCESS"; + abilityInfo->metadata.push_back(meta1); + AppExecFwk::Metadata meta2; + meta2.name = "threadMode"; + meta2.value = "BUNDLE"; + abilityInfo->metadata.push_back(meta2); + auto handler = ext->GetAbilityHandler(abilityInfo); + ASSERT_NE(handler, nullptr); + EXPECT_EQ(ext->threadKey_, "com.test"); + ModularObjectWorkerManager::GetInstance().ReleaseWorkerThread(ext->threadKey_); +} + +HWTEST_F(ModularObjectExtensionTest, + GetAbilityHandler_ShouldShareHandlerWhenSameBundleInBundleMode, TestSize.Level1) +{ + auto ext1 = std::make_shared(); + auto abilityInfo1 = std::make_shared(); + abilityInfo1->bundleName = "com.shared"; + abilityInfo1->name = "ExtA"; + AppExecFwk::Metadata meta1; + meta1.name = "threadMode"; + meta1.value = "BUNDLE"; + abilityInfo1->metadata.push_back(meta1); + + auto ext2 = std::make_shared(); + auto abilityInfo2 = std::make_shared(); + abilityInfo2->bundleName = "com.shared"; + abilityInfo2->name = "ExtB"; + AppExecFwk::Metadata meta2; + meta2.name = "threadMode"; + meta2.value = "BUNDLE"; + abilityInfo2->metadata.push_back(meta2); + + auto handler1 = ext1->GetAbilityHandler(abilityInfo1); + auto handler2 = ext2->GetAbilityHandler(abilityInfo2); + ASSERT_NE(handler1, nullptr); + ASSERT_NE(handler2, nullptr); + EXPECT_EQ(handler1, handler2); + EXPECT_EQ(ext1->threadKey_, "com.shared"); + EXPECT_EQ(ext2->threadKey_, "com.shared"); + ModularObjectWorkerManager::GetInstance().ReleaseWorkerThread("com.shared"); + ModularObjectWorkerManager::GetInstance().ReleaseWorkerThread("com.shared"); +} + +// ==================== OnStop with threadKey_ release ==================== + +HWTEST_F(ModularObjectExtensionTest, + OnStop_ShouldNotCrashWhenThreadKeyIsEmpty, TestSize.Level1) +{ + auto ext = std::make_shared(); + ext->OnStop(); + EXPECT_TRUE(ext->threadKey_.empty()); +} + +HWTEST_F(ModularObjectExtensionTest, + OnStop_ShouldReleaseWorkerThreadWhenThreadKeyIsSet, TestSize.Level1) +{ + auto ext = std::make_shared(); + auto abilityInfo = std::make_shared(); + abilityInfo->bundleName = "com.test.stop"; + abilityInfo->name = "TestExt"; + auto handler = ext->GetAbilityHandler(abilityInfo); + ASSERT_NE(handler, nullptr); + EXPECT_FALSE(ext->threadKey_.empty()); + ext->OnStop(); + EXPECT_TRUE(ext->threadKey_.empty()); +} + +HWTEST_F(ModularObjectExtensionTest, + OnStop_ShouldCallCallbackAndReleaseWhenBothPresent, TestSize.Level1) +{ + g_onDestroyCalled = false; + auto ext = std::make_shared(); + auto record = std::make_shared(); + auto app = std::make_shared(); + auto handler = std::make_shared(); + sptr token; + ext->Init(record, app, handler, token); + ext->moeInstance_->onDestroyFunc = OnDestroyCallback; + + auto abilityInfo = std::make_shared(); + abilityInfo->bundleName = "com.test.stopcb"; + abilityInfo->name = "TestExt"; + ext->GetAbilityHandler(abilityInfo); + EXPECT_FALSE(ext->threadKey_.empty()); + + ext->OnStop(); + EXPECT_TRUE(g_onDestroyCalled); + EXPECT_TRUE(ext->threadKey_.empty()); +} + +// ==================== GetAbilityHandler + OnStop lifecycle ==================== + +HWTEST_F(ModularObjectExtensionTest, + OnStop_ShouldNotCrashWhenCalledTwiceInLifecycle, TestSize.Level1) +{ + auto ext = std::make_shared(); + auto record = std::make_shared(); + auto app = std::make_shared(); + auto handler = std::make_shared(); + sptr token; + ext->Init(record, app, handler, token); + ext->moeInstance_->onDestroyFunc = OnDestroyCallback; + + auto abilityInfo = std::make_shared(); + abilityInfo->bundleName = "com.test.lifecycle"; + abilityInfo->name = "TestExt"; + auto result = ext->GetAbilityHandler(abilityInfo); + ASSERT_NE(result, nullptr); + EXPECT_EQ(ext->threadKey_, "com.test.lifecycle"); + + ext->OnStop(); + EXPECT_TRUE(ext->threadKey_.empty()); + ext->OnStop(); + EXPECT_TRUE(ext->threadKey_.empty()); +} + +// ==================== ModularObjectWorkerManager singleton identity ==================== + +HWTEST_F(ModularObjectExtensionTest, + GetInstance_ShouldReturnSameSingletonReference, TestSize.Level1) +{ + auto &inst1 = ModularObjectWorkerManager::GetInstance(); + auto &inst2 = ModularObjectWorkerManager::GetInstance(); + EXPECT_EQ(&inst1, &inst2); +} diff --git a/test/unittest/modular_object_utils_test/mock/include/ability_manager_errors.h b/test/unittest/modular_object_utils_test/mock/include/ability_manager_errors.h index c3a84ebe69..98cee7d02c 100644 --- a/test/unittest/modular_object_utils_test/mock/include/ability_manager_errors.h +++ b/test/unittest/modular_object_utils_test/mock/include/ability_manager_errors.h @@ -45,6 +45,9 @@ enum { constexpr ErrCode ERR_MODULAR_OBJECT_DISABLED = 2099412; constexpr ErrCode ERR_NO_RUNNING_ABILITIES_WITH_UI = 2099413; constexpr ErrCode ERR_INVALID_DISTRIBUTION_TYPE = 2099414; +constexpr ErrCode ERR_MOE_CONNECTION_LIMIT = 2099415; +constexpr ErrCode ERR_MOE_INSTANCE_LIMIT = 2099416; +constexpr ErrCode ERR_MOE_CROSS_APP_IN_PROCESS = 2099417; constexpr ErrCode ERR_FREQ_START_ABILITY = 2098012; constexpr ErrCode ABILITY_VISIBLE_FALSE_DENY_REQUEST = 2097179; diff --git a/test/unittest/modular_object_utils_test/mock/include/ability_record.h b/test/unittest/modular_object_utils_test/mock/include/ability_record.h index 3def45ec21..a866b47e12 100644 --- a/test/unittest/modular_object_utils_test/mock/include/ability_record.h +++ b/test/unittest/modular_object_utils_test/mock/include/ability_record.h @@ -26,8 +26,12 @@ namespace AAFwk { class AbilityRecord { public: static AppExecFwk::AbilityInfo abilityInfo; + static int32_t recordId_; + static std::string processName_; const AppExecFwk::AbilityInfo &GetAbilityInfo() const { return abilityInfo; } + int32_t GetRecordId() const { return recordId_; } + void SetProcessName(const std::string &name) { processName_ = name; } }; } // namespace AAFwk diff --git a/test/unittest/modular_object_utils_test/mock/include/ability_record/ability_request.h b/test/unittest/modular_object_utils_test/mock/include/ability_record/ability_request.h index 207bc96757..6470babf06 100644 --- a/test/unittest/modular_object_utils_test/mock/include/ability_record/ability_request.h +++ b/test/unittest/modular_object_utils_test/mock/include/ability_record/ability_request.h @@ -18,6 +18,7 @@ #include #include +#include namespace OHOS { namespace AppExecFwk { @@ -25,6 +26,8 @@ struct ApplicationInfo { std::string appDistributionType; uint32_t accessTokenId = 0; int32_t uid = 0; + int32_t appIndex = 0; + std::string process; }; enum ExtensionAbilityType { UNSPECIFIED = 0, @@ -35,6 +38,9 @@ enum ExtensionAbilityType { struct AbilityInfo { ExtensionAbilityType extensionAbilityType = UNSPECIFIED; bool visible = false; + std::string bundleName; + std::string name; + std::string extensionTypeName; }; } // namespace AppExecFwk @@ -64,6 +70,7 @@ private: class Want { public: inline static const std::string PARAM_APP_CLONE_INDEX_KEY = "appCloneIndex"; + inline static const std::string PARAM_RESV_CALLER_BUNDLE_NAME = "callerBundleName"; ElementName GetElement() const { return element_; } void SetElement(const ElementName &element) { element_ = element; } int32_t GetIntParam(const std::string &key, int32_t defaultValue) const @@ -74,9 +81,22 @@ public: return defaultValue; } void SetAppCloneIndex(int32_t index) { appCloneIndex_ = index; } + std::string GetStringParam(const std::string &key) const + { + auto it = stringParams_.find(key); + if (it != stringParams_.end()) { + return it->second; + } + return ""; + } + void SetParam(const std::string &key, const std::string &value) + { + stringParams_[key] = value; + } private: ElementName element_; int32_t appCloneIndex_ = 0; + std::map stringParams_; }; struct AbilityRequest { diff --git a/test/unittest/modular_object_utils_test/mock/include/app_mgr_client.h b/test/unittest/modular_object_utils_test/mock/include/app_mgr_client.h index 692da4ad41..69d8818a8a 100644 --- a/test/unittest/modular_object_utils_test/mock/include/app_mgr_client.h +++ b/test/unittest/modular_object_utils_test/mock/include/app_mgr_client.h @@ -32,6 +32,7 @@ public: } info.state_ = static_cast(MockFlag::processState); info.isPreForeground = MockFlag::isPreForeground; + info.processName_ = MockFlag::processName; return 0; } }; diff --git a/test/unittest/modular_object_utils_test/mock/include/base_extension_record.h b/test/unittest/modular_object_utils_test/mock/include/base_extension_record.h index 2b04592f02..ca40ecc72d 100644 --- a/test/unittest/modular_object_utils_test/mock/include/base_extension_record.h +++ b/test/unittest/modular_object_utils_test/mock/include/base_extension_record.h @@ -23,8 +23,11 @@ namespace AAFwk { class BaseExtensionRecord : public AbilityRecord { public: static pid_t clientPid; + static std::string requestId_; pid_t GetClientPid() const { return clientPid; } + void SetRequestId(const std::string &id) { requestId_ = id; } + std::string GetRequestId() const { return requestId_; } }; } // namespace AAFwk diff --git a/test/unittest/modular_object_utils_test/mock/include/bundle_mgr_helper.h b/test/unittest/modular_object_utils_test/mock/include/bundle_mgr_helper.h index 5ecd0fcec9..252fe574da 100644 --- a/test/unittest/modular_object_utils_test/mock/include/bundle_mgr_helper.h +++ b/test/unittest/modular_object_utils_test/mock/include/bundle_mgr_helper.h @@ -32,7 +32,7 @@ public: if (MockFlag::getNameAndIndexRet != 0) { return MockFlag::getNameAndIndexRet; } - bundleName = "com.caller.bundle"; + bundleName = MockFlag::callerBundleName; appIndex = 0; return 0; } diff --git a/test/unittest/modular_object_utils_test/mock/include/mock_flag.h b/test/unittest/modular_object_utils_test/mock/include/mock_flag.h index 74234396a4..c68c51a9e5 100644 --- a/test/unittest/modular_object_utils_test/mock/include/mock_flag.h +++ b/test/unittest/modular_object_utils_test/mock/include/mock_flag.h @@ -17,8 +17,12 @@ #define MOCK_FLAG_H #include +#include +#include #include +#include "modular_object_extension_info.h" + class MockFlag { public: // AppUtils @@ -32,6 +36,7 @@ public: static int32_t getRunningProcessInfoRet; static int32_t processState; static bool isPreForeground; + static std::string processName; // system::GetBoolParameter static bool isDeveloperMode; @@ -40,12 +45,14 @@ public: static int32_t queryDataRet; static bool extensionFound; static bool extensionDisabled; + static OHOS::AAFwk::MoeLaunchMode launchMode; // BundleMgrHelper static bool bundleMgrHelperNull; static int32_t getNameAndIndexRet; static int32_t getOsAccountRet; static bool getApplicationInfoRet; + static std::string callerBundleName; // AbilityManagerService static bool amsNull; @@ -66,6 +73,10 @@ public: // RateLimiter static bool modularObjectLimited; + + // ModularObjectManager + static int32_t querySelfModularObjectRet; + static std::vector modularObjectInfos; }; #endif // MOCK_FLAG_H diff --git a/test/unittest/modular_object_utils_test/mock/include/modular_object_manager.h b/test/unittest/modular_object_utils_test/mock/include/modular_object_manager.h new file mode 100644 index 0000000000..10c3d0a2e5 --- /dev/null +++ b/test/unittest/modular_object_utils_test/mock/include/modular_object_manager.h @@ -0,0 +1,42 @@ +/* + * 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 MOCK_MODULAR_OBJECT_MANAGER_H +#define MOCK_MODULAR_OBJECT_MANAGER_H + +#include +#include +#include +#include "mock_flag.h" +#include "modular_object_extension_info.h" +#include "singleton.h" + +namespace OHOS { +namespace AbilityRuntime { +class ModularObjectManager : public DelayedSingleton { +public: + int32_t QuerySelfModularObjectExtensionInfos(int32_t userId, const std::string &bundleName, + int32_t appIndex, std::vector &infos) + { + if (MockFlag::querySelfModularObjectRet != 0) { + return MockFlag::querySelfModularObjectRet; + } + infos = MockFlag::modularObjectInfos; + return 0; + } +}; +} // namespace AbilityRuntime +} // namespace OHOS +#endif // MOCK_MODULAR_OBJECT_MANAGER_H diff --git a/test/unittest/modular_object_utils_test/mock/include/modular_object_rdb_storage_mgr.h b/test/unittest/modular_object_utils_test/mock/include/modular_object_rdb_storage_mgr.h index 4a32a6de34..cb68f21eae 100644 --- a/test/unittest/modular_object_utils_test/mock/include/modular_object_rdb_storage_mgr.h +++ b/test/unittest/modular_object_utils_test/mock/include/modular_object_rdb_storage_mgr.h @@ -38,6 +38,7 @@ public: info.bundleName = "com.test.bundle"; info.abilityName = "TestAbility"; info.isDisabled = MockFlag::extensionDisabled; + info.launchMode = MockFlag::launchMode; infos.push_back(info); } return 0; diff --git a/test/unittest/modular_object_utils_test/mock/include/modular_object_utils.h b/test/unittest/modular_object_utils_test/mock/include/modular_object_utils.h index 92b33d69f9..b162e2dd9b 100644 --- a/test/unittest/modular_object_utils_test/mock/include/modular_object_utils.h +++ b/test/unittest/modular_object_utils_test/mock/include/modular_object_utils.h @@ -20,6 +20,7 @@ #include #include "ability_record/ability_request.h" +#include "base_extension_record.h" #include "iremote_object.h" #include "modular_object_extension_info.h" @@ -34,8 +35,14 @@ public: static int32_t CheckRateLimit(); static bool GetPidToCheckByCallerToken(sptr callerToken, pid_t &outPid); + static std::shared_ptr QueryConfig(const AbilityRequest &abilityRequest); + static int32_t SetupNewRecord(const AbilityRequest &abilityRequest, + std::shared_ptr &targetService, const std::string &serviceKey); + static int32_t CheckLimits(int32_t instanceCount, int32_t connectionCount); + static int32_t CheckExtensionEnabled(const ModularObjectExtensionInfo &info, const AbilityRequest &abilityRequest); + static int32_t CheckInProcessLaunchMode(MoeLaunchMode launchMode, int32_t targetUid); static int32_t CheckCallerForeground(); static int32_t CheckAppDistributionType(const std::string &callerAppDistributionType, const std::string &targetAppDistributionType); diff --git a/test/unittest/modular_object_utils_test/mock/include/running_process_info.h b/test/unittest/modular_object_utils_test/mock/include/running_process_info.h index 67bc3fd4ef..4044b477ad 100644 --- a/test/unittest/modular_object_utils_test/mock/include/running_process_info.h +++ b/test/unittest/modular_object_utils_test/mock/include/running_process_info.h @@ -17,6 +17,7 @@ #define MOCK_RUNNING_PROCESS_INFO_H #include +#include namespace OHOS { namespace AppExecFwk { @@ -32,6 +33,7 @@ enum class AppProcessState { struct RunningProcessInfo { AppProcessState state_ = AppProcessState::APP_STATE_FOREGROUND; bool isPreForeground = false; + std::string processName_; }; } // namespace AppExecFwk diff --git a/test/unittest/modular_object_utils_test/mock/src/mock_flag.cpp b/test/unittest/modular_object_utils_test/mock/src/mock_flag.cpp index 825b1e032b..77cef05217 100644 --- a/test/unittest/modular_object_utils_test/mock/src/mock_flag.cpp +++ b/test/unittest/modular_object_utils_test/mock/src/mock_flag.cpp @@ -18,20 +18,25 @@ #include "base_extension_record.h" #include "ability_record/ability_record_utils.h" +#include + bool MockFlag::isSupportModularObjectExtension = true; int32_t MockFlag::callingUid = 1000; pid_t MockFlag::callingPid = 1234; int32_t MockFlag::getRunningProcessInfoRet = 0; int32_t MockFlag::processState = 2; // APP_STATE_FOREGROUND bool MockFlag::isPreForeground = false; +std::string MockFlag::processName; bool MockFlag::isDeveloperMode = false; int32_t MockFlag::queryDataRet = 0; bool MockFlag::extensionFound = true; bool MockFlag::extensionDisabled = false; +OHOS::AAFwk::MoeLaunchMode MockFlag::launchMode = OHOS::AAFwk::MoeLaunchMode::CROSS_PROCESS; bool MockFlag::bundleMgrHelperNull = false; int32_t MockFlag::getNameAndIndexRet = 0; int32_t MockFlag::getOsAccountRet = 0; bool MockFlag::getApplicationInfoRet = true; +std::string MockFlag::callerBundleName = "com.caller.bundle"; bool MockFlag::amsNull = false; bool MockFlag::isSceneBoardEnabled = true; bool MockFlag::hasRunningUIAbility = true; @@ -42,10 +47,15 @@ bool MockFlag::uiExtMgrNull = false; bool MockFlag::isSACall = false; int32_t MockFlag::checkCallModularObjectExtensionPermissionRet = 0; bool MockFlag::modularObjectLimited = false; +int32_t MockFlag::querySelfModularObjectRet = 0; +std::vector MockFlag::modularObjectInfos; // Token mock std::shared_ptr OHOS::AAFwk::Token::abilityRecord_ = nullptr; // AbilityRecord mock OHOS::AppExecFwk::AbilityInfo OHOS::AAFwk::AbilityRecord::abilityInfo; +int32_t OHOS::AAFwk::AbilityRecord::recordId_ = 0; +std::string OHOS::AAFwk::AbilityRecord::processName_; pid_t OHOS::AAFwk::BaseExtensionRecord::clientPid = -1; +std::string OHOS::AAFwk::BaseExtensionRecord::requestId_; diff --git a/test/unittest/modular_object_utils_test/modular_object_utils_test.cpp b/test/unittest/modular_object_utils_test/modular_object_utils_test.cpp index 45426b2b80..8e17114bf5 100644 --- a/test/unittest/modular_object_utils_test/modular_object_utils_test.cpp +++ b/test/unittest/modular_object_utils_test/modular_object_utils_test.cpp @@ -58,6 +58,11 @@ void ResetFlags() Token::abilityRecord_ = nullptr; AbilityRecord::abilityInfo.extensionAbilityType = ExtensionAbilityType::UNSPECIFIED; BaseExtensionRecord::clientPid = -1; + MockFlag::launchMode = MoeLaunchMode::CROSS_PROCESS; + MockFlag::callerBundleName = "com.caller.bundle"; + MockFlag::processName = ""; + MockFlag::querySelfModularObjectRet = 0; + MockFlag::modularObjectInfos.clear(); } } // namespace @@ -636,3 +641,497 @@ HWTEST_F(ModularObjectUtilsTest, GetPidToCheckByCallerToken_006, TestSize.Level1 EXPECT_EQ(outPid, 5678); GTEST_LOG_(INFO) << "GetPidToCheckByCallerToken_006 end"; } + +// ==================== CheckPermission IN_PROCESS tests ==================== + +/** + * @tc.name: CheckPermission_ShouldReturnErrorWhenInProcessCrossApp + * @tc.desc: IN_PROCESS with different uid returns ERR_MOE_CROSS_APP_IN_PROCESS + */ +HWTEST_F(ModularObjectUtilsTest, + CheckPermission_ShouldReturnErrorWhenInProcessCrossApp, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "CheckPermission_ShouldReturnErrorWhenInProcessCrossApp start"; + MockFlag::launchMode = MoeLaunchMode::IN_PROCESS; + // callingUid is 1000 (from ResetFlags), request.uid is 100 (different) + AbilityRequest request; + request.uid = 100; + request.want.SetElement(ElementName("", "com.test.bundle", "TestAbility")); + auto ret = ModularObjectUtils::CheckPermission(request); + EXPECT_EQ(ret, ERR_MOE_CROSS_APP_IN_PROCESS); + GTEST_LOG_(INFO) << "CheckPermission_ShouldReturnErrorWhenInProcessCrossApp end"; +} + +/** + * @tc.name: CheckPermission_ShouldReturnOkWhenInProcessSameUid + * @tc.desc: IN_PROCESS with same uid passes and reaches foreground check + */ +HWTEST_F(ModularObjectUtilsTest, + CheckPermission_ShouldReturnOkWhenInProcessSameUid, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "CheckPermission_ShouldReturnOkWhenInProcessSameUid start"; + MockFlag::launchMode = MoeLaunchMode::IN_PROCESS; + // callingUid is 1000, set request.uid to match + AbilityRequest request; + request.uid = 1000; + request.want.SetElement(ElementName("", "com.test.bundle", "TestAbility")); + // Will fail at CheckCallerForeground, but passes IN_PROCESS check + MockFlag::getRunningProcessInfoRet = -1; + auto ret = ModularObjectUtils::CheckPermission(request); + EXPECT_EQ(ret, -1); + GTEST_LOG_(INFO) << "CheckPermission_ShouldReturnOkWhenInProcessSameUid end"; +} + +/** + * @tc.name: CheckPermission_ShouldReturnOkWhenCrossProcessDifferentCaller + * @tc.desc: CROSS_PROCESS mode skips IN_PROCESS check, allows different caller + */ +HWTEST_F(ModularObjectUtilsTest, + CheckPermission_ShouldReturnOkWhenCrossProcessDifferentCaller, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "CheckPermission_ShouldReturnOkWhenCrossProcessDifferentCaller start"; + MockFlag::launchMode = MoeLaunchMode::CROSS_PROCESS; + // Default callerBundleName differs from target, but CROSS_PROCESS skips check + AbilityRequest request; + request.uid = 100; + request.want.SetElement(ElementName("", "com.test.bundle", "TestAbility")); + MockFlag::getRunningProcessInfoRet = -1; + auto ret = ModularObjectUtils::CheckPermission(request); + EXPECT_EQ(ret, -1); + GTEST_LOG_(INFO) << "CheckPermission_ShouldReturnOkWhenCrossProcessDifferentCaller end"; +} + +// ==================== CheckInProcessLaunchMode ==================== + +/** + * @tc.name: CheckInProcessLaunchMode_ShouldReturnErrorWhenGetNameFail + * @tc.desc: GetNameAndIndexForUid fails returns INNER_ERR + */ +HWTEST_F(ModularObjectUtilsTest, + CheckInProcessLaunchMode_ShouldReturnErrorWhenCallerUidDiffersFromTarget, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "CheckInProcessLaunchMode_ShouldReturnErrorWhenCallerUidDiffersFromTarget start"; + // callingUid is 1000 (from ResetFlags), targetUid is different + auto ret = ModularObjectUtils::CheckInProcessLaunchMode(MoeLaunchMode::IN_PROCESS, 2000); + EXPECT_EQ(ret, ERR_MOE_CROSS_APP_IN_PROCESS); + GTEST_LOG_(INFO) << "CheckInProcessLaunchMode_ShouldReturnErrorWhenCallerUidDiffersFromTarget end"; +} + +/** + * @tc.name: CheckInProcessLaunchMode_ShouldReturnOkWhenCallerUidMatchesTarget + * @tc.desc: Caller uid matches target uid returns ERR_OK + */ +HWTEST_F(ModularObjectUtilsTest, + CheckInProcessLaunchMode_ShouldReturnOkWhenCallerUidMatchesTarget, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "CheckInProcessLaunchMode_ShouldReturnOkWhenCallerUidMatchesTarget start"; + // callingUid is 1000 (from ResetFlags) + auto ret = ModularObjectUtils::CheckInProcessLaunchMode(MoeLaunchMode::IN_PROCESS, 1000); + EXPECT_EQ(ret, ERR_OK); + GTEST_LOG_(INFO) << "CheckInProcessLaunchMode_ShouldReturnOkWhenCallerUidMatchesTarget end"; +} + +/** + * @tc.name: CheckInProcessLaunchMode_ShouldReturnOkWhenCrossProcessMode + * @tc.desc: CROSS_PROCESS mode skips the check and returns ERR_OK + */ +HWTEST_F(ModularObjectUtilsTest, + CheckInProcessLaunchMode_ShouldReturnOkWhenCrossProcessMode, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "CheckInProcessLaunchMode_ShouldReturnOkWhenCrossProcessMode start"; + // CROSS_PROCESS mode skips check regardless of uid + auto ret = ModularObjectUtils::CheckInProcessLaunchMode(MoeLaunchMode::CROSS_PROCESS, 9999); + EXPECT_EQ(ret, ERR_OK); + GTEST_LOG_(INFO) << "CheckInProcessLaunchMode_ShouldReturnOkWhenCrossProcessMode end"; +} + +// ==================== CheckLimits ==================== + +HWTEST_F(ModularObjectUtilsTest, CheckLimits_ShouldReturnOkWhenBothUnderLimit, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "CheckLimits_ShouldReturnOkWhenBothUnderLimit start"; + auto ret = ModularObjectUtils::CheckLimits(0, 0); + EXPECT_EQ(ret, ERR_OK); + GTEST_LOG_(INFO) << "CheckLimits_ShouldReturnOkWhenBothUnderLimit end"; +} + +HWTEST_F(ModularObjectUtilsTest, CheckLimits_ShouldReturnInstanceLimitWhenInstanceAtLimit, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "CheckLimits_ShouldReturnInstanceLimitWhenInstanceAtLimit start"; + auto ret = ModularObjectUtils::CheckLimits(20, 0); + EXPECT_EQ(ret, ERR_MOE_INSTANCE_LIMIT); + GTEST_LOG_(INFO) << "CheckLimits_ShouldReturnInstanceLimitWhenInstanceAtLimit end"; +} + +HWTEST_F(ModularObjectUtilsTest, CheckLimits_ShouldReturnConnectionLimitWhenConnectionAtLimit, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "CheckLimits_ShouldReturnConnectionLimitWhenConnectionAtLimit start"; + auto ret = ModularObjectUtils::CheckLimits(0, 5); + EXPECT_EQ(ret, ERR_MOE_CONNECTION_LIMIT); + GTEST_LOG_(INFO) << "CheckLimits_ShouldReturnConnectionLimitWhenConnectionAtLimit end"; +} + +HWTEST_F(ModularObjectUtilsTest, CheckLimits_ShouldReturnInstanceLimitWhenBothOverLimit, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "CheckLimits_ShouldReturnInstanceLimitWhenBothOverLimit start"; + auto ret = ModularObjectUtils::CheckLimits(25, 10); + EXPECT_EQ(ret, ERR_MOE_INSTANCE_LIMIT); + GTEST_LOG_(INFO) << "CheckLimits_ShouldReturnInstanceLimitWhenBothOverLimit end"; +} + +// ==================== QueryConfig ==================== + +HWTEST_F(ModularObjectUtilsTest, QueryConfig_ShouldReturnNullptrWhenQueryFails, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "QueryConfig_ShouldReturnNullptrWhenQueryFails start"; + MockFlag::querySelfModularObjectRet = -1; + AbilityRequest request; + request.userId = 100; + request.abilityInfo.bundleName = "com.test.bundle"; + request.appInfo.appIndex = 0; + auto result = ModularObjectUtils::QueryConfig(request); + EXPECT_EQ(result, nullptr); + GTEST_LOG_(INFO) << "QueryConfig_ShouldReturnNullptrWhenQueryFails end"; +} + +HWTEST_F(ModularObjectUtilsTest, QueryConfig_ShouldReturnNullptrWhenEmptyInfos, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "QueryConfig_ShouldReturnNullptrWhenEmptyInfos start"; + MockFlag::querySelfModularObjectRet = 0; + MockFlag::modularObjectInfos.clear(); + AbilityRequest request; + request.userId = 100; + request.abilityInfo.bundleName = "com.test.bundle"; + request.abilityInfo.name = "TestAbility"; + request.appInfo.appIndex = 0; + auto result = ModularObjectUtils::QueryConfig(request); + EXPECT_EQ(result, nullptr); + GTEST_LOG_(INFO) << "QueryConfig_ShouldReturnNullptrWhenEmptyInfos end"; +} + +HWTEST_F(ModularObjectUtilsTest, QueryConfig_ShouldReturnNullptrWhenAbilityNameNotFound, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "QueryConfig_ShouldReturnNullptrWhenAbilityNameNotFound start"; + ModularObjectExtensionInfo info; + info.bundleName = "com.test.bundle"; + info.abilityName = "OtherAbility"; + MockFlag::modularObjectInfos = {info}; + AbilityRequest request; + request.userId = 100; + request.abilityInfo.bundleName = "com.test.bundle"; + request.abilityInfo.name = "TestAbility"; + request.appInfo.appIndex = 0; + auto result = ModularObjectUtils::QueryConfig(request); + EXPECT_EQ(result, nullptr); + GTEST_LOG_(INFO) << "QueryConfig_ShouldReturnNullptrWhenAbilityNameNotFound end"; +} + +HWTEST_F(ModularObjectUtilsTest, QueryConfig_ShouldReturnConfigWhenAbilityNameMatched, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "QueryConfig_ShouldReturnConfigWhenAbilityNameMatched start"; + ModularObjectExtensionInfo info; + info.bundleName = "com.test.bundle"; + info.abilityName = "TestAbility"; + info.launchMode = MoeLaunchMode::CROSS_PROCESS; + MockFlag::modularObjectInfos = {info}; + AbilityRequest request; + request.userId = 100; + request.abilityInfo.bundleName = "com.test.bundle"; + request.abilityInfo.name = "TestAbility"; + request.appInfo.appIndex = 0; + auto result = ModularObjectUtils::QueryConfig(request); + ASSERT_NE(result, nullptr); + EXPECT_EQ(result->abilityName, "TestAbility"); + EXPECT_EQ(result->launchMode, MoeLaunchMode::CROSS_PROCESS); + GTEST_LOG_(INFO) << "QueryConfig_ShouldReturnConfigWhenAbilityNameMatched end"; +} + +// ==================== SetupNewRecord ==================== + +HWTEST_F(ModularObjectUtilsTest, SetupNewRecord_ShouldNotCrashWhenServiceIsNull, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "SetupNewRecord_ShouldNotCrashWhenServiceIsNull start"; + AbilityRequest request; + std::shared_ptr nullService = nullptr; + ModularObjectUtils::SetupNewRecord(request, nullService, "key_123"); + GTEST_LOG_(INFO) << "SetupNewRecord_ShouldNotCrashWhenServiceIsNull end"; +} + +HWTEST_F(ModularObjectUtilsTest, SetupNewRecord_ShouldNotSetProcessNameWhenConfigIsNull, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "SetupNewRecord_ShouldNotSetProcessNameWhenConfigIsNull start"; + MockFlag::modularObjectInfos.clear(); + AbilityRequest request; + auto service = std::make_shared(); + ModularObjectUtils::SetupNewRecord(request, service, "key_123"); + EXPECT_EQ(AbilityRecord::processName_, ""); + GTEST_LOG_(INFO) << "SetupNewRecord_ShouldNotSetProcessNameWhenConfigIsNull end"; +} + +HWTEST_F(ModularObjectUtilsTest, SetupNewRecord_ShouldUseProcessNameWhenInProcessMode, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "SetupNewRecord_ShouldUseProcessNameWhenInProcessMode start"; + ModularObjectExtensionInfo info; + info.bundleName = "com.test.bundle"; + info.abilityName = "TestAbility"; + info.launchMode = MoeLaunchMode::IN_PROCESS; + MockFlag::modularObjectInfos = {info}; + MockFlag::processName = "com.test.process"; + AbilityRequest request; + request.abilityInfo.bundleName = "com.test.bundle"; + request.abilityInfo.name = "TestAbility"; + auto service = std::make_shared(); + AbilityRecord::processName_ = ""; + ModularObjectUtils::SetupNewRecord(request, service, "key_456"); + EXPECT_EQ(AbilityRecord::processName_, "com.test.process"); + GTEST_LOG_(INFO) << "SetupNewRecord_ShouldUseProcessNameWhenInProcessMode end"; +} + +HWTEST_F(ModularObjectUtilsTest, SetupNewRecord_ShouldUseBundleProcessModeWhenCrossProcessBundle, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "SetupNewRecord_ShouldUseBundleProcessModeWhenCrossProcessBundle start"; + ModularObjectExtensionInfo info; + info.bundleName = "com.test.bundle"; + info.abilityName = "TestAbility"; + info.launchMode = MoeLaunchMode::CROSS_PROCESS; + info.processMode = MoeProcessMode::BUNDLE; + MockFlag::modularObjectInfos = {info}; + AbilityRequest request; + request.abilityInfo.bundleName = "com.test.bundle"; + request.abilityInfo.name = "TestAbility"; + request.abilityInfo.extensionTypeName = "modularObject"; + auto service = std::make_shared(); + AbilityRecord::processName_ = ""; + ModularObjectUtils::SetupNewRecord(request, service, "key_202"); + EXPECT_EQ(AbilityRecord::processName_, "com.test.bundle:modularObject"); + GTEST_LOG_(INFO) << "SetupNewRecord_ShouldUseBundleProcessModeWhenCrossProcessBundle end"; +} + +HWTEST_F(ModularObjectUtilsTest, SetupNewRecord_ShouldUseTypeProcessModeWhenCrossProcessType, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "SetupNewRecord_ShouldUseTypeProcessModeWhenCrossProcessType start"; + ModularObjectExtensionInfo info; + info.bundleName = "com.test.bundle"; + info.abilityName = "TestAbility"; + info.launchMode = MoeLaunchMode::CROSS_PROCESS; + info.processMode = MoeProcessMode::TYPE; + MockFlag::modularObjectInfos = {info}; + AbilityRequest request; + request.abilityInfo.bundleName = "com.test.bundle"; + request.abilityInfo.name = "TestAbility"; + auto service = std::make_shared(); + AbilityRecord::processName_ = ""; + ModularObjectUtils::SetupNewRecord(request, service, "key_303"); + EXPECT_EQ(AbilityRecord::processName_, "com.test.bundle:TestAbility"); + GTEST_LOG_(INFO) << "SetupNewRecord_ShouldUseTypeProcessModeWhenCrossProcessType end"; +} + +HWTEST_F(ModularObjectUtilsTest, SetupNewRecord_ShouldUseInstanceProcessModeWhenCrossProcessInstance, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "SetupNewRecord_ShouldUseInstanceProcessModeWhenCrossProcessInstance start"; + ModularObjectExtensionInfo info; + info.bundleName = "com.test.bundle"; + info.abilityName = "TestAbility"; + info.launchMode = MoeLaunchMode::CROSS_PROCESS; + info.processMode = MoeProcessMode::INSTANCE; + MockFlag::modularObjectInfos = {info}; + AbilityRequest request; + request.abilityInfo.bundleName = "com.test.bundle"; + request.abilityInfo.name = "TestAbility"; + auto service = std::make_shared(); + AbilityRecord::recordId_ = 42; + AbilityRecord::processName_ = ""; + ModularObjectUtils::SetupNewRecord(request, service, "key_404"); + EXPECT_EQ(AbilityRecord::processName_, "com.test.bundle:TestAbility:42"); + GTEST_LOG_(INFO) << "SetupNewRecord_ShouldUseInstanceProcessModeWhenCrossProcessInstance end"; +} + +HWTEST_F(ModularObjectUtilsTest, SetupNewRecord_ShouldSetRequestIdWhenUnderscoreExists, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "SetupNewRecord_ShouldSetRequestIdWhenUnderscoreExists start"; + ModularObjectExtensionInfo info; + info.bundleName = "com.test.bundle"; + info.abilityName = "TestAbility"; + info.launchMode = MoeLaunchMode::CROSS_PROCESS; + info.processMode = MoeProcessMode::BUNDLE; + MockFlag::modularObjectInfos = {info}; + AbilityRequest request; + request.abilityInfo.bundleName = "com.test.bundle"; + request.abilityInfo.name = "TestAbility"; + request.abilityInfo.extensionTypeName = "modularObject"; + auto service = std::make_shared(); + BaseExtensionRecord::requestId_ = ""; + ModularObjectUtils::SetupNewRecord(request, service, "key_myrandid123"); + EXPECT_EQ(BaseExtensionRecord::requestId_, "myrandid123"); + GTEST_LOG_(INFO) << "SetupNewRecord_ShouldSetRequestIdWhenUnderscoreExists end"; +} + +HWTEST_F(ModularObjectUtilsTest, SetupNewRecord_ShouldNotSetRequestIdWhenEmptyServiceKey, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "SetupNewRecord_ShouldNotSetRequestIdWhenEmptyServiceKey start"; + ModularObjectExtensionInfo info; + info.bundleName = "com.test.bundle"; + info.abilityName = "TestAbility"; + info.launchMode = MoeLaunchMode::CROSS_PROCESS; + info.processMode = MoeProcessMode::BUNDLE; + MockFlag::modularObjectInfos = {info}; + AbilityRequest request; + request.abilityInfo.bundleName = "com.test.bundle"; + request.abilityInfo.name = "TestAbility"; + request.abilityInfo.extensionTypeName = "modularObject"; + auto service = std::make_shared(); + BaseExtensionRecord::requestId_ = "oldvalue"; + std::string emptyKey; + ModularObjectUtils::SetupNewRecord(request, service, emptyKey); + EXPECT_EQ(BaseExtensionRecord::requestId_, "oldvalue"); + GTEST_LOG_(INFO) << "SetupNewRecord_ShouldNotSetRequestIdWhenEmptyServiceKey end"; +} + +HWTEST_F(ModularObjectUtilsTest, SetupNewRecord_ShouldNotSetRequestIdWhenNoUnderscore, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "SetupNewRecord_ShouldNotSetRequestIdWhenNoUnderscore start"; + ModularObjectExtensionInfo info; + info.bundleName = "com.test.bundle"; + info.abilityName = "TestAbility"; + info.launchMode = MoeLaunchMode::CROSS_PROCESS; + info.processMode = MoeProcessMode::BUNDLE; + MockFlag::modularObjectInfos = {info}; + AbilityRequest request; + request.abilityInfo.bundleName = "com.test.bundle"; + request.abilityInfo.name = "TestAbility"; + request.abilityInfo.extensionTypeName = "modularObject"; + auto service = std::make_shared(); + BaseExtensionRecord::requestId_ = "oldvalue"; + ModularObjectUtils::SetupNewRecord(request, service, "noUnderscoreKey"); + EXPECT_EQ(BaseExtensionRecord::requestId_, "oldvalue"); + GTEST_LOG_(INFO) << "SetupNewRecord_ShouldNotSetRequestIdWhenNoUnderscore end"; +} + +HWTEST_F(ModularObjectUtilsTest, SetupNewRecord_ShouldSetEmptyRequestIdWhenTrailingUnderscore, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "SetupNewRecord_ShouldSetEmptyRequestIdWhenTrailingUnderscore start"; + ModularObjectExtensionInfo info; + info.bundleName = "com.test.bundle"; + info.abilityName = "TestAbility"; + info.launchMode = MoeLaunchMode::CROSS_PROCESS; + info.processMode = MoeProcessMode::BUNDLE; + MockFlag::modularObjectInfos = {info}; + AbilityRequest request; + request.abilityInfo.bundleName = "com.test.bundle"; + request.abilityInfo.name = "TestAbility"; + request.abilityInfo.extensionTypeName = "modularObject"; + auto service = std::make_shared(); + BaseExtensionRecord::requestId_ = "oldvalue"; + ModularObjectUtils::SetupNewRecord(request, service, "key_"); + EXPECT_EQ(BaseExtensionRecord::requestId_, ""); + GTEST_LOG_(INFO) << "SetupNewRecord_ShouldSetEmptyRequestIdWhenTrailingUnderscore end"; +} + +HWTEST_F(ModularObjectUtilsTest, SetupNewRecord_ShouldNotSetProcessNameWhenInvalidProcessMode, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "SetupNewRecord_ShouldNotSetProcessNameWhenInvalidProcessMode start"; + ModularObjectExtensionInfo info; + info.bundleName = "com.test.bundle"; + info.abilityName = "TestAbility"; + info.launchMode = MoeLaunchMode::CROSS_PROCESS; + info.processMode = static_cast(99); + MockFlag::modularObjectInfos = {info}; + AbilityRequest request; + request.abilityInfo.bundleName = "com.test.bundle"; + request.abilityInfo.name = "TestAbility"; + auto service = std::make_shared(); + AbilityRecord::processName_ = ""; + ModularObjectUtils::SetupNewRecord(request, service, "key_123"); + EXPECT_EQ(AbilityRecord::processName_, ""); + GTEST_LOG_(INFO) << "SetupNewRecord_ShouldNotSetProcessNameWhenInvalidProcessMode end"; +} + +HWTEST_F(ModularObjectUtilsTest, + SetupNewRecord_ShouldAppendAppCloneIndexWhenCrossProcessBundleWithClone, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "SetupNewRecord_ShouldAppendAppCloneIndexWhenCrossProcessBundleWithClone start"; + // CROSS_PROCESS + BUNDLE mode + appCloneIndex > 0 + ModularObjectExtensionInfo info; + info.bundleName = "com.test.bundle"; + info.abilityName = "TestAbility"; + info.launchMode = MoeLaunchMode::CROSS_PROCESS; + info.processMode = MoeProcessMode::BUNDLE; + MockFlag::modularObjectInfos = {info}; + AbilityRequest request; + request.abilityInfo.bundleName = "com.test.bundle"; + request.abilityInfo.name = "TestAbility"; + request.abilityInfo.extensionTypeName = "modularObject"; + request.appInfo.appIndex = 2; + auto service = std::make_shared(); + AbilityRecord::processName_ = ""; + ModularObjectUtils::SetupNewRecord(request, service, "key_500"); + EXPECT_EQ(AbilityRecord::processName_, "com.test.bundle:modularObject:2"); + GTEST_LOG_(INFO) << "SetupNewRecord_ShouldAppendAppCloneIndexWhenCrossProcessBundleWithClone end"; +} + +HWTEST_F(ModularObjectUtilsTest, + SetupNewRecord_ShouldAppendAppCloneIndexWhenCrossProcessTypeWithClone, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "SetupNewRecord_ShouldAppendAppCloneIndexWhenCrossProcessTypeWithClone start"; + // CROSS_PROCESS + TYPE mode + appCloneIndex > 0 + ModularObjectExtensionInfo info; + info.bundleName = "com.test.bundle"; + info.abilityName = "TestAbility"; + info.launchMode = MoeLaunchMode::CROSS_PROCESS; + info.processMode = MoeProcessMode::TYPE; + MockFlag::modularObjectInfos = {info}; + AbilityRequest request; + request.abilityInfo.bundleName = "com.test.bundle"; + request.abilityInfo.name = "TestAbility"; + request.appInfo.appIndex = 3; + auto service = std::make_shared(); + AbilityRecord::processName_ = ""; + ModularObjectUtils::SetupNewRecord(request, service, "key_600"); + EXPECT_EQ(AbilityRecord::processName_, "com.test.bundle:TestAbility:3"); + GTEST_LOG_(INFO) << "SetupNewRecord_ShouldAppendAppCloneIndexWhenCrossProcessTypeWithClone end"; +} + +HWTEST_F(ModularObjectUtilsTest, + SetupNewRecord_ShouldNotAppendAppCloneIndexWhenInProcessWithClone, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "SetupNewRecord_ShouldNotAppendAppCloneIndexWhenInProcessWithClone start"; + // IN_PROCESS mode + appCloneIndex > 0 → should NOT append + ModularObjectExtensionInfo info; + info.bundleName = "com.test.bundle"; + info.abilityName = "TestAbility"; + info.launchMode = MoeLaunchMode::IN_PROCESS; + MockFlag::modularObjectInfos = {info}; + MockFlag::processName = "com.test.process"; + AbilityRequest request; + request.abilityInfo.bundleName = "com.test.bundle"; + request.abilityInfo.name = "TestAbility"; + request.appInfo.appIndex = 2; + auto service = std::make_shared(); + AbilityRecord::processName_ = ""; + ModularObjectUtils::SetupNewRecord(request, service, "key_700"); + EXPECT_EQ(AbilityRecord::processName_, "com.test.process"); + GTEST_LOG_(INFO) << "SetupNewRecord_ShouldNotAppendAppCloneIndexWhenInProcessWithClone end"; +} + +HWTEST_F(ModularObjectUtilsTest, + SetupNewRecord_ShouldReturnErrorWhenInProcessGetProcessInfoFailed, TestSize.Level1) +{ + GTEST_LOG_(INFO) << "SetupNewRecord_ShouldReturnErrorWhenInProcessGetProcessInfoFailed start"; + // IN_PROCESS + GetRunningProcessInfoByPid fails → return error + ModularObjectExtensionInfo info; + info.bundleName = "com.test.bundle"; + info.abilityName = "TestAbility"; + info.launchMode = MoeLaunchMode::IN_PROCESS; + MockFlag::modularObjectInfos = {info}; + MockFlag::getRunningProcessInfoRet = -1; + AbilityRequest request; + request.abilityInfo.bundleName = "com.test.bundle"; + request.abilityInfo.name = "TestAbility"; + request.appInfo.appIndex = 2; + auto service = std::make_shared(); + AbilityRecord::processName_ = ""; + auto ret = ModularObjectUtils::SetupNewRecord(request, service, "key_800"); + EXPECT_NE(ret, ERR_OK); + MockFlag::getRunningProcessInfoRet = 0; + GTEST_LOG_(INFO) << "SetupNewRecord_ShouldReturnErrorWhenInProcessGetProcessInfoFailed end"; +} diff --git a/test/unittest/multi_user_config_mgr_test/BUILD.gn b/test/unittest/multi_user_config_mgr_test/BUILD.gn index 11234200c2..175e47384e 100644 --- a/test/unittest/multi_user_config_mgr_test/BUILD.gn +++ b/test/unittest/multi_user_config_mgr_test/BUILD.gn @@ -50,6 +50,7 @@ ohos_unittest("multi_user_config_mgr_test") { "i18n:i18n_sa_client", "init:libbegetutil", "ipc:ipc_core", + "ipc:ipc_single", "os_account:libaccountkits", "os_account:os_account_innerkits", "window_manager:libwm", diff --git a/test/unittest/native_ability_wrapper_test/BUILD.gn b/test/unittest/native_ability_wrapper_test/BUILD.gn new file mode 100644 index 0000000000..908ae897ca --- /dev/null +++ b/test/unittest/native_ability_wrapper_test/BUILD.gn @@ -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/capi_ability_runtime" + +ohos_unittest("native_ability_wrapper_test") { + module_out_path = module_output_path + + cflags_cc = [] + include_dirs = [] + + sources = [ "native_ability_wrapper_test.cpp" ] + + include_dirs = [ + "${ability_runtime_ndk_path}/ability_runtime", + "${ability_runtime_path}/frameworks/c/ability_runtime/include", + "${ability_runtime_path}/interfaces/kits/native/appkit/ability_runtime/context", + "${ability_runtime_path}/interfaces/inner_api/", + "${ability_runtime_path}/interfaces/inner_api/ability_manager/include", + "${ability_runtime_path}/services/common/include", + ] + + deps = [ + "${ability_runtime_path}/frameworks/c/ability_runtime:ability_runtime", + ] + + external_deps = [ + "c_utils:utils", + "googletest:gmock_main", + "googletest:gtest_main", + "hilog:libhilog", + "napi:ace_napi", + ] +} + +group("unittest") { + testonly = true + deps = [ ":native_ability_wrapper_test" ] +} \ No newline at end of file diff --git a/test/unittest/native_ability_wrapper_test/native_ability_wrapper_test.cpp b/test/unittest/native_ability_wrapper_test/native_ability_wrapper_test.cpp new file mode 100644 index 0000000000..7164aae16a --- /dev/null +++ b/test/unittest/native_ability_wrapper_test/native_ability_wrapper_test.cpp @@ -0,0 +1,415 @@ +/* + * 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 "hilog_tag_wrapper.h" +#include "native_ability_wrapper.h" +#include "ability_native_thread.h" + +using namespace testing::ext; + +constexpr int32_t UUID_BUFFER_SIZE = 37; // UUID format: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx + '\0' + +class NativeAbilityWrapperTest : public ::testing::Test { +protected: + void SetUp() override + { + wrapper_ = new AbilityRuntime_NativeAbilityWrapper(); + } + + void TearDown() override + { + delete wrapper_; + wrapper_ = nullptr; + } + + AbilityRuntime_NativeAbilityWrapper* wrapper_ = nullptr; +}; + +/** + * @tc.name: OH_AbilityRuntime_GetAbilityInstanceId_001 + * @tc.desc: Test GetAbilityInstanceId with null wrapper pointer + * @tc.type: FUNC + */ +HWTEST_F(NativeAbilityWrapperTest, OH_AbilityRuntime_GetAbilityInstanceId_001, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "OH_AbilityRuntime_GetAbilityInstanceId_001 begin"); + char buffer[UUID_BUFFER_SIZE] = {0}; + AbilityRuntime_ErrorCode result = OH_AbilityRuntime_GetAbilityInstanceId(nullptr, buffer, UUID_BUFFER_SIZE); + EXPECT_EQ(ABILITY_RUNTIME_ERROR_CODE_PARAM_INVALID, result); + TAG_LOGI(AAFwkTag::TEST, "OH_AbilityRuntime_GetAbilityInstanceId_001 end"); +} + +/** + * @tc.name: OH_AbilityRuntime_GetAbilityInstanceId_002 + * @tc.desc: Test GetAbilityInstanceId with null buffer pointer + * @tc.type: FUNC + */ +HWTEST_F(NativeAbilityWrapperTest, OH_AbilityRuntime_GetAbilityInstanceId_002, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "OH_AbilityRuntime_GetAbilityInstanceId_002 begin"); + AbilityRuntime_ErrorCode result = OH_AbilityRuntime_GetAbilityInstanceId(wrapper_, nullptr, UUID_BUFFER_SIZE); + EXPECT_EQ(ABILITY_RUNTIME_ERROR_CODE_PARAM_INVALID, result); + TAG_LOGI(AAFwkTag::TEST, "OH_AbilityRuntime_GetAbilityInstanceId_002 end"); +} + +/** + * @tc.name: OH_AbilityRuntime_GetAbilityInstanceId_003 + * @tc.desc: Test GetAbilityInstanceId with buffer size less than 37 + * @tc.type: FUNC + */ +HWTEST_F(NativeAbilityWrapperTest, OH_AbilityRuntime_GetAbilityInstanceId_003, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "OH_AbilityRuntime_GetAbilityInstanceId_003 begin"); + char buffer[UUID_BUFFER_SIZE] = {0}; + AbilityRuntime_ErrorCode result = OH_AbilityRuntime_GetAbilityInstanceId(wrapper_, buffer, 36); + EXPECT_EQ(ABILITY_RUNTIME_ERROR_CODE_PARAM_INVALID, result); + + result = OH_AbilityRuntime_GetAbilityInstanceId(wrapper_, buffer, 0); + EXPECT_EQ(ABILITY_RUNTIME_ERROR_CODE_PARAM_INVALID, result); + + result = OH_AbilityRuntime_GetAbilityInstanceId(wrapper_, buffer, -1); + EXPECT_EQ(ABILITY_RUNTIME_ERROR_CODE_PARAM_INVALID, result); + TAG_LOGI(AAFwkTag::TEST, "OH_AbilityRuntime_GetAbilityInstanceId_003 end"); +} + +/** + * @tc.name: OH_AbilityRuntime_GetAbilityInstanceId_004 + * @tc.desc: Test GetAbilityInstanceId with empty instanceId in wrapper + * @tc.type: FUNC + */ +HWTEST_F(NativeAbilityWrapperTest, OH_AbilityRuntime_GetAbilityInstanceId_004, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "OH_AbilityRuntime_GetAbilityInstanceId_004 begin"); + wrapper_->instanceId = ""; + char buffer[UUID_BUFFER_SIZE] = {0}; + AbilityRuntime_ErrorCode result = OH_AbilityRuntime_GetAbilityInstanceId(wrapper_, buffer, UUID_BUFFER_SIZE); + EXPECT_EQ(ABILITY_RUNTIME_ERROR_CODE_PARAM_INVALID, result); + TAG_LOGI(AAFwkTag::TEST, "OH_AbilityRuntime_GetAbilityInstanceId_004 end"); +} + +/** + * @tc.name: OH_AbilityRuntime_GetAbilityInstanceId_005 + * @tc.desc: Test GetAbilityInstanceId with valid parameters and UUID format instanceId + * @tc.type: FUNC + */ +HWTEST_F(NativeAbilityWrapperTest, OH_AbilityRuntime_GetAbilityInstanceId_005, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "OH_AbilityRuntime_GetAbilityInstanceId_005 begin"); + const std::string testInstanceId = "12345678-1234-1234-1234-123456789abc"; + wrapper_->instanceId = testInstanceId; + char buffer[UUID_BUFFER_SIZE] = {0}; + AbilityRuntime_ErrorCode result = OH_AbilityRuntime_GetAbilityInstanceId(wrapper_, buffer, UUID_BUFFER_SIZE); + EXPECT_EQ(ABILITY_RUNTIME_ERROR_CODE_NO_ERROR, result); + EXPECT_EQ(testInstanceId, std::string(buffer)); + TAG_LOGI(AAFwkTag::TEST, "OH_AbilityRuntime_GetAbilityInstanceId_005 end"); +} + +/** + * @tc.name: OH_AbilityRuntime_GetAbilityInstanceId_006 + * @tc.desc: Test GetAbilityInstanceId with buffer size exactly 37 (minimum required) + * @tc.type: FUNC + */ +HWTEST_F(NativeAbilityWrapperTest, OH_AbilityRuntime_GetAbilityInstanceId_006, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "OH_AbilityRuntime_GetAbilityInstanceId_006 begin"); + const std::string testInstanceId = "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"; + wrapper_->instanceId = testInstanceId; + char buffer[UUID_BUFFER_SIZE] = {0}; + AbilityRuntime_ErrorCode result = OH_AbilityRuntime_GetAbilityInstanceId(wrapper_, buffer, UUID_BUFFER_SIZE); + EXPECT_EQ(ABILITY_RUNTIME_ERROR_CODE_NO_ERROR, result); + EXPECT_EQ(testInstanceId, std::string(buffer)); + TAG_LOGI(AAFwkTag::TEST, "OH_AbilityRuntime_GetAbilityInstanceId_006 end"); +} + +/** + * @tc.name: OH_AbilityRuntime_GetAbilityInstanceId_007 + * @tc.desc: Test GetAbilityInstanceId with buffer size larger than 37 + * @tc.type: FUNC + */ +HWTEST_F(NativeAbilityWrapperTest, OH_AbilityRuntime_GetAbilityInstanceId_007, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "OH_AbilityRuntime_GetAbilityInstanceId_007 begin"); + const std::string testInstanceId = "11111111-2222-3333-4444-555555555555"; + wrapper_->instanceId = testInstanceId; + char buffer[100] = {0}; + AbilityRuntime_ErrorCode result = OH_AbilityRuntime_GetAbilityInstanceId(wrapper_, buffer, 100); + EXPECT_EQ(ABILITY_RUNTIME_ERROR_CODE_NO_ERROR, result); + EXPECT_EQ(testInstanceId, std::string(buffer)); + TAG_LOGI(AAFwkTag::TEST, "OH_AbilityRuntime_GetAbilityInstanceId_007 end"); +} + +/** + * @tc.name: OH_AbilityRuntime_GetAbilityName_001 + * @tc.desc: Test GetAbilityName with null wrapper pointer + * @tc.type: FUNC + */ +HWTEST_F(NativeAbilityWrapperTest, OH_AbilityRuntime_GetAbilityName_001, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "OH_AbilityRuntime_GetAbilityName_001 begin"); + char buffer[100] = {0}; + int32_t writeLength = 0; + AbilityRuntime_ErrorCode result = OH_AbilityRuntime_GetAbilityName(nullptr, buffer, 100, &writeLength); + EXPECT_EQ(ABILITY_RUNTIME_ERROR_CODE_PARAM_INVALID, result); + TAG_LOGI(AAFwkTag::TEST, "OH_AbilityRuntime_GetAbilityName_001 end"); +} + +/** + * @tc.name: OH_AbilityRuntime_GetAbilityName_002 + * @tc.desc: Test GetAbilityName with null writeLength pointer + * @tc.type: FUNC + */ +HWTEST_F(NativeAbilityWrapperTest, OH_AbilityRuntime_GetAbilityName_002, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "OH_AbilityRuntime_GetAbilityName_002 begin"); + wrapper_->abilityName = "TestAbility"; + char buffer[100] = {0}; + AbilityRuntime_ErrorCode result = OH_AbilityRuntime_GetAbilityName(wrapper_, buffer, 100, nullptr); + EXPECT_EQ(ABILITY_RUNTIME_ERROR_CODE_PARAM_INVALID, result); + TAG_LOGI(AAFwkTag::TEST, "OH_AbilityRuntime_GetAbilityName_002 end"); +} + +/** + * @tc.name: OH_AbilityRuntime_GetAbilityName_003 + * @tc.desc: Test GetAbilityName with empty abilityName in wrapper + * @tc.type: FUNC + */ +HWTEST_F(NativeAbilityWrapperTest, OH_AbilityRuntime_GetAbilityName_003, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "OH_AbilityRuntime_GetAbilityName_003 begin"); + wrapper_->abilityName = ""; + char buffer[100] = {0}; + int32_t writeLength = 0; + AbilityRuntime_ErrorCode result = OH_AbilityRuntime_GetAbilityName(wrapper_, buffer, 100, &writeLength); + EXPECT_EQ(ABILITY_RUNTIME_ERROR_CODE_ABILITY_WRAPPER_INVALID, result); + TAG_LOGI(AAFwkTag::TEST, "OH_AbilityRuntime_GetAbilityName_003 end"); +} + +/** + * @tc.name: OH_AbilityRuntime_GetAbilityName_004 + * @tc.desc: Test GetAbilityName with null buffer (query length mode) + * @tc.type: FUNC + */ +HWTEST_F(NativeAbilityWrapperTest, OH_AbilityRuntime_GetAbilityName_004, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "OH_AbilityRuntime_GetAbilityName_004 begin"); + const std::string testAbilityName = "MainAbility"; + wrapper_->abilityName = testAbilityName; + int32_t writeLength = 0; + AbilityRuntime_ErrorCode result = OH_AbilityRuntime_GetAbilityName(wrapper_, nullptr, 0, &writeLength); + EXPECT_EQ(ABILITY_RUNTIME_ERROR_CODE_NO_ERROR, result); + EXPECT_EQ(static_cast(testAbilityName.length()), writeLength); + TAG_LOGI(AAFwkTag::TEST, "OH_AbilityRuntime_GetAbilityName_004 end"); +} + +/** + * @tc.name: OH_AbilityRuntime_GetAbilityName_005 + * @tc.desc: Test GetAbilityName with bufferSize <= 0 + * @tc.type: FUNC + */ +HWTEST_F(NativeAbilityWrapperTest, OH_AbilityRuntime_GetAbilityName_005, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "OH_AbilityRuntime_GetAbilityName_005 begin"); + wrapper_->abilityName = "TestAbility"; + char buffer[100] = {0}; + int32_t writeLength = 0; + + AbilityRuntime_ErrorCode result = OH_AbilityRuntime_GetAbilityName(wrapper_, buffer, 0, &writeLength); + EXPECT_EQ(ABILITY_RUNTIME_ERROR_CODE_PARAM_INVALID, result); + + result = OH_AbilityRuntime_GetAbilityName(wrapper_, buffer, -1, &writeLength); + EXPECT_EQ(ABILITY_RUNTIME_ERROR_CODE_PARAM_INVALID, result); + TAG_LOGI(AAFwkTag::TEST, "OH_AbilityRuntime_GetAbilityName_005 end"); +} + +/** + * @tc.name: OH_AbilityRuntime_GetAbilityName_006 + * @tc.desc: Test GetAbilityName with bufferSize less than abilityName length + * @tc.type: FUNC + */ +HWTEST_F(NativeAbilityWrapperTest, OH_AbilityRuntime_GetAbilityName_006, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "OH_AbilityRuntime_GetAbilityName_006 begin"); + const std::string testAbilityName = "VeryLongAbilityNameForTesting"; + wrapper_->abilityName = testAbilityName; + char buffer[5] = {0}; + int32_t writeLength = 0; + AbilityRuntime_ErrorCode result = OH_AbilityRuntime_GetAbilityName(wrapper_, buffer, 5, &writeLength); + EXPECT_EQ(ABILITY_RUNTIME_ERROR_CODE_PARAM_INVALID, result); + TAG_LOGI(AAFwkTag::TEST, "OH_AbilityRuntime_GetAbilityName_006 end"); +} + +/** + * @tc.name: OH_AbilityRuntime_GetAbilityName_007 + * @tc.desc: Test GetAbilityName with bufferSize equal to abilityName length + 1 (minimum required) + * @tc.type: FUNC + */ +HWTEST_F(NativeAbilityWrapperTest, OH_AbilityRuntime_GetAbilityName_007, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "OH_AbilityRuntime_GetAbilityName_007 begin"); + const std::string testAbilityName = "ShortName"; + wrapper_->abilityName = testAbilityName; + int32_t nameLength = static_cast(testAbilityName.length()); + char buffer[10] = {0}; + int32_t writeLength = 0; + AbilityRuntime_ErrorCode result = OH_AbilityRuntime_GetAbilityName(wrapper_, buffer, nameLength + 1, &writeLength); + EXPECT_EQ(ABILITY_RUNTIME_ERROR_CODE_NO_ERROR, result); + EXPECT_EQ(nameLength, writeLength); + EXPECT_EQ(testAbilityName, std::string(buffer)); + TAG_LOGI(AAFwkTag::TEST, "OH_AbilityRuntime_GetAbilityName_007 end"); +} + +/** + * @tc.name: OH_AbilityRuntime_GetAbilityName_008 + * @tc.desc: Test GetAbilityName with bufferSize exactly equal to name length (no room for '\0', should fail) + * @tc.type: FUNC + */ +HWTEST_F(NativeAbilityWrapperTest, OH_AbilityRuntime_GetAbilityName_008, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "OH_AbilityRuntime_GetAbilityName_008 begin"); + const std::string testAbilityName = "Test"; + wrapper_->abilityName = testAbilityName; + int32_t nameLength = static_cast(testAbilityName.length()); + char buffer[4] = {0}; + int32_t writeLength = 0; + AbilityRuntime_ErrorCode result = OH_AbilityRuntime_GetAbilityName(wrapper_, buffer, nameLength, &writeLength); + EXPECT_EQ(ABILITY_RUNTIME_ERROR_CODE_PARAM_INVALID, result); + TAG_LOGI(AAFwkTag::TEST, "OH_AbilityRuntime_GetAbilityName_008 end"); +} + +/** + * @tc.name: OH_AbilityRuntime_GetAbilityName_009 + * @tc.desc: Test GetAbilityName with valid parameters + * @tc.type: FUNC + */ +HWTEST_F(NativeAbilityWrapperTest, OH_AbilityRuntime_GetAbilityName_009, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "OH_AbilityRuntime_GetAbilityName_009 begin"); + const std::string testAbilityName = "EntryAbility"; + wrapper_->abilityName = testAbilityName; + char buffer[100] = {0}; + int32_t writeLength = 0; + AbilityRuntime_ErrorCode result = OH_AbilityRuntime_GetAbilityName(wrapper_, buffer, 100, &writeLength); + EXPECT_EQ(ABILITY_RUNTIME_ERROR_CODE_NO_ERROR, result); + EXPECT_EQ(static_cast(testAbilityName.length()), writeLength); + EXPECT_EQ(testAbilityName, std::string(buffer)); + TAG_LOGI(AAFwkTag::TEST, "OH_AbilityRuntime_GetAbilityName_009 end"); +} + +/** + * @tc.name: OH_AbilityRuntime_GetAbilityName_010 + * @tc.desc: Test GetAbilityName with one character abilityName + * @tc.type: FUNC + */ +HWTEST_F(NativeAbilityWrapperTest, OH_AbilityRuntime_GetAbilityName_010, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "OH_AbilityRuntime_GetAbilityName_010 begin"); + const std::string testAbilityName = "A"; + wrapper_->abilityName = testAbilityName; + char buffer[10] = {0}; + int32_t writeLength = 0; + AbilityRuntime_ErrorCode result = OH_AbilityRuntime_GetAbilityName(wrapper_, buffer, 10, &writeLength); + EXPECT_EQ(ABILITY_RUNTIME_ERROR_CODE_NO_ERROR, result); + EXPECT_EQ(1, writeLength); + EXPECT_EQ(testAbilityName, std::string(buffer)); + TAG_LOGI(AAFwkTag::TEST, "OH_AbilityRuntime_GetAbilityName_010 end"); +} + +/** + * @tc.name: OH_AbilityRuntime_GetEnv_001 + * @tc.desc: Test GetEnv with null wrapper pointer + * @tc.type: FUNC + */ +HWTEST_F(NativeAbilityWrapperTest, OH_AbilityRuntime_GetEnv_001, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "OH_AbilityRuntime_GetEnv_001 begin"); + napi_env env = nullptr; + AbilityRuntime_ErrorCode result = OH_AbilityRuntime_GetEnv(nullptr, &env); + EXPECT_EQ(ABILITY_RUNTIME_ERROR_CODE_PARAM_INVALID, result); + TAG_LOGI(AAFwkTag::TEST, "OH_AbilityRuntime_GetEnv_001 end"); +} + +/** + * @tc.name: OH_AbilityRuntime_GetEnv_002 + * @tc.desc: Test GetEnv with null env pointer + * @tc.type: FUNC + */ +HWTEST_F(NativeAbilityWrapperTest, OH_AbilityRuntime_GetEnv_002, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "OH_AbilityRuntime_GetEnv_002 begin"); + AbilityRuntime_ErrorCode result = OH_AbilityRuntime_GetEnv(wrapper_, nullptr); + EXPECT_EQ(ABILITY_RUNTIME_ERROR_CODE_PARAM_INVALID, result); + TAG_LOGI(AAFwkTag::TEST, "OH_AbilityRuntime_GetEnv_002 end"); +} + +/** + * @tc.name: OH_AbilityRuntime_GetEnv_003 + * @tc.desc: Test GetEnv with null env in wrapper + * @tc.type: FUNC + */ +HWTEST_F(NativeAbilityWrapperTest, OH_AbilityRuntime_GetEnv_003, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "OH_AbilityRuntime_GetEnv_003 begin"); + wrapper_->env = nullptr; + napi_env env = nullptr; + AbilityRuntime_ErrorCode result = OH_AbilityRuntime_GetEnv(wrapper_, &env); + EXPECT_EQ(ABILITY_RUNTIME_ERROR_CODE_ABILITY_WRAPPER_INVALID, result); + TAG_LOGI(AAFwkTag::TEST, "OH_AbilityRuntime_GetEnv_003 end"); +} + +/** + * @tc.name: OH_AbilityRuntime_GetEnv_004 + * @tc.desc: Test GetEnv with valid env in wrapper + * @tc.type: FUNC + */ +HWTEST_F(NativeAbilityWrapperTest, OH_AbilityRuntime_GetEnv_004, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "OH_AbilityRuntime_GetEnv_004 begin"); + napi_env fakeEnv = reinterpret_cast(0x12345678); + wrapper_->env = fakeEnv; + napi_env resultEnv = nullptr; + AbilityRuntime_ErrorCode result = OH_AbilityRuntime_GetEnv(wrapper_, &resultEnv); + EXPECT_EQ(ABILITY_RUNTIME_ERROR_CODE_NO_ERROR, result); + EXPECT_EQ(fakeEnv, resultEnv); + TAG_LOGI(AAFwkTag::TEST, "OH_AbilityRuntime_GetEnv_004 end"); +} + +/** + * @tc.name: OH_AbilityRuntime_GetEnv_005 + * @tc.desc: Test GetEnv with both wrapper and env param null + * @tc.type: FUNC + */ +HWTEST_F(NativeAbilityWrapperTest, OH_AbilityRuntime_GetEnv_005, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "OH_AbilityRuntime_GetEnv_005 begin"); + AbilityRuntime_ErrorCode result = OH_AbilityRuntime_GetEnv(nullptr, nullptr); + EXPECT_EQ(ABILITY_RUNTIME_ERROR_CODE_PARAM_INVALID, result); + TAG_LOGI(AAFwkTag::TEST, "OH_AbilityRuntime_GetEnv_005 end"); +} + +/** + * @tc.name: OH_AbilityRuntime_GetEnv_006 + * @tc.desc: Test GetEnv resets env value when wrapper env is null + * @tc.type: FUNC + */ +HWTEST_F(NativeAbilityWrapperTest, OH_AbilityRuntime_GetEnv_006, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "OH_AbilityRuntime_GetEnv_006 begin"); + wrapper_->env = nullptr; + napi_env env = reinterpret_cast(0x99999999); + AbilityRuntime_ErrorCode result = OH_AbilityRuntime_GetEnv(wrapper_, &env); + EXPECT_EQ(ABILITY_RUNTIME_ERROR_CODE_ABILITY_WRAPPER_INVALID, result); + TAG_LOGI(AAFwkTag::TEST, "OH_AbilityRuntime_GetEnv_006 end"); +} \ No newline at end of file diff --git a/test/unittest/preload_manager_service_test/preload_manager_service_test.cpp b/test/unittest/preload_manager_service_test/preload_manager_service_test.cpp index 16f2352941..87d7eb635f 100644 --- a/test/unittest/preload_manager_service_test/preload_manager_service_test.cpp +++ b/test/unittest/preload_manager_service_test/preload_manager_service_test.cpp @@ -348,5 +348,107 @@ HWTEST_F(PreloadManagerServiceTest, PreloadApplication_011, TestSize.Level1) TAG_LOGI(AAFwkTag::TEST, "PreloadManagerServiceTest PreloadApplication_011 end"); } + +/* + * Feature: PreloadManagerService + * Name: LaunchGameCustomized_001 + * Function: LaunchGameCustomized + * SubFunction: NA + * FunctionPoints: PreloadManagerService LaunchGameCustomized verification failed + */ +HWTEST_F(PreloadManagerServiceTest, LaunchGameCustomized_001, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "PreloadManagerServiceTest LaunchGameCustomized_001 start"); + + // Set up for verification failure + MyStatus::GetInstance().isMultiUserConcurrency_ = false; + std::string bundleName = "com.test.game"; + int32_t userId = 100; + int32_t appIndex = 0; + + // PreloadApplicationVerification will fail due to isMultiUserConcurrency_ = false + auto result = PreloadManagerService::GetInstance().LaunchGameCustomized(bundleName, userId, appIndex); + EXPECT_EQ(result, ERR_CROSS_USER); + + TAG_LOGI(AAFwkTag::TEST, "PreloadManagerServiceTest LaunchGameCustomized_001 end"); +} + +/* + * Feature: PreloadManagerService + * Name: LaunchGameCustomized_002 + * Function: LaunchGameCustomized + * SubFunction: NA + * FunctionPoints: PreloadManagerService LaunchGameCustomized record already exist + */ +HWTEST_F(PreloadManagerServiceTest, LaunchGameCustomized_002, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "PreloadManagerServiceTest LaunchGameCustomized_002 start"); + + // Set up for record already exist + MyStatus::GetInstance().isMultiUserConcurrency_ = true; + MyStatus::GetInstance().retCheckPreloadAppRecordExist_ = ERR_OK; + MyStatus::GetInstance().isPreloadApplicationRecordExist_ = true; + std::string bundleName = "com.test.game"; + int32_t userId = 100; + int32_t appIndex = 0; + + auto result = PreloadManagerService::GetInstance().LaunchGameCustomized(bundleName, userId, appIndex); + EXPECT_EQ(result, ERR_PRELOAD_APP_RECORD_ALREADY_EXIST); + + TAG_LOGI(AAFwkTag::TEST, "PreloadManagerServiceTest LaunchGameCustomized_002 end"); +} + +/* + * Feature: PreloadManagerService + * Name: LaunchGameCustomized_003 + * Function: LaunchGameCustomized + * SubFunction: NA + * FunctionPoints: PreloadManagerService LaunchGameCustomized bundle mgr helper is null + */ +HWTEST_F(PreloadManagerServiceTest, LaunchGameCustomized_003, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "PreloadManagerServiceTest LaunchGameCustomized_003 start"); + + // Set up for bundle mgr helper null + MyStatus::GetInstance().isMultiUserConcurrency_ = true; + MyStatus::GetInstance().retCheckPreloadAppRecordExist_ = ERR_OK; + MyStatus::GetInstance().isPreloadApplicationRecordExist_ = false; + MyStatus::GetInstance().bundleMgrHelper_ = nullptr; + std::string bundleName = "com.test.game"; + int32_t userId = 100; + int32_t appIndex = 0; + + auto result = PreloadManagerService::GetInstance().LaunchGameCustomized(bundleName, userId, appIndex); + EXPECT_EQ(result, INNER_ERR); + + TAG_LOGI(AAFwkTag::TEST, "PreloadManagerServiceTest LaunchGameCustomized_003 end"); +} + +/* + * Feature: PreloadManagerService + * Name: LaunchGameCustomized_004 + * Function: LaunchGameCustomized + * SubFunction: NA + * FunctionPoints: PreloadManagerService LaunchGameCustomized get launch want failed + */ +HWTEST_F(PreloadManagerServiceTest, LaunchGameCustomized_004, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "PreloadManagerServiceTest LaunchGameCustomized_004 start"); + + // Set up for GetLaunchWantForBundle failure + MyStatus::GetInstance().isMultiUserConcurrency_ = true; + MyStatus::GetInstance().retCheckPreloadAppRecordExist_ = ERR_OK; + MyStatus::GetInstance().isPreloadApplicationRecordExist_ = false; + MyStatus::GetInstance().bundleMgrHelper_ = DelayedSingleton::GetInstance(); + MyStatus::GetInstance().retGetLaunchWantForBundle_ = -1; + std::string bundleName = "com.test.game"; + int32_t userId = 100; + int32_t appIndex = 0; + + auto result = PreloadManagerService::GetInstance().LaunchGameCustomized(bundleName, userId, appIndex); + EXPECT_EQ(result, -1); + + TAG_LOGI(AAFwkTag::TEST, "PreloadManagerServiceTest LaunchGameCustomized_004 end"); +} } // namespace AAFwk } // namespace OHOS diff --git a/test/unittest/runtime_test/ets_runtime_test.cpp b/test/unittest/runtime_test/ets_runtime_test.cpp index d45f9a2374..6a023dfc8c 100644 --- a/test/unittest/runtime_test/ets_runtime_test.cpp +++ b/test/unittest/runtime_test/ets_runtime_test.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 Huawei Device Co., Ltd. + * Copyright (c) 2025-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 @@ -15,6 +15,8 @@ #include #include +#include +#include #define private public #define protected public @@ -43,6 +45,25 @@ const std::string TEST_CODE_PATH = "/data/storage/el1/bundle"; const std::string TEST_HAP_PATH = "/system/app/com.ohos.contactsdataabilityContacts_DataAbility.hap"; const std::string TEST_LIB_PATH = "/data/storage/el1/bundle/lib/"; const std::string TEST_MODULE_PATH = "/data/storage/el1/bundle/curJsModulePath"; +const std::string TEST_HOST_AOT_ROOT = "/system/app/ark_cache/"; +const std::string TEST_HOST_AOT_BUNDLE_NAME = "com.example.hostaot"; + +std::string GetHostAotAnPath(const std::string &bundleName, const std::string &moduleName) +{ + return TEST_HOST_AOT_ROOT + bundleName + "/" + moduleName + ".an"; +} + +void CreateHostAotAnFile(const std::string &bundleName, const std::string &moduleName) +{ + std::string anPath = GetHostAotAnPath(bundleName, moduleName); + std::string mkdirCmd = "mkdir -p " + TEST_HOST_AOT_ROOT + bundleName; + ASSERT_EQ(std::system(mkdirCmd.c_str()), 0); + + std::ofstream anFile(anPath); + ASSERT_TRUE(anFile.is_open()); + anFile << "test"; + anFile.close(); +} } // namespace class EtsRuntimeTest : public testing::Test { @@ -72,7 +93,11 @@ void EtsRuntimeTest::SetUp() options_.eventRunner = eventRunner; } -void EtsRuntimeTest::TearDown() {} +void EtsRuntimeTest::TearDown() +{ + std::string rmCmd = "rm -rf " + TEST_HOST_AOT_ROOT + TEST_HOST_AOT_BUNDLE_NAME; + std::system(rmCmd.c_str()); +} /** * @tc.name: Create_100 @@ -725,6 +750,88 @@ HWTEST_F(EtsRuntimeTest, GetAotPath_012, TestSize.Level1) EXPECT_NE(result.find("hspModule1.an"), std::string::npos); } +/** + * @tc.name: GetAotPath_013 + * @tc.desc: Test GetAotPath loads preinstall host AOT an file when aot status is failed. + * @tc.type: FUNC + */ +HWTEST_F(EtsRuntimeTest, GetAotPath_013, TestSize.Level1) +{ + auto etsRuntime = std::make_unique(); + Runtime::Options options; + options.bundleName = TEST_HOST_AOT_BUNDLE_NAME; + options.aotCompileStatusMap = { + {"entry", static_cast(AppExecFwk::AOTCompileStatus::NOT_COMPILED)} + }; + options.commonHspBundleInfos = {}; + CreateHostAotAnFile(TEST_HOST_AOT_BUNDLE_NAME, "entry"); + + auto result = etsRuntime->GetAotPath(options); + EXPECT_EQ(result, GetHostAotAnPath(TEST_HOST_AOT_BUNDLE_NAME, "entry")); +} + +/** + * @tc.name: GetAotPath_014 + * @tc.desc: Test GetAotPath skips missing preinstall host AOT an file when aot status is failed. + * @tc.type: FUNC + */ +HWTEST_F(EtsRuntimeTest, GetAotPath_014, TestSize.Level1) +{ + auto etsRuntime = std::make_unique(); + Runtime::Options options; + options.bundleName = TEST_HOST_AOT_BUNDLE_NAME; + options.aotCompileStatusMap = { + {"entry", static_cast(AppExecFwk::AOTCompileStatus::COMPILE_FAILED)} + }; + options.commonHspBundleInfos = {}; + + auto result = etsRuntime->GetAotPath(options); + EXPECT_EQ(result, ""); +} + +/** + * @tc.name: GetAotPath_015 + * @tc.desc: Test GetAotPath only loads existing preinstall host AOT an files for failed modules. + * @tc.type: FUNC + */ +HWTEST_F(EtsRuntimeTest, GetAotPath_015, TestSize.Level1) +{ + auto etsRuntime = std::make_unique(); + Runtime::Options options; + options.bundleName = TEST_HOST_AOT_BUNDLE_NAME; + options.aotCompileStatusMap = { + {"entry", static_cast(AppExecFwk::AOTCompileStatus::COMPILE_FAILED)}, + {"feature", static_cast(AppExecFwk::AOTCompileStatus::COMPILE_FAILED)} + }; + options.commonHspBundleInfos = {}; + CreateHostAotAnFile(TEST_HOST_AOT_BUNDLE_NAME, "entry"); + + auto result = etsRuntime->GetAotPath(options); + EXPECT_NE(result.find(GetHostAotAnPath(TEST_HOST_AOT_BUNDLE_NAME, "entry")), std::string::npos); + EXPECT_EQ(result.find(GetHostAotAnPath(TEST_HOST_AOT_BUNDLE_NAME, "feature")), std::string::npos); +} + +/** + * @tc.name: GetAotPath_016 + * @tc.desc: Test successful AOT status still loads original sandbox an path. + * @tc.type: FUNC + */ +HWTEST_F(EtsRuntimeTest, GetAotPath_016, TestSize.Level1) +{ + auto etsRuntime = std::make_unique(); + Runtime::Options options; + options.bundleName = TEST_HOST_AOT_BUNDLE_NAME; + options.aotCompileStatusMap = { + {"entry", static_cast(AppExecFwk::AOTCompileStatus::IDLE_COMPILE_SUCCESS)} + }; + options.commonHspBundleInfos = {}; + CreateHostAotAnFile(TEST_HOST_AOT_BUNDLE_NAME, "entry"); + + auto result = etsRuntime->GetAotPath(options); + EXPECT_EQ(result, "/data/storage/ark-cache/arm64/entry.an"); + EXPECT_EQ(result.find(GetHostAotAnPath(TEST_HOST_AOT_BUNDLE_NAME, "entry")), std::string::npos); +} + /** * @tc.name: ForceFullGC_0100 * @tc.desc: EtsRuntimeTest test for ForceFullGC. diff --git a/test/unittest/runtime_test/js_runtime_test.cpp b/test/unittest/runtime_test/js_runtime_test.cpp index 9086d0cc43..fa75ad5075 100755 --- a/test/unittest/runtime_test/js_runtime_test.cpp +++ b/test/unittest/runtime_test/js_runtime_test.cpp @@ -1431,6 +1431,166 @@ HWTEST_F(JsRuntimeTest, DumpHeapSnapshot_0600, TestSize.Level1) TAG_LOGI(AAFwkTag::TEST, "DumpHeapSnapshot end"); } +/** + * @tc.name: DumpHeapSnapshot_0700 + * @tc.desc: JsRuntime test for DumpHeapSnapshot with JsHeapDumpParam, only isFullGC is true. + * @tc.type: FUNC + */ +HWTEST_F(JsRuntimeTest, DumpHeapSnapshot_0700, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "DumpHeapSnapshot_0700 start"); + auto jsRuntime = std::make_unique(); + uint32_t tid = 100; + OHOS::AbilityRuntime::Runtime::JsHeapDumpParam param; + param.isFullGC = true; + param.isBinary = false; + param.isClearNodeIdCache = false; + param.isProcDump = false; + jsRuntime->DumpHeapSnapshot(tid, param); + EXPECT_TRUE(jsRuntime != nullptr); + TAG_LOGI(AAFwkTag::TEST, "DumpHeapSnapshot_0700 end"); +} + +/** + * @tc.name: DumpHeapSnapshot_0800 + * @tc.desc: JsRuntime test for DumpHeapSnapshot with JsHeapDumpParam, only isBinary is true. + * @tc.type: FUNC + */ +HWTEST_F(JsRuntimeTest, DumpHeapSnapshot_0800, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "DumpHeapSnapshot_0800 start"); + auto jsRuntime = std::make_unique(); + uint32_t tid = 200; + OHOS::AbilityRuntime::Runtime::JsHeapDumpParam param; + param.isFullGC = false; + param.isBinary = true; + param.isClearNodeIdCache = false; + param.isProcDump = false; + jsRuntime->DumpHeapSnapshot(tid, param); + EXPECT_TRUE(jsRuntime != nullptr); + TAG_LOGI(AAFwkTag::TEST, "DumpHeapSnapshot_0800 end"); +} + +/** + * @tc.name: DumpHeapSnapshot_0900 + * @tc.desc: JsRuntime test for DumpHeapSnapshot with JsHeapDumpParam, only isClearNodeIdCache is true. + * @tc.type: FUNC + */ +HWTEST_F(JsRuntimeTest, DumpHeapSnapshot_0900, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "DumpHeapSnapshot_0900 start"); + auto jsRuntime = std::make_unique(); + uint32_t tid = 300; + OHOS::AbilityRuntime::Runtime::JsHeapDumpParam param; + param.isFullGC = false; + param.isBinary = false; + param.isClearNodeIdCache = true; + param.isProcDump = false; + jsRuntime->DumpHeapSnapshot(tid, param); + EXPECT_TRUE(jsRuntime != nullptr); + TAG_LOGI(AAFwkTag::TEST, "DumpHeapSnapshot_0900 end"); +} + +/** + * @tc.name: DumpHeapSnapshot_1000 + * @tc.desc: JsRuntime test for DumpHeapSnapshot with JsHeapDumpParam, only isProcDump is true. + * @tc.type: FUNC + */ +HWTEST_F(JsRuntimeTest, DumpHeapSnapshot_1000, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "DumpHeapSnapshot_1000 start"); + auto jsRuntime = std::make_unique(); + uint32_t tid = 400; + OHOS::AbilityRuntime::Runtime::JsHeapDumpParam param; + param.isFullGC = false; + param.isBinary = false; + param.isClearNodeIdCache = false; + param.isProcDump = true; + jsRuntime->DumpHeapSnapshot(tid, param); + EXPECT_TRUE(jsRuntime != nullptr); + TAG_LOGI(AAFwkTag::TEST, "DumpHeapSnapshot_1000 end"); +} + +/** + * @tc.name: DumpHeapSnapshot_1100 + * @tc.desc: JsRuntime test for DumpHeapSnapshot with JsHeapDumpParam, isFullGC and isBinary both true. + * @tc.type: FUNC + */ +HWTEST_F(JsRuntimeTest, DumpHeapSnapshot_1100, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "DumpHeapSnapshot_1100 start"); + auto jsRuntime = std::make_unique(); + uint32_t tid = 500; + OHOS::AbilityRuntime::Runtime::JsHeapDumpParam param; + param.isFullGC = true; + param.isBinary = true; + param.isClearNodeIdCache = false; + param.isProcDump = false; + jsRuntime->DumpHeapSnapshot(tid, param); + EXPECT_TRUE(jsRuntime != nullptr); + TAG_LOGI(AAFwkTag::TEST, "DumpHeapSnapshot_1100 end"); +} + +/** + * @tc.name: DumpHeapSnapshot_1200 + * @tc.desc: JsRuntime test for DumpHeapSnapshot with JsHeapDumpParam, isFullGC and isProcDump both true. + * @tc.type: FUNC + */ +HWTEST_F(JsRuntimeTest, DumpHeapSnapshot_1200, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "DumpHeapSnapshot_1200 start"); + auto jsRuntime = std::make_unique(); + uint32_t tid = 600; + OHOS::AbilityRuntime::Runtime::JsHeapDumpParam param; + param.isFullGC = true; + param.isBinary = false; + param.isClearNodeIdCache = false; + param.isProcDump = true; + jsRuntime->DumpHeapSnapshot(tid, param); + EXPECT_TRUE(jsRuntime != nullptr); + TAG_LOGI(AAFwkTag::TEST, "DumpHeapSnapshot_1200 end"); +} + +/** + * @tc.name: DumpHeapSnapshot_1300 + * @tc.desc: JsRuntime test for DumpHeapSnapshot with JsHeapDumpParam, tid is 0. + * @tc.type: FUNC + */ +HWTEST_F(JsRuntimeTest, DumpHeapSnapshot_1300, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "DumpHeapSnapshot_1300 start"); + auto jsRuntime = std::make_unique(); + uint32_t tid = 0; + OHOS::AbilityRuntime::Runtime::JsHeapDumpParam param; + param.isFullGC = false; + param.isBinary = false; + param.isClearNodeIdCache = false; + param.isProcDump = false; + jsRuntime->DumpHeapSnapshot(tid, param); + EXPECT_TRUE(jsRuntime != nullptr); + TAG_LOGI(AAFwkTag::TEST, "DumpHeapSnapshot_1300 end"); +} + +/** + * @tc.name: DumpHeapSnapshot_1400 + * @tc.desc: JsRuntime test for DumpHeapSnapshot with JsHeapDumpParam, tid is UINT32_MAX. + * @tc.type: FUNC + */ +HWTEST_F(JsRuntimeTest, DumpHeapSnapshot_1400, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "DumpHeapSnapshot_1400 start"); + auto jsRuntime = std::make_unique(); + uint32_t tid = UINT32_MAX; + OHOS::AbilityRuntime::Runtime::JsHeapDumpParam param; + param.isFullGC = true; + param.isBinary = true; + param.isClearNodeIdCache = true; + param.isProcDump = true; + jsRuntime->DumpHeapSnapshot(tid, param); + EXPECT_TRUE(jsRuntime != nullptr); + TAG_LOGI(AAFwkTag::TEST, "DumpHeapSnapshot_1400 end"); +} + /** * @tc.name: AllowCrossThreadExecution_0200 * @tc.desc: JsRuntime test for AllowCrossThreadExecution. diff --git a/test/unittest/runtime_test/native_runtime_test.cpp b/test/unittest/runtime_test/native_runtime_test.cpp index 8385928b1d..164cdff2c2 100644 --- a/test/unittest/runtime_test/native_runtime_test.cpp +++ b/test/unittest/runtime_test/native_runtime_test.cpp @@ -89,5 +89,85 @@ HWTEST_F(NativeRuntimeTest, LoadModule_0300, TestSize.Level1) bool ret = NativeRuntime::LoadModule("testBundleName/moduleName", "test.so", "testAbilityName", instance); EXPECT_EQ(ret, false); } + +/** + * @tc.name: StartDebugMode_0100 + * @tc.desc: StartDebugMode returns when local debug and developer mode are both false + * @tc.type: FUNC + */ +HWTEST_F(NativeRuntimeTest, StartDebugMode_0100, TestSize.Level1) +{ + Runtime::DebugOption debugOption; + debugOption.isDebugApp = false; + debugOption.isDebugFromLocal = false; + debugOption.isDeveloperMode = false; + debugOption.isDebugApp = false; + NativeRuntime::StartDebugMode(debugOption, "com.test.bundle"); + EXPECT_EQ(debugOption.isDebugApp, false); +} + +/** + * @tc.name: StartDebugMode_0200 + * @tc.desc: StartDebugMode is callable when developer mode is true + * @tc.type: FUNC + */ +HWTEST_F(NativeRuntimeTest, StartDebugMode_0200, TestSize.Level1) +{ + Runtime::DebugOption debugOption; + debugOption.isDebugFromLocal = false; + debugOption.isDeveloperMode = true; + debugOption.isDebugApp = false; + NativeRuntime::StartDebugMode(debugOption, "com.test.bundle"); + EXPECT_EQ(debugOption.isDebugApp, false); +} + +/** + * @tc.name: StartDebugMode_0300 + * @tc.desc: StartDebugMode is callable when local debug is true + * @tc.type: FUNC + */ +HWTEST_F(NativeRuntimeTest, StartDebugMode_0300, TestSize.Level1) +{ + Runtime::DebugOption debugOption; + debugOption.isDebugFromLocal = true; + debugOption.isDeveloperMode = false; + debugOption.isDebugApp = false; + NativeRuntime::StartDebugMode(debugOption, "com.test.bundle"); + EXPECT_EQ(debugOption.isDebugApp, false); +} + +/** + * @tc.name: StartDebugMode_0400 + * @tc.desc: StartDebugMode is callable for debug app with release provision type + * @tc.type: FUNC + */ +HWTEST_F(NativeRuntimeTest, StartDebugMode_0400, TestSize.Level1) +{ + Runtime::DebugOption debugOption; + debugOption.isDebugFromLocal = true; + debugOption.isDeveloperMode = true; + debugOption.isDebugApp = true; + debugOption.appProvisionType = AppExecFwk::Constants::APP_PROVISION_TYPE_RELEASE; + NativeRuntime::StartDebugMode(debugOption, "com.test.bundle"); + EXPECT_EQ(debugOption.isDebugApp, true); +} + +/** + * @tc.name: StartDebugMode_0500 + * @tc.desc: StartDebugMode is callable for debug app with non-release provision type + * @tc.type: FUNC + */ +HWTEST_F(NativeRuntimeTest, StartDebugMode_0500, TestSize.Level1) +{ + Runtime::DebugOption debugOption; + debugOption.isDebugFromLocal = true; + debugOption.isDeveloperMode = true; + debugOption.isDebugApp = true; + debugOption.appProvisionType = "testProvisionType"; + NativeRuntime::StartDebugMode(debugOption, "com.test.bundle"); + NativeRuntime::StartDebugMode(debugOption, "com.test.bundle"); + NativeRuntime::StopDebugMode(); + EXPECT_EQ(debugOption.isDebugApp, true); +} } // namespace AbilityRuntime } // namespace OHOS \ No newline at end of file diff --git a/test/unittest/service_extension_context_test/ability_manager_stub_mock.h b/test/unittest/service_extension_context_test/ability_manager_stub_mock.h index e416d3f124..ed36eee02f 100644 --- a/test/unittest/service_extension_context_test/ability_manager_stub_mock.h +++ b/test/unittest/service_extension_context_test/ability_manager_stub_mock.h @@ -330,8 +330,8 @@ public: } int StartAbilityByCall(const Want& want, const sptr& connect, - const sptr& callerToken, int32_t userId = DEFAULT_INVAL_VALUE, bool isSilent = false, - bool promotePriority = false, bool isVisible = false) override + const sptr& callerToken, int32_t accountId = DEFAULT_INVAL_VALUE, bool isSilent = false, + bool promotePriority = false, bool isVisible = false, uint64_t specifiedFullTokenId = 0) override { return 0; } diff --git a/test/unittest/skill_execute_callback_proxy_test/BUILD.gn b/test/unittest/skill_execute_callback_proxy_test/BUILD.gn new file mode 100644 index 0000000000..2472fe7b30 --- /dev/null +++ b/test/unittest/skill_execute_callback_proxy_test/BUILD.gn @@ -0,0 +1,71 @@ +# 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/skill" + +ohos_unittest("skill_execute_callback_proxy_test") { + module_out_path = module_output_path + + include_dirs = [ "${ability_runtime_test_path}/mock/services_abilitymgr_test/libs/system_ability_mock" ] + + sources = [ + "${ability_runtime_test_path}/mock/services_abilitymgr_test/libs/appexecfwk_core/src/appmgr/mock_app_scheduler.cpp", + "skill_execute_callback_proxy_test.cpp", + ] + + configs = [ + "${ability_runtime_services_path}/abilitymgr:abilityms_config", + "${ability_runtime_test_path}/mock/services_abilitymgr_test:aafwk_mock_config", + ] + + cflags = [] + if (target_cpu == "arm") { + cflags += [ "-DBINDER_IPC_32BIT" ] + } + + deps = [ + "${ability_runtime_innerkits_path}/ability_manager:ability_manager", + "${ability_runtime_innerkits_path}/deps_wrapper:ability_deps_wrapper", + "${ability_runtime_native_path}/ability/native:abilitykit_native", + "${ability_runtime_services_path}/abilitymgr:abilityms", + "${ability_runtime_services_path}/abilitymgr:abilityms_target", + "${ability_runtime_test_path}/mock/services_abilitymgr_test/libs/appexecfwk_core:appexecfwk_appmgr_mock", + "${ability_runtime_test_path}/mock/services_abilitymgr_test/libs/appexecfwk_core:appexecfwk_bundlemgr_mock", + ] + + external_deps = [ + "ability_base:want", + "ability_base:zuri", + "access_token:libaccesstoken_sdk", + "c_utils:utils", + "common_event_service:cesfwk_innerkits", + "ffrt:libffrt", + "googletest:gmock_main", + "googletest:gtest_main", + "hilog:libhilog", + "init:libbeget_proxy", + "ipc:ipc_core", + "napi:ace_napi", + "relational_store:native_appdatafwk", + "relational_store:native_rdb", + "samgr:samgr_proxy", + ] +} + +group("unittest") { + testonly = true + deps = [ ":skill_execute_callback_proxy_test" ] +} diff --git a/test/unittest/skill_execute_callback_proxy_test/mock_skill_execute_callback_stub.h b/test/unittest/skill_execute_callback_proxy_test/mock_skill_execute_callback_stub.h new file mode 100644 index 0000000000..42cb9ceb49 --- /dev/null +++ b/test/unittest/skill_execute_callback_proxy_test/mock_skill_execute_callback_stub.h @@ -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. + */ + +#ifndef UNITTEST_OHOS_ABILITY_RUNTIME_MOCK_SKILL_EXECUTE_CALLBACK_STUB_H +#define UNITTEST_OHOS_ABILITY_RUNTIME_MOCK_SKILL_EXECUTE_CALLBACK_STUB_H + +#include +#include "semaphore_ex.h" +#include "skill/skill_execute_callback_stub.h" + +namespace OHOS { +namespace AAFwk { +class MockSkillExecuteCallbackStub : public SkillExecuteCallbackStub { +public: + MOCK_METHOD3(OnExecuteDone, + void(const std::string &requestCode, int32_t resultCode, + const AppExecFwk::SkillExecuteResult &result)); + + void Wait() + { + sem_.Wait(); + } + + int Post() + { + sem_.Post(); + return 0; + } + + void PostVoid() + { + sem_.Post(); + } + +private: + Semaphore sem_; +}; +} // namespace AAFwk +} // namespace OHOS +#endif // UNITTEST_OHOS_ABILITY_RUNTIME_MOCK_SKILL_EXECUTE_CALLBACK_STUB_H diff --git a/test/unittest/skill_execute_callback_proxy_test/skill_execute_callback_proxy_test.cpp b/test/unittest/skill_execute_callback_proxy_test/skill_execute_callback_proxy_test.cpp new file mode 100644 index 0000000000..9bcf0b904e --- /dev/null +++ b/test/unittest/skill_execute_callback_proxy_test/skill_execute_callback_proxy_test.cpp @@ -0,0 +1,133 @@ +/* + * 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 + +#include "hilog_tag_wrapper.h" +#include "mock_skill_execute_callback_stub.h" +#include "skill/skill_execute_callback_proxy.h" +#include "skill_execute_result.h" + +using namespace testing::ext; +using namespace testing; +namespace OHOS { +namespace AAFwk { + +class SkillExecuteCallbackProxyTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; + +void SkillExecuteCallbackProxyTest::SetUpTestCase(void) +{} +void SkillExecuteCallbackProxyTest::TearDownTestCase(void) +{} +void SkillExecuteCallbackProxyTest::SetUp() +{} +void SkillExecuteCallbackProxyTest::TearDown() +{} + +/** + * @tc.name: OnExecuteDone_0100 + * @tc.desc: Test OnExecuteDone IPC between proxy and stub. + * @tc.type: FUNC + */ +HWTEST_F(SkillExecuteCallbackProxyTest, OnExecuteDone_0100, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "begin."); + sptr mockStub(new MockSkillExecuteCallbackStub()); + sptr proxy(new SkillExecuteCallbackProxy(mockStub)); + + AppExecFwk::SkillExecuteResult result; + result.code = 0; + result.result = std::make_shared(); + + EXPECT_CALL(*mockStub, OnExecuteDone(_, _, _)) + .Times(1) + .WillOnce(InvokeWithoutArgs(mockStub.GetRefPtr(), &MockSkillExecuteCallbackStub::PostVoid)); + + proxy->OnExecuteDone("req001", 0, result); + mockStub->Wait(); + TAG_LOGI(AAFwkTag::TEST, "end."); +} + +/** + * @tc.name: OnExecuteDone_0200 + * @tc.desc: Test OnExecuteDone with error result code. + * @tc.type: FUNC + */ +HWTEST_F(SkillExecuteCallbackProxyTest, OnExecuteDone_0200, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "begin."); + sptr mockStub(new MockSkillExecuteCallbackStub()); + sptr proxy(new SkillExecuteCallbackProxy(mockStub)); + + AppExecFwk::SkillExecuteResult result; + result.code = -1; + result.result = std::make_shared(); + + EXPECT_CALL(*mockStub, OnExecuteDone(_, _, _)) + .Times(1) + .WillOnce(InvokeWithoutArgs(mockStub.GetRefPtr(), &MockSkillExecuteCallbackStub::PostVoid)); + + proxy->OnExecuteDone("req002", -1, result); + mockStub->Wait(); + TAG_LOGI(AAFwkTag::TEST, "end."); +} + +/** + * @tc.name: OnExecuteDone_0300 + * @tc.desc: Test OnExecuteDone with uris in result. + * @tc.type: FUNC + */ +HWTEST_F(SkillExecuteCallbackProxyTest, OnExecuteDone_0300, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "begin."); + sptr mockStub(new MockSkillExecuteCallbackStub()); + sptr proxy(new SkillExecuteCallbackProxy(mockStub)); + + AppExecFwk::SkillExecuteResult result; + result.code = 0; + result.result = std::make_shared(); + result.uris = { "file://docs/storage/test.txt" }; + result.flags = 1; + + EXPECT_CALL(*mockStub, OnExecuteDone(_, _, _)) + .Times(1) + .WillOnce(InvokeWithoutArgs(mockStub.GetRefPtr(), &MockSkillExecuteCallbackStub::PostVoid)); + + proxy->OnExecuteDone("req003", 0, result); + mockStub->Wait(); + TAG_LOGI(AAFwkTag::TEST, "end."); +} + +/** + * @tc.name: ProxyInstance_0100 + * @tc.desc: Test proxy instance creation is successful. + * @tc.type: FUNC + */ +HWTEST_F(SkillExecuteCallbackProxyTest, ProxyInstance_0100, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "begin."); + sptr mockStub(new MockSkillExecuteCallbackStub()); + sptr proxy(new SkillExecuteCallbackProxy(mockStub)); + EXPECT_NE(proxy, nullptr); + TAG_LOGI(AAFwkTag::TEST, "end."); +} +} // namespace AAFwk +} // namespace OHOS diff --git a/test/unittest/skill_execute_callback_stub_test/BUILD.gn b/test/unittest/skill_execute_callback_stub_test/BUILD.gn new file mode 100644 index 0000000000..803b2a083d --- /dev/null +++ b/test/unittest/skill_execute_callback_stub_test/BUILD.gn @@ -0,0 +1,71 @@ +# 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/skill" + +ohos_unittest("skill_execute_callback_stub_test") { + module_out_path = module_output_path + + include_dirs = [ "${ability_runtime_test_path}/mock/services_abilitymgr_test/libs/system_ability_mock" ] + + sources = [ + "${ability_runtime_test_path}/mock/services_abilitymgr_test/libs/appexecfwk_core/src/appmgr/mock_app_scheduler.cpp", + "skill_execute_callback_stub_test.cpp", + ] + + configs = [ + "${ability_runtime_services_path}/abilitymgr:abilityms_config", + "${ability_runtime_test_path}/mock/services_abilitymgr_test:aafwk_mock_config", + ] + + cflags = [] + if (target_cpu == "arm") { + cflags += [ "-DBINDER_IPC_32BIT" ] + } + + deps = [ + "${ability_runtime_innerkits_path}/ability_manager:ability_manager", + "${ability_runtime_innerkits_path}/deps_wrapper:ability_deps_wrapper", + "${ability_runtime_native_path}/ability/native:abilitykit_native", + "${ability_runtime_services_path}/abilitymgr:abilityms", + "${ability_runtime_services_path}/abilitymgr:abilityms_target", + "${ability_runtime_test_path}/mock/services_abilitymgr_test/libs/appexecfwk_core:appexecfwk_appmgr_mock", + "${ability_runtime_test_path}/mock/services_abilitymgr_test/libs/appexecfwk_core:appexecfwk_bundlemgr_mock", + ] + + external_deps = [ + "ability_base:want", + "ability_base:zuri", + "access_token:libaccesstoken_sdk", + "c_utils:utils", + "common_event_service:cesfwk_innerkits", + "ffrt:libffrt", + "googletest:gmock_main", + "googletest:gtest_main", + "hilog:libhilog", + "init:libbeget_proxy", + "ipc:ipc_core", + "napi:ace_napi", + "relational_store:native_appdatafwk", + "relational_store:native_rdb", + "samgr:samgr_proxy", + ] +} + +group("unittest") { + testonly = true + deps = [ ":skill_execute_callback_stub_test" ] +} diff --git a/test/unittest/skill_execute_callback_stub_test/mock_skill_execute_callback_stub_for_stub_test.h b/test/unittest/skill_execute_callback_stub_test/mock_skill_execute_callback_stub_for_stub_test.h new file mode 100644 index 0000000000..9c1aa6dc71 --- /dev/null +++ b/test/unittest/skill_execute_callback_stub_test/mock_skill_execute_callback_stub_for_stub_test.h @@ -0,0 +1,32 @@ +/* + * 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 UNITTEST_OHOS_ABILITY_RUNTIME_MOCK_SKILL_EXECUTE_CALLBACK_STUB_FOR_STUB_TEST_H +#define UNITTEST_OHOS_ABILITY_RUNTIME_MOCK_SKILL_EXECUTE_CALLBACK_STUB_FOR_STUB_TEST_H + +#include +#include "skill/skill_execute_callback_stub.h" + +namespace OHOS { +namespace AAFwk { +class MockSkillExecuteCallback : public SkillExecuteCallbackStub { +public: + MOCK_METHOD3(OnExecuteDone, + void(const std::string &requestCode, int32_t resultCode, + const AppExecFwk::SkillExecuteResult &result)); +}; +} // namespace AAFwk +} // namespace OHOS +#endif // UNITTEST_OHOS_ABILITY_RUNTIME_MOCK_SKILL_EXECUTE_CALLBACK_STUB_FOR_STUB_TEST_H diff --git a/test/unittest/skill_execute_callback_stub_test/skill_execute_callback_stub_test.cpp b/test/unittest/skill_execute_callback_stub_test/skill_execute_callback_stub_test.cpp new file mode 100644 index 0000000000..5fc9c8a779 --- /dev/null +++ b/test/unittest/skill_execute_callback_stub_test/skill_execute_callback_stub_test.cpp @@ -0,0 +1,222 @@ +/* + * 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 + +#include "hilog_tag_wrapper.h" +#include "mock_skill_execute_callback_stub_for_stub_test.h" +#include "skill/skill_execute_callback_proxy.h" +#include "skill_execute_result.h" +#include "want_params.h" + +using namespace testing::ext; +using namespace testing; +namespace OHOS { +namespace AAFwk { + +class SkillExecuteCallbackStubTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); + + void WriteInterfaceToken(MessageParcel &data); +}; + +void SkillExecuteCallbackStubTest::SetUpTestCase(void) +{} +void SkillExecuteCallbackStubTest::TearDownTestCase(void) +{} +void SkillExecuteCallbackStubTest::SetUp() +{} +void SkillExecuteCallbackStubTest::TearDown() +{} + +void SkillExecuteCallbackStubTest::WriteInterfaceToken(MessageParcel &data) +{ + data.WriteInterfaceToken(ISkillExecuteCallback::GetDescriptor()); +} + +/** + * @tc.name: OnRemoteRequest_0100 + * @tc.desc: Test OnRemoteRequest with invalid interface token. + * @tc.type: FUNC + */ +HWTEST_F(SkillExecuteCallbackStubTest, OnRemoteRequest_0100, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "begin."); + sptr mockStub(new MockSkillExecuteCallback()); + MessageParcel data; + MessageParcel reply; + MessageOption option; + + // Write wrong interface token + data.WriteInterfaceToken(u"wrong.descriptor"); + + int res = mockStub->OnRemoteRequest(ISkillExecuteCallback::ON_SKILL_EXECUTE_DONE, + data, reply, option); + EXPECT_EQ(res, ERR_INVALID_STATE); + TAG_LOGI(AAFwkTag::TEST, "end."); +} + +/** + * @tc.name: OnRemoteRequest_0200 + * @tc.desc: Test OnRemoteRequest with unknown code. + * @tc.type: FUNC + */ +HWTEST_F(SkillExecuteCallbackStubTest, OnRemoteRequest_0200, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "begin."); + sptr mockStub(new MockSkillExecuteCallback()); + MessageParcel data; + MessageParcel reply; + MessageOption option; + + WriteInterfaceToken(data); + + int res = mockStub->OnRemoteRequest(999, data, reply, option); + EXPECT_NE(res, ERR_OK); + TAG_LOGI(AAFwkTag::TEST, "end."); +} + +/** + * @tc.name: OnRemoteRequest_0300 + * @tc.desc: Test OnRemoteRequest with valid OnExecuteDone request but null result. + * @tc.type: FUNC + */ +HWTEST_F(SkillExecuteCallbackStubTest, OnRemoteRequest_0300, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "begin."); + sptr mockStub(new MockSkillExecuteCallback()); + MessageParcel data; + MessageParcel reply; + MessageOption option; + + WriteInterfaceToken(data); + data.WriteString("req001"); + data.WriteInt32(0); + // Write null result parcelable + data.WriteParcelable(nullptr); + + int res = mockStub->OnRemoteRequest(ISkillExecuteCallback::ON_SKILL_EXECUTE_DONE, + data, reply, option); + EXPECT_EQ(res, ERR_INVALID_VALUE); + TAG_LOGI(AAFwkTag::TEST, "end."); +} + +/** + * @tc.name: OnRemoteRequest_0400 + * @tc.desc: Test OnRemoteRequest with valid OnExecuteDone request and valid result. + * @tc.type: FUNC + */ +HWTEST_F(SkillExecuteCallbackStubTest, OnRemoteRequest_0400, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "begin."); + sptr mockStub(new MockSkillExecuteCallback()); + MessageParcel data; + MessageParcel reply; + MessageOption option; + + WriteInterfaceToken(data); + data.WriteString("req001"); + data.WriteInt32(0); + + AppExecFwk::SkillExecuteResult result; + result.code = 0; + result.result = std::make_shared(); + data.WriteParcelable(&result); + + EXPECT_CALL(*mockStub, OnExecuteDone(_, _, _)).Times(1); + int res = mockStub->OnRemoteRequest(ISkillExecuteCallback::ON_SKILL_EXECUTE_DONE, + data, reply, option); + EXPECT_EQ(res, ERR_OK); + TAG_LOGI(AAFwkTag::TEST, "end."); +} + +/** + * @tc.name: OnRemoteRequest_0500 + * @tc.desc: Test OnRemoteRequest with valid data including uris in result. + * @tc.type: FUNC + */ +HWTEST_F(SkillExecuteCallbackStubTest, OnRemoteRequest_0500, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "begin."); + sptr mockStub(new MockSkillExecuteCallback()); + MessageParcel data; + MessageParcel reply; + MessageOption option; + + WriteInterfaceToken(data); + data.WriteString("req002"); + data.WriteInt32(-1); + + AppExecFwk::SkillExecuteResult result; + result.code = -1; + result.result = std::make_shared(); + result.uris = { "file://test.txt" }; + result.flags = 1; + data.WriteParcelable(&result); + + EXPECT_CALL(*mockStub, OnExecuteDone(_, _, _)).Times(1); + int res = mockStub->OnRemoteRequest(ISkillExecuteCallback::ON_SKILL_EXECUTE_DONE, + data, reply, option); + EXPECT_EQ(res, ERR_OK); + TAG_LOGI(AAFwkTag::TEST, "end."); +} + +/** + * @tc.name: OnRemoteRequest_0600 + * @tc.desc: Test OnRemoteRequest with error result code. + * @tc.type: FUNC + */ +HWTEST_F(SkillExecuteCallbackStubTest, OnRemoteRequest_0600, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "begin."); + sptr mockStub(new MockSkillExecuteCallback()); + MessageParcel data; + MessageParcel reply; + MessageOption option; + + WriteInterfaceToken(data); + data.WriteString("req003"); + data.WriteInt32(12345); + + AppExecFwk::SkillExecuteResult result; + result.code = 12345; + result.result = std::make_shared(); + data.WriteParcelable(&result); + + EXPECT_CALL(*mockStub, OnExecuteDone(_, _, _)).Times(1); + int res = mockStub->OnRemoteRequest(ISkillExecuteCallback::ON_SKILL_EXECUTE_DONE, + data, reply, option); + EXPECT_EQ(res, ERR_OK); + TAG_LOGI(AAFwkTag::TEST, "end."); +} + +/** + * @tc.name: StubInstance_0100 + * @tc.desc: Test stub instance creation. + * @tc.type: FUNC + */ +HWTEST_F(SkillExecuteCallbackStubTest, StubInstance_0100, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "begin."); + sptr mockStub(new MockSkillExecuteCallback()); + EXPECT_NE(mockStub, nullptr); + TAG_LOGI(AAFwkTag::TEST, "end."); +} +} // namespace AAFwk +} // namespace OHOS diff --git a/test/unittest/skill_execute_param_test/BUILD.gn b/test/unittest/skill_execute_param_test/BUILD.gn new file mode 100644 index 0000000000..b4ed8553c3 --- /dev/null +++ b/test/unittest/skill_execute_param_test/BUILD.gn @@ -0,0 +1,53 @@ +# 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("//build/test.gni") +import("//foundation/ability/ability_runtime/ability_runtime.gni") + +ohos_unittest("skill_execute_param_test") { + module_out_path = "ability_runtime/ability_runtime/skill" + + include_dirs = [] + + sources = [ "skill_execute_param_test.cpp" ] + + configs = [ "${ability_runtime_services_path}/abilitymgr:abilityms_config" ] + + cflags = [] + if (target_cpu == "arm") { + cflags += [ "-DBINDER_IPC_32BIT" ] + } + + deps = [ + "${ability_runtime_innerkits_path}/ability_manager:ability_manager", + "${ability_runtime_innerkits_path}/deps_wrapper:ability_deps_wrapper", + ] + + external_deps = [ + "ability_base:base", + "ability_base:want", + "bundle_framework:libappexecfwk_common", + "c_utils:utils", + "ffrt:libffrt", + "googletest:gmock_main", + "googletest:gtest_main", + "hilog:libhilog", + "ipc:ipc_core", + ] +} + +group("unittest") { + testonly = true + deps = [ ":skill_execute_param_test" ] +} diff --git a/test/unittest/skill_execute_param_test/skill_execute_param_test.cpp b/test/unittest/skill_execute_param_test/skill_execute_param_test.cpp new file mode 100644 index 0000000000..178d337a6d --- /dev/null +++ b/test/unittest/skill_execute_param_test/skill_execute_param_test.cpp @@ -0,0 +1,479 @@ +/* + * 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 + +#include "hilog_tag_wrapper.h" +#include "message_parcel.h" +#include "skill_execute_param.h" +#include "string_wrapper.h" + +using namespace testing; +using namespace testing::ext; + +namespace OHOS { +namespace AppExecFwk { +namespace { +const std::string TEST_BUNDLE_NAME = "com.test.bundle"; +const std::string TEST_MODULE_NAME = "entry"; +const std::string TEST_SKILL_NAME = "PlayMusic"; +const std::string TEST_SCRIPT_PATH = "./ets/entry/PlayMusic.ts"; +const std::string TEST_FUNCTION_NAME = "executePlay"; +const std::string TEST_REQUEST_CODE = "req_001"; +const std::string TEST_HAP_PATH = "/data/app/com.test.bundle/entry.hap"; +const std::string TEST_SRC_ENTRY = "./ets/entry/PlayMusic.ts"; +} // namespace + +void BuildFullSkillExecuteParam(SkillExecuteParam ¶m) +{ + param.bundleName_ = TEST_BUNDLE_NAME; + param.moduleName_ = TEST_MODULE_NAME; + param.skillName_ = TEST_SKILL_NAME; + param.scriptPath_ = TEST_SCRIPT_PATH; + param.functionName_ = TEST_FUNCTION_NAME; + param.skillArgs_ = std::make_shared(); + param.srcEntries_ = { TEST_SRC_ENTRY, "./ets/entry/StopMusic.ts" }; + param.requestCode_ = TEST_REQUEST_CODE; + param.hapPath_ = TEST_HAP_PATH; +} + +class SkillExecuteParamTest : public testing::Test { +public: + static void SetUpTestCase(); + static void TearDownTestCase(); + void SetUp() override; + void TearDown() override; +}; + +void SkillExecuteParamTest::SetUpTestCase(void) +{} + +void SkillExecuteParamTest::TearDownTestCase(void) +{} + +void SkillExecuteParamTest::SetUp() +{} + +void SkillExecuteParamTest::TearDown() +{} + +/** + * @tc.name: Marshalling_0100 + * @tc.desc: Test Marshalling with default (empty) SkillExecuteParam. + * @tc.type: FUNC + */ +HWTEST_F(SkillExecuteParamTest, Marshalling_0100, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "begin."); + MessageParcel parcel; + SkillExecuteParam param; + EXPECT_TRUE(param.Marshalling(parcel)); + TAG_LOGI(AAFwkTag::TEST, "end."); +} + +/** + * @tc.name: Marshalling_0200 + * @tc.desc: Test Marshalling with fully populated SkillExecuteParam. + * @tc.type: FUNC + */ +HWTEST_F(SkillExecuteParamTest, Marshalling_0200, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "begin."); + MessageParcel parcel; + SkillExecuteParam param; + BuildFullSkillExecuteParam(param); + EXPECT_TRUE(param.Marshalling(parcel)); + TAG_LOGI(AAFwkTag::TEST, "end."); +} + +/** + * @tc.name: MarshallingAndUnmarshalling_0100 + * @tc.desc: Test round-trip Marshalling and Unmarshalling with full data. + * @tc.type: FUNC + */ +HWTEST_F(SkillExecuteParamTest, MarshallingAndUnmarshalling_0100, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "begin."); + MessageParcel parcel; + SkillExecuteParam param; + BuildFullSkillExecuteParam(param); + + EXPECT_TRUE(param.Marshalling(parcel)); + + auto result = SkillExecuteParam::Unmarshalling(parcel); + ASSERT_NE(result, nullptr); + EXPECT_EQ(result->bundleName_, TEST_BUNDLE_NAME); + EXPECT_EQ(result->moduleName_, TEST_MODULE_NAME); + EXPECT_EQ(result->skillName_, TEST_SKILL_NAME); + EXPECT_EQ(result->scriptPath_, TEST_SCRIPT_PATH); + EXPECT_EQ(result->functionName_, TEST_FUNCTION_NAME); + ASSERT_NE(result->skillArgs_, nullptr); + ASSERT_EQ(result->srcEntries_.size(), 2U); + EXPECT_EQ(result->srcEntries_[0], TEST_SRC_ENTRY); + EXPECT_EQ(result->requestCode_, TEST_REQUEST_CODE); + EXPECT_EQ(result->hapPath_, TEST_HAP_PATH); + delete result; + TAG_LOGI(AAFwkTag::TEST, "end."); +} + +/** + * @tc.name: MarshallingAndUnmarshalling_0200 + * @tc.desc: Test round-trip with null skillArgs. + * @tc.type: FUNC + */ +HWTEST_F(SkillExecuteParamTest, MarshallingAndUnmarshalling_0200, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "begin."); + MessageParcel parcel; + SkillExecuteParam param; + param.bundleName_ = TEST_BUNDLE_NAME; + param.moduleName_ = TEST_MODULE_NAME; + param.skillName_ = TEST_SKILL_NAME; + param.scriptPath_ = ""; + param.functionName_ = ""; + param.skillArgs_ = nullptr; + param.srcEntries_ = {}; + param.requestCode_ = ""; + param.hapPath_ = ""; + + EXPECT_TRUE(param.Marshalling(parcel)); + + auto result = SkillExecuteParam::Unmarshalling(parcel); + ASSERT_NE(result, nullptr); + EXPECT_EQ(result->bundleName_, TEST_BUNDLE_NAME); + ASSERT_NE(result->skillArgs_, nullptr); + EXPECT_EQ(result->srcEntries_.size(), 0U); + delete result; + TAG_LOGI(AAFwkTag::TEST, "end."); +} + +/** + * @tc.name: ReadFromParcel_0100 + * @tc.desc: Test ReadFromParcel with manually written data. + * @tc.type: FUNC + */ +HWTEST_F(SkillExecuteParamTest, ReadFromParcel_0100, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "begin."); + MessageParcel parcel; + parcel.WriteString16(Str8ToStr16(TEST_BUNDLE_NAME)); + parcel.WriteString16(Str8ToStr16(TEST_MODULE_NAME)); + parcel.WriteString16(Str8ToStr16(TEST_SKILL_NAME)); + parcel.WriteString16(Str8ToStr16("")); + parcel.WriteString16(Str8ToStr16("")); + AAFwk::WantParams emptyParams; + parcel.WriteParcelable(&emptyParams); + parcel.WriteInt32(0); // srcCount + parcel.WriteString16(Str8ToStr16(TEST_REQUEST_CODE)); + parcel.WriteString16(Str8ToStr16(TEST_HAP_PATH)); + + SkillExecuteParam param; + EXPECT_TRUE(param.ReadFromParcel(parcel)); + EXPECT_EQ(param.bundleName_, TEST_BUNDLE_NAME); + EXPECT_EQ(param.moduleName_, TEST_MODULE_NAME); + EXPECT_EQ(param.skillName_, TEST_SKILL_NAME); + EXPECT_EQ(param.requestCode_, TEST_REQUEST_CODE); + EXPECT_EQ(param.hapPath_, TEST_HAP_PATH); + TAG_LOGI(AAFwkTag::TEST, "end."); +} + +/** + * @tc.name: IsSkillExecute_0100 + * @tc.desc: Test IsSkillExecute returns true when want has skill name parameter. + * @tc.type: FUNC + */ +HWTEST_F(SkillExecuteParamTest, IsSkillExecute_0100, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "begin."); + AAFwk::Want want; + want.SetParam(SKILL_EXECUTE_PARAM_SKILL_NAME, TEST_SKILL_NAME); + EXPECT_TRUE(SkillExecuteParam::IsSkillExecute(want)); + TAG_LOGI(AAFwkTag::TEST, "end."); +} + +/** + * @tc.name: IsSkillExecute_0200 + * @tc.desc: Test IsSkillExecute returns false when want has no skill name parameter. + * @tc.type: FUNC + */ +HWTEST_F(SkillExecuteParamTest, IsSkillExecute_0200, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "begin."); + AAFwk::Want want; + EXPECT_FALSE(SkillExecuteParam::IsSkillExecute(want)); + TAG_LOGI(AAFwkTag::TEST, "end."); +} + +/** + * @tc.name: IsSkillExecute_0300 + * @tc.desc: Test IsSkillExecute returns false when want has other parameters but not skill name. + * @tc.type: FUNC + */ +HWTEST_F(SkillExecuteParamTest, IsSkillExecute_0300, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "begin."); + AAFwk::Want want; + want.SetParam(SKILL_EXECUTE_PARAM_BUNDLE_NAME, TEST_BUNDLE_NAME); + EXPECT_FALSE(SkillExecuteParam::IsSkillExecute(want)); + TAG_LOGI(AAFwkTag::TEST, "end."); +} + +/** + * @tc.name: WriteToWant_0100 + * @tc.desc: Test WriteToWant writes all parameters to want. + * @tc.type: FUNC + */ +HWTEST_F(SkillExecuteParamTest, WriteToWant_0100, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "begin."); + AAFwk::Want want; + auto skillArgs = std::make_shared(); + std::vector srcEntries = { TEST_SRC_ENTRY }; + SkillExecuteParam::WriteToWant(want, TEST_BUNDLE_NAME, TEST_MODULE_NAME, + TEST_SKILL_NAME, TEST_SCRIPT_PATH, TEST_FUNCTION_NAME, skillArgs, + srcEntries, TEST_REQUEST_CODE, TEST_HAP_PATH); + + auto params = want.GetParams(); + EXPECT_EQ(params.GetStringParam(SKILL_EXECUTE_PARAM_BUNDLE_NAME), TEST_BUNDLE_NAME); + EXPECT_EQ(params.GetStringParam(SKILL_EXECUTE_PARAM_MODULE_NAME), TEST_MODULE_NAME); + EXPECT_EQ(params.GetStringParam(SKILL_EXECUTE_PARAM_SKILL_NAME), TEST_SKILL_NAME); + EXPECT_EQ(params.GetStringParam(SKILL_EXECUTE_PARAM_SCRIPT_PATH), TEST_SCRIPT_PATH); + EXPECT_EQ(params.GetStringParam(SKILL_EXECUTE_PARAM_FUNCTION_NAME), TEST_FUNCTION_NAME); + EXPECT_EQ(params.GetStringParam(SKILL_EXECUTE_PARAM_REQUEST_CODE), TEST_REQUEST_CODE); + EXPECT_EQ(params.GetStringParam(SKILL_EXECUTE_PARAM_HAP_PATH), TEST_HAP_PATH); + TAG_LOGI(AAFwkTag::TEST, "end."); +} + +/** + * @tc.name: WriteToWant_0200 + * @tc.desc: Test WriteToWant with empty optional parameters. + * @tc.type: FUNC + */ +HWTEST_F(SkillExecuteParamTest, WriteToWant_0200, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "begin."); + AAFwk::Want want; + SkillExecuteParam::WriteToWant(want, TEST_BUNDLE_NAME, TEST_MODULE_NAME, + TEST_SKILL_NAME); + + auto params = want.GetParams(); + EXPECT_EQ(params.GetStringParam(SKILL_EXECUTE_PARAM_BUNDLE_NAME), TEST_BUNDLE_NAME); + EXPECT_EQ(params.GetStringParam(SKILL_EXECUTE_PARAM_SKILL_NAME), TEST_SKILL_NAME); + // Empty optional params should not be written + EXPECT_FALSE(params.HasParam(SKILL_EXECUTE_PARAM_SCRIPT_PATH)); + EXPECT_FALSE(params.HasParam(SKILL_EXECUTE_PARAM_FUNCTION_NAME)); + EXPECT_FALSE(params.HasParam(SKILL_EXECUTE_PARAM_HAP_PATH)); + EXPECT_FALSE(params.HasParam(SKILL_EXECUTE_PARAM_REQUEST_CODE)); + TAG_LOGI(AAFwkTag::TEST, "end."); +} + +/** + * @tc.name: WriteToWant_0300 + * @tc.desc: Test WriteToWant with skill args. + * @tc.type: FUNC + */ +HWTEST_F(SkillExecuteParamTest, WriteToWant_0300, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "begin."); + AAFwk::Want want; + auto skillArgs = std::make_shared(); + skillArgs->SetParam("key1", AAFwk::String::Box("value1")); + SkillExecuteParam::WriteToWant(want, TEST_BUNDLE_NAME, TEST_MODULE_NAME, + TEST_SKILL_NAME, "", "", skillArgs); + + auto params = want.GetParams(); + EXPECT_TRUE(params.HasParam(SKILL_EXECUTE_PARAM_ARGS_KEYS)); + EXPECT_EQ(params.GetStringParam(SKILL_EXECUTE_PARAM_ARGS_KEYS), "key1"); + TAG_LOGI(AAFwkTag::TEST, "end."); +} + +/** + * @tc.name: WriteToWant_0400 + * @tc.desc: Test WriteToWant with srcEntries. + * @tc.type: FUNC + */ +HWTEST_F(SkillExecuteParamTest, WriteToWant_0400, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "begin."); + AAFwk::Want want; + std::vector srcEntries = { "src1.ts", "src2.ts" }; + SkillExecuteParam::WriteToWant(want, TEST_BUNDLE_NAME, TEST_MODULE_NAME, + TEST_SKILL_NAME, "", "", nullptr, srcEntries); + + auto params = want.GetParams(); + EXPECT_EQ(params.GetStringParam(SKILL_EXECUTE_PARAM_SRC_ENTRIES_COUNT), "2"); + EXPECT_TRUE(params.HasParam(std::string(SKILL_EXECUTE_PARAM_SRC_ENTRY_PREFIX) + "0")); + EXPECT_TRUE(params.HasParam(std::string(SKILL_EXECUTE_PARAM_SRC_ENTRY_PREFIX) + "1")); + TAG_LOGI(AAFwkTag::TEST, "end."); +} + +/** + * @tc.name: GenerateFromWant_0100 + * @tc.desc: Test GenerateFromWant with want containing skill name. + * @tc.type: FUNC + */ +HWTEST_F(SkillExecuteParamTest, GenerateFromWant_0100, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "begin."); + AAFwk::Want want; + SkillExecuteParam::WriteToWant(want, TEST_BUNDLE_NAME, TEST_MODULE_NAME, + TEST_SKILL_NAME, TEST_SCRIPT_PATH, TEST_FUNCTION_NAME, nullptr, + { TEST_SRC_ENTRY }, TEST_REQUEST_CODE, TEST_HAP_PATH); + + SkillExecuteParam param; + EXPECT_TRUE(SkillExecuteParam::GenerateFromWant(want, param)); + EXPECT_EQ(param.bundleName_, TEST_BUNDLE_NAME); + EXPECT_EQ(param.moduleName_, TEST_MODULE_NAME); + EXPECT_EQ(param.skillName_, TEST_SKILL_NAME); + EXPECT_EQ(param.scriptPath_, TEST_SCRIPT_PATH); + EXPECT_EQ(param.functionName_, TEST_FUNCTION_NAME); + ASSERT_EQ(param.srcEntries_.size(), 1U); + EXPECT_EQ(param.srcEntries_[0], TEST_SRC_ENTRY); + EXPECT_EQ(param.requestCode_, TEST_REQUEST_CODE); + EXPECT_EQ(param.hapPath_, TEST_HAP_PATH); + TAG_LOGI(AAFwkTag::TEST, "end."); +} + +/** + * @tc.name: GenerateFromWant_0200 + * @tc.desc: Test GenerateFromWant returns false when want has no skill name. + * @tc.type: FUNC + */ +HWTEST_F(SkillExecuteParamTest, GenerateFromWant_0200, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "begin."); + AAFwk::Want want; + SkillExecuteParam param; + EXPECT_FALSE(SkillExecuteParam::GenerateFromWant(want, param)); + TAG_LOGI(AAFwkTag::TEST, "end."); +} + +/** + * @tc.name: GenerateFromWant_0300 + * @tc.desc: Test GenerateFromWant with skill args. + * @tc.type: FUNC + */ +HWTEST_F(SkillExecuteParamTest, GenerateFromWant_0300, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "begin."); + AAFwk::Want want; + auto skillArgs = std::make_shared(); + skillArgs->SetParam("argKey", AAFwk::String::Box("argValue")); + SkillExecuteParam::WriteToWant(want, TEST_BUNDLE_NAME, TEST_MODULE_NAME, + TEST_SKILL_NAME, "", "", skillArgs); + + SkillExecuteParam param; + EXPECT_TRUE(SkillExecuteParam::GenerateFromWant(want, param)); + ASSERT_NE(param.skillArgs_, nullptr); + EXPECT_TRUE(param.skillArgs_->HasParam("argKey")); + TAG_LOGI(AAFwkTag::TEST, "end."); +} + +/** + * @tc.name: RemoveSkillParam_0100 + * @tc.desc: Test RemoveSkillParam removes all skill parameters from want. + * @tc.type: FUNC + */ +HWTEST_F(SkillExecuteParamTest, RemoveSkillParam_0100, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "begin."); + AAFwk::Want want; + SkillExecuteParam::WriteToWant(want, TEST_BUNDLE_NAME, TEST_MODULE_NAME, + TEST_SKILL_NAME, TEST_SCRIPT_PATH, TEST_FUNCTION_NAME, nullptr, + { TEST_SRC_ENTRY }, TEST_REQUEST_CODE, TEST_HAP_PATH); + + EXPECT_TRUE(SkillExecuteParam::RemoveSkillParam(want)); + + auto params = want.GetParams(); + EXPECT_FALSE(params.HasParam(SKILL_EXECUTE_PARAM_BUNDLE_NAME)); + EXPECT_FALSE(params.HasParam(SKILL_EXECUTE_PARAM_MODULE_NAME)); + EXPECT_FALSE(params.HasParam(SKILL_EXECUTE_PARAM_SKILL_NAME)); + EXPECT_FALSE(params.HasParam(SKILL_EXECUTE_PARAM_SCRIPT_PATH)); + EXPECT_FALSE(params.HasParam(SKILL_EXECUTE_PARAM_FUNCTION_NAME)); + EXPECT_FALSE(params.HasParam(SKILL_EXECUTE_PARAM_SRC_ENTRIES_COUNT)); + EXPECT_FALSE(params.HasParam(SKILL_EXECUTE_PARAM_REQUEST_CODE)); + EXPECT_FALSE(params.HasParam(SKILL_EXECUTE_PARAM_HAP_PATH)); + EXPECT_FALSE(params.HasParam(std::string(SKILL_EXECUTE_PARAM_SRC_ENTRY_PREFIX) + "0")); + TAG_LOGI(AAFwkTag::TEST, "end."); +} + +/** + * @tc.name: RemoveSkillParam_0200 + * @tc.desc: Test RemoveSkillParam with empty want (no skill params). + * @tc.type: FUNC + */ +HWTEST_F(SkillExecuteParamTest, RemoveSkillParam_0200, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "begin."); + AAFwk::Want want; + EXPECT_TRUE(SkillExecuteParam::RemoveSkillParam(want)); + TAG_LOGI(AAFwkTag::TEST, "end."); +} + +/** + * @tc.name: RemoveSkillParam_0300 + * @tc.desc: Test RemoveSkillParam removes skill args as well. + * @tc.type: FUNC + */ +HWTEST_F(SkillExecuteParamTest, RemoveSkillParam_0300, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "begin."); + AAFwk::Want want; + auto skillArgs = std::make_shared(); + skillArgs->SetParam("argKey", AAFwk::String::Box("argValue")); + SkillExecuteParam::WriteToWant(want, TEST_BUNDLE_NAME, TEST_MODULE_NAME, + TEST_SKILL_NAME, "", "", skillArgs); + + EXPECT_TRUE(SkillExecuteParam::RemoveSkillParam(want)); + + auto params = want.GetParams(); + EXPECT_FALSE(params.HasParam(SKILL_EXECUTE_PARAM_ARGS_KEYS)); + EXPECT_FALSE(params.HasParam(std::string(SKILL_EXECUTE_PARAM_ARGS_PREFIX) + "argKey")); + TAG_LOGI(AAFwkTag::TEST, "end."); +} + +/** + * @tc.name: WriteToWantAndGenerateFromWant_0100 + * @tc.desc: Test full round-trip: WriteToWant -> GenerateFromWant. + * @tc.type: FUNC + */ +HWTEST_F(SkillExecuteParamTest, WriteToWantAndGenerateFromWant_0100, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "begin."); + AAFwk::Want want; + auto skillArgs = std::make_shared(); + skillArgs->SetParam("key1", AAFwk::String::Box("value1")); + std::vector srcEntries = { "src1.ts" }; + SkillExecuteParam::WriteToWant(want, TEST_BUNDLE_NAME, TEST_MODULE_NAME, + TEST_SKILL_NAME, TEST_SCRIPT_PATH, TEST_FUNCTION_NAME, skillArgs, + srcEntries, TEST_REQUEST_CODE, TEST_HAP_PATH); + + SkillExecuteParam param; + EXPECT_TRUE(SkillExecuteParam::GenerateFromWant(want, param)); + EXPECT_EQ(param.bundleName_, TEST_BUNDLE_NAME); + EXPECT_EQ(param.moduleName_, TEST_MODULE_NAME); + EXPECT_EQ(param.skillName_, TEST_SKILL_NAME); + EXPECT_EQ(param.scriptPath_, TEST_SCRIPT_PATH); + EXPECT_EQ(param.functionName_, TEST_FUNCTION_NAME); + ASSERT_NE(param.skillArgs_, nullptr); + EXPECT_TRUE(param.skillArgs_->HasParam("key1")); + ASSERT_EQ(param.srcEntries_.size(), 1U); + EXPECT_EQ(param.srcEntries_[0], "src1.ts"); + EXPECT_EQ(param.requestCode_, TEST_REQUEST_CODE); + EXPECT_EQ(param.hapPath_, TEST_HAP_PATH); + TAG_LOGI(AAFwkTag::TEST, "end."); +} +} // namespace AppExecFwk +} // namespace OHOS diff --git a/test/unittest/skill_execute_result_test/BUILD.gn b/test/unittest/skill_execute_result_test/BUILD.gn new file mode 100644 index 0000000000..1bf7c76eef --- /dev/null +++ b/test/unittest/skill_execute_result_test/BUILD.gn @@ -0,0 +1,53 @@ +# 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("//build/test.gni") +import("//foundation/ability/ability_runtime/ability_runtime.gni") + +ohos_unittest("skill_execute_result_test") { + module_out_path = "ability_runtime/ability_runtime/skill" + + include_dirs = [] + + sources = [ "skill_execute_result_test.cpp" ] + + configs = [ "${ability_runtime_services_path}/abilitymgr:abilityms_config" ] + + cflags = [] + if (target_cpu == "arm") { + cflags += [ "-DBINDER_IPC_32BIT" ] + } + + deps = [ + "${ability_runtime_innerkits_path}/ability_manager:ability_manager", + "${ability_runtime_innerkits_path}/deps_wrapper:ability_deps_wrapper", + ] + + external_deps = [ + "ability_base:base", + "ability_base:want", + "bundle_framework:libappexecfwk_common", + "c_utils:utils", + "ffrt:libffrt", + "googletest:gmock_main", + "googletest:gtest_main", + "hilog:libhilog", + "ipc:ipc_core", + ] +} + +group("unittest") { + testonly = true + deps = [ ":skill_execute_result_test" ] +} diff --git a/test/unittest/skill_execute_result_test/skill_execute_result_test.cpp b/test/unittest/skill_execute_result_test/skill_execute_result_test.cpp new file mode 100644 index 0000000000..e3801f303d --- /dev/null +++ b/test/unittest/skill_execute_result_test/skill_execute_result_test.cpp @@ -0,0 +1,253 @@ +/* + * 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 + +#include "hilog_tag_wrapper.h" +#include "message_parcel.h" +#include "skill_execute_result.h" +#include "want_params.h" + +using namespace testing; +using namespace testing::ext; + +namespace OHOS { +namespace AppExecFwk { +namespace { +const int32_t TEST_CODE = 0; +const int32_t TEST_ERROR_CODE = -1; +const uint32_t TEST_FLAGS = 1; +const std::string TEST_URI = "file://docs/storage/test.txt"; +} // namespace + +void BuildFullSkillExecuteResult(SkillExecuteResult &result) +{ + result.code = TEST_CODE; + result.result = std::make_shared(); + result.uris = { TEST_URI, "file://docs/storage/test2.txt" }; + result.flags = TEST_FLAGS; +} + +class SkillExecuteResultTest : public testing::Test { +public: + static void SetUpTestCase(); + static void TearDownTestCase(); + void SetUp() override; + void TearDown() override; +}; + +void SkillExecuteResultTest::SetUpTestCase(void) +{} + +void SkillExecuteResultTest::TearDownTestCase(void) +{} + +void SkillExecuteResultTest::SetUp() +{} + +void SkillExecuteResultTest::TearDown() +{} + +/** + * @tc.name: Marshalling_0100 + * @tc.desc: Test Marshalling with default (empty/null result) SkillExecuteResult. + * @tc.type: FUNC + */ +HWTEST_F(SkillExecuteResultTest, Marshalling_0100, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "begin."); + MessageParcel parcel; + SkillExecuteResult result; + EXPECT_TRUE(result.Marshalling(parcel)); + TAG_LOGI(AAFwkTag::TEST, "end."); +} + +/** + * @tc.name: Marshalling_0200 + * @tc.desc: Test Marshalling with fully populated SkillExecuteResult. + * @tc.type: FUNC + */ +HWTEST_F(SkillExecuteResultTest, Marshalling_0200, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "begin."); + MessageParcel parcel; + SkillExecuteResult result; + BuildFullSkillExecuteResult(result); + EXPECT_TRUE(result.Marshalling(parcel)); + TAG_LOGI(AAFwkTag::TEST, "end."); +} + +/** + * @tc.name: Unmarshalling_0100 + * @tc.desc: Test Unmarshalling with empty parcel returns nullptr (wantParams null). + * @tc.type: FUNC + */ +HWTEST_F(SkillExecuteResultTest, Unmarshalling_0100, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "begin."); + MessageParcel parcel; + parcel.WriteInt32(0); // code + // No WantParams data - ReadParcelable returns nullptr + auto result = SkillExecuteResult::Unmarshalling(parcel); + EXPECT_EQ(result, nullptr); + TAG_LOGI(AAFwkTag::TEST, "end."); +} + +/** + * @tc.name: Unmarshalling_0200 + * @tc.desc: Test Unmarshalling with manually written valid data. + * @tc.type: FUNC + */ +HWTEST_F(SkillExecuteResultTest, Unmarshalling_0200, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "begin."); + MessageParcel parcel; + SkillExecuteResult original; + BuildFullSkillExecuteResult(original); + ASSERT_TRUE(original.Marshalling(parcel)); + + auto result = SkillExecuteResult::Unmarshalling(parcel); + ASSERT_NE(result, nullptr); + EXPECT_EQ(result->code, TEST_CODE); + ASSERT_NE(result->result, nullptr); + ASSERT_EQ(result->uris.size(), 2U); + EXPECT_EQ(result->uris[0], TEST_URI); + EXPECT_EQ(result->uris[1], "file://docs/storage/test2.txt"); + EXPECT_EQ(result->flags, TEST_FLAGS); + delete result; + TAG_LOGI(AAFwkTag::TEST, "end."); +} + +/** + * @tc.name: MarshallingAndUnmarshalling_0100 + * @tc.desc: Test round-trip Marshalling and Unmarshalling with full data. + * @tc.type: FUNC + */ +HWTEST_F(SkillExecuteResultTest, MarshallingAndUnmarshalling_0100, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "begin."); + MessageParcel parcel; + SkillExecuteResult result; + BuildFullSkillExecuteResult(result); + + EXPECT_TRUE(result.Marshalling(parcel)); + + auto restored = SkillExecuteResult::Unmarshalling(parcel); + ASSERT_NE(restored, nullptr); + EXPECT_EQ(restored->code, TEST_CODE); + ASSERT_NE(restored->result, nullptr); + ASSERT_EQ(restored->uris.size(), 2U); + EXPECT_EQ(restored->uris[0], TEST_URI); + EXPECT_EQ(restored->flags, TEST_FLAGS); + delete restored; + TAG_LOGI(AAFwkTag::TEST, "end."); +} + +/** + * @tc.name: ReadFromParcel_0100 + * @tc.desc: Test ReadFromParcel with valid parcel data. + * @tc.type: FUNC + */ +HWTEST_F(SkillExecuteResultTest, ReadFromParcel_0100, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "begin."); + MessageParcel parcel; + parcel.WriteInt32(TEST_ERROR_CODE); + AAFwk::WantParams params; + parcel.WriteParcelable(¶ms); + parcel.WriteInt32(0); // uriCount + parcel.WriteUint32(TEST_FLAGS); + + SkillExecuteResult result; + EXPECT_TRUE(result.ReadFromParcel(parcel)); + EXPECT_EQ(result.code, TEST_ERROR_CODE); + ASSERT_NE(result.result, nullptr); + EXPECT_EQ(result.uris.size(), 0U); + EXPECT_EQ(result.flags, TEST_FLAGS); + TAG_LOGI(AAFwkTag::TEST, "end."); +} + +/** + * @tc.name: ReadFromParcel_0200 + * @tc.desc: Test ReadFromParcel returns false when wantParams is null. + * @tc.type: FUNC + */ +HWTEST_F(SkillExecuteResultTest, ReadFromParcel_0200, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "begin."); + MessageParcel parcel; + parcel.WriteInt32(0); + // Write null WantParams - just write nothing that matches a valid parcelable + // ReadParcelable will return nullptr for empty parcel + auto result = SkillExecuteResult::Unmarshalling(parcel); + EXPECT_EQ(result, nullptr); + TAG_LOGI(AAFwkTag::TEST, "end."); +} + +/** + * @tc.name: MarshallingAndUnmarshalling_0200 + * @tc.desc: Test round-trip with no uris and zero flags. + * @tc.type: FUNC + */ +HWTEST_F(SkillExecuteResultTest, MarshallingAndUnmarshalling_0200, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "begin."); + MessageParcel parcel; + SkillExecuteResult result; + result.code = 100; + result.result = std::make_shared(); + result.uris = {}; + result.flags = 0; + + EXPECT_TRUE(result.Marshalling(parcel)); + + auto restored = SkillExecuteResult::Unmarshalling(parcel); + ASSERT_NE(restored, nullptr); + EXPECT_EQ(restored->code, 100); + EXPECT_EQ(restored->uris.size(), 0U); + EXPECT_EQ(restored->flags, 0U); + delete restored; + TAG_LOGI(AAFwkTag::TEST, "end."); +} + +/** + * @tc.name: MarshallingAndUnmarshalling_0300 + * @tc.desc: Test round-trip with multiple uris. + * @tc.type: FUNC + */ +HWTEST_F(SkillExecuteResultTest, MarshallingAndUnmarshalling_0300, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "begin."); + MessageParcel parcel; + SkillExecuteResult result; + result.code = 0; + result.result = std::make_shared(); + result.uris = { "uri1", "uri2", "uri3" }; + result.flags = 3; + + EXPECT_TRUE(result.Marshalling(parcel)); + + auto restored = SkillExecuteResult::Unmarshalling(parcel); + ASSERT_NE(restored, nullptr); + ASSERT_EQ(restored->uris.size(), 3U); + EXPECT_EQ(restored->uris[0], "uri1"); + EXPECT_EQ(restored->uris[1], "uri2"); + EXPECT_EQ(restored->uris[2], "uri3"); + EXPECT_EQ(restored->flags, 3U); + delete restored; + TAG_LOGI(AAFwkTag::TEST, "end."); +} +} // namespace AppExecFwk +} // namespace OHOS diff --git a/test/unittest/start_remote_ability_test/BUILD.gn b/test/unittest/start_remote_ability_test/BUILD.gn new file mode 100644 index 0000000000..63560f3368 --- /dev/null +++ b/test/unittest/start_remote_ability_test/BUILD.gn @@ -0,0 +1,70 @@ +# Copyright (c) 2025 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") + +ohos_unittest("start_remote_intent_test") { + module_out_path = "ability_runtime/ability_runtime/start_remote_intent_test" + + include_dirs = [ + "${ability_runtime_test_path}/mock/common/include", + "${ability_runtime_test_path}/mock/services_abilitymgr_test/libs/aakit/include", + ] + + sources = [ + "${ability_runtime_native_path}/ability/native/distributed_ability_runtime/distributed_client.cpp", + "${ability_runtime_test_path}/mock/services_abilitymgr_test/libs/aakit/src/mock_ability_connect_callback.cpp", + "mock_remote_intent_result_callback.cpp", + "start_remote_ability_test.cpp", + ] + + configs = [ + "${ability_runtime_test_path}/mock/services_abilitymgr_test:aafwk_mock_config", + ] + + deps = [ + "${ability_runtime_innerkits_path}/ability_manager:ability_connect_callback_stub", + "${ability_runtime_innerkits_path}/ability_manager:ability_manager", + "${ability_runtime_innerkits_path}/ability_manager:mission_info", + "${ability_runtime_native_path}/ability/native:abilitykit_native", + "${ability_runtime_native_path}/ability/native:abilitykit_utils", + "${ability_runtime_native_path}/appkit:app_context", + ] + + external_deps = [ + "ability_base:extractortool", + "ability_base:want", + "bundle_framework:appexecfwk_base", + "c_utils:utils", + "googletest:gtest_main", + "hilog:libhilog", + "hitrace:hitrace_meter", + "ipc:ipc_core", + "napi:ace_napi", + "resource_management:global_resmgr", + "samgr:samgr_proxy", + ] + + if (ability_runtime_graphics) { + external_deps += [ + "form_fwk:fmskit_native", + "i18n:intl_util", + ] + } +} + +group("unittest") { + testonly = true + deps = [ ":start_remote_intent_test" ] +} diff --git a/test/unittest/start_remote_ability_test/mock_remote_intent_result_callback.cpp b/test/unittest/start_remote_ability_test/mock_remote_intent_result_callback.cpp new file mode 100644 index 0000000000..dbb9ddd738 --- /dev/null +++ b/test/unittest/start_remote_ability_test/mock_remote_intent_result_callback.cpp @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2025 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 "mock_remote_intent_result_callback.h" + +namespace OHOS { +namespace AAFwk { +int RemoteIntentResultCallback::onIntentResultCount = 0; +int RemoteIntentResultCallback::onLinkDisconnectedCount = 0; + +void RemoteIntentResultCallback::OnIntentResult( + int32_t requestCode, int32_t resultCode, const std::string& resultMsg) +{ + onIntentResultCount++; +} + +void RemoteIntentResultCallback::OnLinkDisconnected(int32_t requestCode, int32_t reason) +{ + onLinkDisconnectedCount++; +} +} // namespace AAFwk +} // namespace OHOS diff --git a/test/unittest/start_remote_ability_test/mock_remote_intent_result_callback.h b/test/unittest/start_remote_ability_test/mock_remote_intent_result_callback.h new file mode 100644 index 0000000000..93b496a725 --- /dev/null +++ b/test/unittest/start_remote_ability_test/mock_remote_intent_result_callback.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2025 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_MOCK_REMOTE_INTENT_RESULT_CALLBACK_H +#define OHOS_ABILITY_RUNTIME_MOCK_REMOTE_INTENT_RESULT_CALLBACK_H + +#include +#include "iremote_stub.h" +#include "remote_intent_result_callback_interface.h" + +namespace OHOS { +namespace AAFwk { +class RemoteIntentResultCallback : public IRemoteStub { +public: + RemoteIntentResultCallback() = default; + virtual ~RemoteIntentResultCallback() = default; + + void OnIntentResult(int32_t requestCode, int32_t resultCode, const std::string& resultMsg) override; + void OnLinkDisconnected(int32_t requestCode, int32_t reason) override; + + static int onIntentResultCount; + static int onLinkDisconnectedCount; +}; +} // namespace AAFwk +} // namespace OHOS +#endif diff --git a/test/unittest/start_remote_ability_test/start_remote_ability_test.cpp b/test/unittest/start_remote_ability_test/start_remote_ability_test.cpp new file mode 100644 index 0000000000..3ff3c0c70b --- /dev/null +++ b/test/unittest/start_remote_ability_test/start_remote_ability_test.cpp @@ -0,0 +1,231 @@ +/* + * Copyright (c) 2025 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 +#include +#define private public +#define protected public +#include "ability_manager_errors.h" +#include "dms_intent_caller_info.h" +#include "distributed_client.h" +#include "distributed_parcel_helper.h" +#include "iservice_registry.h" +#include "iremote_object.h" +#include "mock_ability_connect_callback.h" +#include "mock_remote_intent_result_callback.h" +#include "parcel.h" +#undef protected +#undef private + +using namespace OHOS; +using namespace OHOS::AppExecFwk; +using namespace testing; +using namespace testing::ext; + +class StartRemoteIntentTest : public testing::Test { +public: + static void SetUpTestCase(); + static void TearDownTestCase(); + void SetUp(); + void TearDown(); +}; + +void StartRemoteIntentTest::SetUpTestCase() +{} + +void StartRemoteIntentTest::TearDownTestCase() +{} + +void StartRemoteIntentTest::SetUp() +{} + +void StartRemoteIntentTest::TearDown() +{} + +/** + * @tc.number: StartRemoteIntent_0100 + * @tc.name: StartRemoteIntent + * @tc.desc: StartRemoteIntent Test, return DMS_PERMISSION_DENIED. + */ +HWTEST_F(StartRemoteIntentTest, StartRemoteIntent_0100, TestSize.Level3) +{ + GTEST_LOG_(INFO) << "StartRemoteIntentTest StartRemoteIntent_0100 start"; + auto client = std::make_shared(); + OHOS::AAFwk::Want want; + OHOS::AAFwk::IntentCallerInfo callerInfo; + callerInfo.callerUid = 100; + callerInfo.requestCode = 1; + callerInfo.accessToken = 200; + callerInfo.specifyTokenId = 300; + sptr callback = new (std::nothrow) AAFwk::RemoteIntentResultCallback(); + int32_t result = client->StartRemoteIntent(want, callerInfo, callback); + if (client->GetDmsProxy() != nullptr) { + EXPECT_EQ(result, OHOS::AAFwk::DMS_PERMISSION_DENIED); + } else { + EXPECT_EQ(result, OHOS::AAFwk::INVALID_PARAMETERS_ERR); + } + GTEST_LOG_(INFO) << "StartRemoteIntentTest StartRemoteIntent_0100 end"; +} + +/** + * @tc.number: StartRemoteIntent_0200 + * @tc.name: StartRemoteIntent + * @tc.desc: StartRemoteIntent Test, return INVALID_PARAMETERS_ERR when samgr is nullptr. + */ +HWTEST_F(StartRemoteIntentTest, StartRemoteIntent_0200, TestSize.Level3) +{ + GTEST_LOG_(INFO) << "StartRemoteIntentTest StartRemoteIntent_0200 start"; + auto client = std::make_shared(); + OHOS::AAFwk::Want want; + OHOS::AAFwk::IntentCallerInfo callerInfo; + sptr callback = new (std::nothrow) AAFwk::RemoteIntentResultCallback(); + SystemAbilityManagerClient::GetInstance().systemAbilityManager_ = nullptr; + int32_t result = client->StartRemoteIntent(want, callerInfo, callback); + EXPECT_EQ(result, OHOS::AAFwk::INVALID_PARAMETERS_ERR); + GTEST_LOG_(INFO) << "StartRemoteIntentTest StartRemoteIntent_0200 end"; +} + +/** + * @tc.number: StartRemoteIntent_0300 + * @tc.name: StartRemoteIntent + * @tc.desc: StartRemoteIntent Test with specifyTokenId, return INVALID_PARAMETERS_ERR when samgr is nullptr. + */ +HWTEST_F(StartRemoteIntentTest, StartRemoteIntent_0300, TestSize.Level3) +{ + GTEST_LOG_(INFO) << "StartRemoteIntentTest StartRemoteIntent_0300 start"; + auto client = std::make_shared(); + OHOS::AAFwk::Want want; + OHOS::AAFwk::IntentCallerInfo callerInfo; + callerInfo.callerUid = 100; + callerInfo.requestCode = 1; + callerInfo.accessToken = 200; + callerInfo.specifyTokenId = 300; + sptr callback = new (std::nothrow) AAFwk::RemoteIntentResultCallback(); + SystemAbilityManagerClient::GetInstance().systemAbilityManager_ = nullptr; + int32_t result = client->StartRemoteIntent(want, callerInfo, callback); + EXPECT_EQ(result, OHOS::AAFwk::INVALID_PARAMETERS_ERR); + GTEST_LOG_(INFO) << "StartRemoteIntentTest StartRemoteIntent_0300 end"; +} + +/** + * @tc.number: StartRemoteIntent_0400 + * @tc.name: StartRemoteIntent + * @tc.desc: StartRemoteIntent Test with null callback, return DMS_PERMISSION_DENIED or INVALID_PARAMETERS_ERR. + */ +HWTEST_F(StartRemoteIntentTest, StartRemoteIntent_0400, TestSize.Level3) +{ + GTEST_LOG_(INFO) << "StartRemoteIntentTest StartRemoteIntent_0400 start"; + auto client = std::make_shared(); + OHOS::AAFwk::Want want; + OHOS::AAFwk::IntentCallerInfo callerInfo; + callerInfo.callerUid = 0; + int32_t result = client->StartRemoteIntent(want, callerInfo, nullptr); + if (client->GetDmsProxy() != nullptr) { + EXPECT_EQ(result, OHOS::AAFwk::DMS_PERMISSION_DENIED); + } else { + EXPECT_EQ(result, OHOS::AAFwk::INVALID_PARAMETERS_ERR); + } + GTEST_LOG_(INFO) << "StartRemoteIntentTest StartRemoteIntent_0400 end"; +} + +/** + * @tc.number: StartRemoteIntent_0500 + * @tc.name: StartRemoteIntent + * @tc.desc: StartRemoteIntent Test with null callback and samgr nullptr, return INVALID_PARAMETERS_ERR. + */ +HWTEST_F(StartRemoteIntentTest, StartRemoteIntent_0500, TestSize.Level3) +{ + GTEST_LOG_(INFO) << "StartRemoteIntentTest StartRemoteIntent_0500 start"; + auto client = std::make_shared(); + OHOS::AAFwk::Want want; + OHOS::AAFwk::IntentCallerInfo callerInfo; + SystemAbilityManagerClient::GetInstance().systemAbilityManager_ = nullptr; + int32_t result = client->StartRemoteIntent(want, callerInfo, nullptr); + EXPECT_EQ(result, OHOS::AAFwk::INVALID_PARAMETERS_ERR); + GTEST_LOG_(INFO) << "StartRemoteIntentTest StartRemoteIntent_0500 end"; +} + +/** + * @tc.number: StartRemoteIntent_0600 + * @tc.name: StartRemoteIntent + * @tc.desc: StartRemoteIntent Test with Want containing ElementName, return DMS_PERMISSION_DENIED. + */ +HWTEST_F(StartRemoteIntentTest, StartRemoteIntent_0600, TestSize.Level3) +{ + GTEST_LOG_(INFO) << "StartRemoteIntentTest StartRemoteIntent_0600 start"; + auto client = std::make_shared(); + OHOS::AAFwk::Want want; + AppExecFwk::ElementName element("deviceId", "com.test.bundle", "MainAbility"); + want.SetElement(element); + OHOS::AAFwk::IntentCallerInfo callerInfo; + callerInfo.callerUid = 100; + callerInfo.accessToken = 1000; + sptr callback = new (std::nothrow) AAFwk::RemoteIntentResultCallback(); + int32_t result = client->StartRemoteIntent(want, callerInfo, callback); + if (client->GetDmsProxy() != nullptr) { + EXPECT_EQ(result, OHOS::AAFwk::DMS_PERMISSION_DENIED); + } else { + EXPECT_EQ(result, OHOS::AAFwk::INVALID_PARAMETERS_ERR); + } + GTEST_LOG_(INFO) << "StartRemoteIntentTest StartRemoteIntent_0600 end"; +} + +/** + * @tc.number: StartRemoteIntent_0700 + * @tc.name: StartRemoteIntent + * @tc.desc: StartRemoteIntent Test with zero IntentCallerInfo, return DMS_PERMISSION_DENIED. + */ +HWTEST_F(StartRemoteIntentTest, StartRemoteIntent_0700, TestSize.Level3) +{ + GTEST_LOG_(INFO) << "StartRemoteIntentTest StartRemoteIntent_0700 start"; + auto client = std::make_shared(); + OHOS::AAFwk::Want want; + OHOS::AAFwk::IntentCallerInfo callerInfo; + callerInfo.callerUid = 0; + callerInfo.requestCode = 0; + callerInfo.accessToken = 0; + callerInfo.specifyTokenId = 0; + sptr callback = new (std::nothrow) AAFwk::RemoteIntentResultCallback(); + int32_t result = client->StartRemoteIntent(want, callerInfo, callback); + if (client->GetDmsProxy() != nullptr) { + EXPECT_EQ(result, OHOS::AAFwk::DMS_PERMISSION_DENIED); + } else { + EXPECT_EQ(result, OHOS::AAFwk::INVALID_PARAMETERS_ERR); + } + GTEST_LOG_(INFO) << "StartRemoteIntentTest StartRemoteIntent_0700 end"; +} + +/** + * @tc.number: StartRemoteIntent_0800 + * @tc.name: StartRemoteIntent + * @tc.desc: StartRemoteIntent Test with negative callerUid, return DMS_PERMISSION_DENIED. + */ +HWTEST_F(StartRemoteIntentTest, StartRemoteIntent_0800, TestSize.Level3) +{ + GTEST_LOG_(INFO) << "StartRemoteIntentTest StartRemoteIntent_0800 start"; + auto client = std::make_shared(); + OHOS::AAFwk::Want want; + OHOS::AAFwk::IntentCallerInfo callerInfo; + callerInfo.callerUid = -1; + callerInfo.requestCode = -1; + sptr callback = new (std::nothrow) AAFwk::RemoteIntentResultCallback(); + int32_t result = client->StartRemoteIntent(want, callerInfo, callback); + if (client->GetDmsProxy() != nullptr) { + EXPECT_EQ(result, OHOS::AAFwk::DMS_PERMISSION_DENIED); + } else { + EXPECT_EQ(result, OHOS::AAFwk::INVALID_PARAMETERS_ERR); + } + GTEST_LOG_(INFO) << "StartRemoteIntentTest StartRemoteIntent_0800 end"; +} diff --git a/test/unittest/sys_mgr_client_test/mock_ability_manager_service.h b/test/unittest/sys_mgr_client_test/mock_ability_manager_service.h index 33de221af2..de795606b3 100644 --- a/test/unittest/sys_mgr_client_test/mock_ability_manager_service.h +++ b/test/unittest/sys_mgr_client_test/mock_ability_manager_service.h @@ -148,8 +148,8 @@ public: MOCK_METHOD1(GetAbilityRunningInfos, int(std::vector& info)); MOCK_METHOD2(GetExtensionRunningInfos, int(int upperLimit, std::vector& info)); MOCK_METHOD1(GetProcessRunningInfos, int(std::vector& info)); - MOCK_METHOD7(StartAbilityByCall, - int(const Want&, const sptr&, const sptr&, int32_t, bool, bool, bool)); + MOCK_METHOD8(StartAbilityByCall, int(const Want&, const sptr&, + const sptr&, int32_t, bool, bool, bool, uint64_t)); MOCK_METHOD1(GetMissionIdByToken, int32_t(const sptr& token)); MOCK_METHOD2(GetAbilityTokenByCalleeObj, void(const sptr &callStub, sptr &token)); MOCK_METHOD2(AcquireShareData, int32_t(const int32_t &missionId, const sptr &shareData)); diff --git a/test/unittest/ui_ability_lifecycle_manager_test/ui_ability_lifecycle_manager_test.cpp b/test/unittest/ui_ability_lifecycle_manager_test/ui_ability_lifecycle_manager_test.cpp index 3f6418adc2..db23dd3313 100644 --- a/test/unittest/ui_ability_lifecycle_manager_test/ui_ability_lifecycle_manager_test.cpp +++ b/test/unittest/ui_ability_lifecycle_manager_test/ui_ability_lifecycle_manager_test.cpp @@ -7470,6 +7470,97 @@ HWTEST_F(UIAbilityLifecycleManagerTest, IsUIAbilityAlreadyExist_0003, TestSize.L EXPECT_EQ(ret, ERR_OK); } +/** + * @tc.name: IsSpecifiedUIAbilityAlreadyExist_0001 + * @tc.desc: Match specified ability should return ERROR_UIABILITY_IS_ALREADY_EXIST + * @tc.type: FUNC + */ +HWTEST_F(UIAbilityLifecycleManagerTest, IsSpecifiedUIAbilityAlreadyExist_0001, TestSize.Level1) +{ + auto mgr = std::make_shared(); + ASSERT_NE(mgr, nullptr); + + std::string abilityName = "AbilityC"; + std::string bundleName = "com.example.test"; + std::string moduleName = "entry"; + std::string specifiedFlag = "flagY"; + int32_t appIndex = 3; + std::string instanceKey = "ik3"; + + AbilityRequest abilityRequest; + abilityRequest.abilityInfo.name = abilityName; + abilityRequest.abilityInfo.bundleName = bundleName; + abilityRequest.abilityInfo.moduleName = moduleName; + abilityRequest.sessionInfo = new SessionInfo(); + abilityRequest.sessionInfo->instanceKey = instanceKey; + auto record = UIAbilityRecord::CreateAbilityRecord(abilityRequest); + record->SetAppIndex(appIndex); + record->SetInstanceKey(instanceKey); + record->SetSpecifiedFlag(specifiedFlag); + mgr->sessionAbilityMap_[20] = record; + + Want want; + want.SetElementName("device", bundleName, abilityName, moduleName); + auto ret = mgr->IsSpecifiedUIAbilityAlreadyExist(want, specifiedFlag, appIndex, instanceKey); + EXPECT_EQ(ret, ERROR_UIABILITY_IS_ALREADY_EXIST); +} + +/** + * @tc.name: IsSpecifiedUIAbilityAlreadyExist_0002 + * @tc.desc: Return ERR_OK when specified ability does not match + * @tc.type: FUNC + */ +HWTEST_F(UIAbilityLifecycleManagerTest, IsSpecifiedUIAbilityAlreadyExist_0002, TestSize.Level1) +{ + auto mgr = std::make_shared(); + ASSERT_NE(mgr, nullptr); + + AbilityRequest abilityRequest; + abilityRequest.abilityInfo.name = "OtherAbility"; + abilityRequest.abilityInfo.moduleName = "entry"; + abilityRequest.sessionInfo = new SessionInfo(); + abilityRequest.sessionInfo->instanceKey = "ik4"; + auto record = UIAbilityRecord::CreateAbilityRecord(abilityRequest); + record->SetAppIndex(0); + record->SetInstanceKey("ik4"); + record->SetSpecifiedFlag("otherFlag"); + mgr->sessionAbilityMap_[21] = record; + + Want want; + want.SetElementName("device", "com.example.test", "AbilityD", "entry"); + auto ret = mgr->IsSpecifiedUIAbilityAlreadyExist(want, "flagZ", 0, "ik4"); + EXPECT_EQ(ret, ERR_OK); +} + +/** + * @tc.name: IsSpecifiedUIAbilityAlreadyExist_0003 + * @tc.desc: Empty module should not block matching and nullptr record should be skipped + * @tc.type: FUNC + */ +HWTEST_F(UIAbilityLifecycleManagerTest, IsSpecifiedUIAbilityAlreadyExist_0003, TestSize.Level1) +{ + auto mgr = std::make_shared(); + ASSERT_NE(mgr, nullptr); + mgr->sessionAbilityMap_[30] = nullptr; + + AbilityRequest abilityRequest; + abilityRequest.abilityInfo.name = "AbilityE"; + abilityRequest.abilityInfo.bundleName = "com.example.test"; + abilityRequest.abilityInfo.moduleName = "entry"; + abilityRequest.sessionInfo = new SessionInfo(); + abilityRequest.sessionInfo->instanceKey = "ik5"; + auto record = UIAbilityRecord::CreateAbilityRecord(abilityRequest); + record->SetAppIndex(1); + record->SetInstanceKey("ik5"); + record->SetSpecifiedFlag("flagE"); + mgr->sessionAbilityMap_[31] = record; + + Want want; + want.SetElementName("device", "com.example.test", "AbilityE", ""); + auto ret = mgr->IsSpecifiedUIAbilityAlreadyExist(want, "flagE", 1, "ik5"); + EXPECT_EQ(ret, ERROR_UIABILITY_IS_ALREADY_EXIST); +} + /** * @tc.name: FindUIAbilityRecordByIdLocked_0001 * @tc.desc: FindUIAbilityRecordByIdLocked @@ -8517,7 +8608,7 @@ HWTEST_F(UIAbilityLifecycleManagerTest, SetGamePreLaunchCompleteTime_003, TestSi */ HWTEST_F(UIAbilityLifecycleManagerTest, StartSelf_001, TestSize.Level1) { - auto mgr = std::make_unique(); + auto mgr = std::make_shared(); EXPECT_EQ(mgr->StartSelf(nullptr), ERR_INVALID_VALUE); } @@ -8528,7 +8619,7 @@ HWTEST_F(UIAbilityLifecycleManagerTest, StartSelf_001, TestSize.Level1) */ HWTEST_F(UIAbilityLifecycleManagerTest, StartSelf_002, TestSize.Level1) { - auto mgr = std::make_unique(); + auto mgr = std::make_shared(); auto abilityRecord = InitAbilityRecord(); EXPECT_NE(abilityRecord, nullptr); // Default nativeState is NONE @@ -8543,7 +8634,7 @@ HWTEST_F(UIAbilityLifecycleManagerTest, StartSelf_002, TestSize.Level1) */ HWTEST_F(UIAbilityLifecycleManagerTest, StartSelf_003, TestSize.Level1) { - auto mgr = std::make_unique(); + auto mgr = std::make_shared(); auto abilityRecord = InitAbilityRecord(); abilityRecord->SetNativeState(AbilityNativeState::NORMAL); EXPECT_EQ(mgr->StartSelf(abilityRecord), ERR_INVALID_VALUE); @@ -8556,7 +8647,7 @@ HWTEST_F(UIAbilityLifecycleManagerTest, StartSelf_003, TestSize.Level1) */ HWTEST_F(UIAbilityLifecycleManagerTest, StartSelf_004, TestSize.Level1) { - auto mgr = std::make_unique(); + auto mgr = std::make_shared(); auto abilityRecord = InitAbilityRecord(); abilityRecord->SetNativeState(AbilityNativeState::NORMAL); sptr sessionInfo(new SessionInfo()); @@ -8572,7 +8663,7 @@ HWTEST_F(UIAbilityLifecycleManagerTest, StartSelf_004, TestSize.Level1) */ HWTEST_F(UIAbilityLifecycleManagerTest, StartSelf_005, TestSize.Level1) { - auto mgr = std::make_unique(); + auto mgr = std::make_shared(); auto abilityRecord = InitAbilityRecord(); abilityRecord->SetNativeState(AbilityNativeState::NORMAL); @@ -8598,7 +8689,7 @@ HWTEST_F(UIAbilityLifecycleManagerTest, StartSelf_005, TestSize.Level1) */ HWTEST_F(UIAbilityLifecycleManagerTest, StartSelf_006, TestSize.Level1) { - auto mgr = std::make_unique(); + auto mgr = std::make_shared(); auto abilityRecord = InitAbilityRecord(); abilityRecord->SetNativeState(AbilityNativeState::CREATED); @@ -8622,7 +8713,7 @@ HWTEST_F(UIAbilityLifecycleManagerTest, StartSelf_006, TestSize.Level1) */ HWTEST_F(UIAbilityLifecycleManagerTest, StartSelf_007, TestSize.Level1) { - auto mgr = std::make_unique(); + auto mgr = std::make_shared(); auto abilityRecord = InitAbilityRecord(); abilityRecord->SetNativeState(AbilityNativeState::ON_FOREGROUND); @@ -8646,7 +8737,7 @@ HWTEST_F(UIAbilityLifecycleManagerTest, StartSelf_007, TestSize.Level1) */ HWTEST_F(UIAbilityLifecycleManagerTest, DispatchForeground_NativeModuleAttached_0100, TestSize.Level1) { - auto mgr = std::make_unique(); + auto mgr = std::make_shared(); auto abilityRecord = InitAbilityRecord(); abilityRecord->SetNativeState(AbilityNativeState::ATTACHED); abilityRecord->SetAbilityState(AbilityState::FOREGROUNDING); @@ -8664,7 +8755,7 @@ HWTEST_F(UIAbilityLifecycleManagerTest, DispatchForeground_NativeModuleAttached_ */ HWTEST_F(UIAbilityLifecycleManagerTest, DispatchForeground_NativeModuleOnForeground_0100, TestSize.Level1) { - auto mgr = std::make_unique(); + auto mgr = std::make_shared(); auto abilityRecord = InitAbilityRecord(); abilityRecord->SetNativeState(AbilityNativeState::ON_FOREGROUND); abilityRecord->SetAbilityState(AbilityState::FOREGROUNDING); @@ -8681,7 +8772,7 @@ HWTEST_F(UIAbilityLifecycleManagerTest, DispatchForeground_NativeModuleOnForegro */ HWTEST_F(UIAbilityLifecycleManagerTest, CalcHideNativeWindow_0002, TestSize.Level1) { - auto mgr = std::make_unique(); + auto mgr = std::make_shared(); AppExecFwk::AbilityInfo abilityInfo; EXPECT_FALSE(mgr->CalcHideNativeWindow(0, abilityInfo)); @@ -8694,7 +8785,7 @@ HWTEST_F(UIAbilityLifecycleManagerTest, CalcHideNativeWindow_0002, TestSize.Leve */ HWTEST_F(UIAbilityLifecycleManagerTest, CalcHideNativeWindow_0003, TestSize.Level1) { - auto mgr = std::make_unique(); + auto mgr = std::make_shared(); AppExecFwk::AbilityInfo abilityInfo; EXPECT_FALSE(mgr->CalcHideNativeWindow(100, abilityInfo)); @@ -8707,7 +8798,7 @@ HWTEST_F(UIAbilityLifecycleManagerTest, CalcHideNativeWindow_0003, TestSize.Leve */ HWTEST_F(UIAbilityLifecycleManagerTest, CalcHideNativeWindow_0004, TestSize.Level1) { - auto mgr = std::make_unique(); + auto mgr = std::make_shared(); AppExecFwk::AbilityInfo abilityInfo; auto abilityRecord = InitAbilityRecord(); mgr->sessionAbilityMap_[1] = abilityRecord; @@ -8723,7 +8814,7 @@ HWTEST_F(UIAbilityLifecycleManagerTest, CalcHideNativeWindow_0004, TestSize.Leve */ HWTEST_F(UIAbilityLifecycleManagerTest, CalcHideNativeWindow_0005, TestSize.Level1) { - auto mgr = std::make_unique(); + auto mgr = std::make_shared(); AppExecFwk::AbilityInfo abilityInfo; auto abilityRecord = InitAbilityRecord(); abilityRecord->SetNativeState(AbilityNativeState::NORMAL); @@ -8739,7 +8830,7 @@ HWTEST_F(UIAbilityLifecycleManagerTest, CalcHideNativeWindow_0005, TestSize.Leve */ HWTEST_F(UIAbilityLifecycleManagerTest, CalcHideNativeWindow_0006, TestSize.Level1) { - auto mgr = std::make_unique(); + auto mgr = std::make_shared(); AppExecFwk::AbilityInfo abilityInfo; auto abilityRecord = InitAbilityRecord(); abilityRecord->SetNativeState(AbilityNativeState::INIT); @@ -8755,7 +8846,7 @@ HWTEST_F(UIAbilityLifecycleManagerTest, CalcHideNativeWindow_0006, TestSize.Leve */ HWTEST_F(UIAbilityLifecycleManagerTest, CalcHideNativeWindow_0007, TestSize.Level1) { - auto mgr = std::make_unique(); + auto mgr = std::make_shared(); AppExecFwk::AbilityInfo abilityInfo; auto abilityRecord = InitAbilityRecord(); abilityRecord->SetNativeState(AbilityNativeState::ATTACHED); @@ -8771,7 +8862,7 @@ HWTEST_F(UIAbilityLifecycleManagerTest, CalcHideNativeWindow_0007, TestSize.Leve */ HWTEST_F(UIAbilityLifecycleManagerTest, CalcHideNativeWindow_0008, TestSize.Level1) { - auto mgr = std::make_unique(); + auto mgr = std::make_shared(); AppExecFwk::AbilityInfo abilityInfo; auto abilityRecord = InitAbilityRecord(); abilityRecord->SetNativeState(AbilityNativeState::CREATED); @@ -8787,7 +8878,7 @@ HWTEST_F(UIAbilityLifecycleManagerTest, CalcHideNativeWindow_0008, TestSize.Leve */ HWTEST_F(UIAbilityLifecycleManagerTest, CalcHideNativeWindow_0009, TestSize.Level1) { - auto mgr = std::make_unique(); + auto mgr = std::make_shared(); AppExecFwk::AbilityInfo abilityInfo; auto abilityRecord = InitAbilityRecord(); abilityRecord->SetNativeState(AbilityNativeState::ON_FOREGROUND); @@ -8803,11 +8894,93 @@ HWTEST_F(UIAbilityLifecycleManagerTest, CalcHideNativeWindow_0009, TestSize.Leve */ HWTEST_F(UIAbilityLifecycleManagerTest, CalcHideNativeWindow_0010, TestSize.Level1) { - auto mgr = std::make_unique(); + auto mgr = std::make_shared(); AppExecFwk::AbilityInfo abilityInfo; mgr->sessionAbilityMap_[1] = nullptr; EXPECT_FALSE(mgr->CalcHideNativeWindow(1, abilityInfo)); } + +/** + * @tc.name: CompleteForegroundSuccess_GameSAPreLaunch_0001 + * @tc.desc: CompleteForegroundSuccess with GameSAPreLaunch = true + * @tc.type: FUNC + */ +HWTEST_F(UIAbilityLifecycleManagerTest, CompleteForegroundSuccess_GameSAPreLaunch_0001, TestSize.Level1) +{ + auto mgr = std::make_unique(); + AbilityRequest abilityRequest; + auto abilityRecord = UIAbilityRecord::CreateAbilityRecord(abilityRequest); + abilityRecord->SetPendingState(AbilityState::FOREGROUND); + abilityRecord->SetGameSAPreLaunch(true); + + mgr->CompleteForegroundSuccess(abilityRecord); + + EXPECT_EQ(abilityRecord->GetAbilityState(), AbilityState::FOREGROUND); + EXPECT_TRUE(abilityRecord->IsGameSAPreLaunch()); +} + +/** + * @tc.name: CompleteForegroundSuccess_GameSAPreLaunch_0002 + * @tc.desc: CompleteForegroundSuccess with GameSAPreLaunch = false + * @tc.type: FUNC + */ +HWTEST_F(UIAbilityLifecycleManagerTest, CompleteForegroundSuccess_GameSAPreLaunch_0002, TestSize.Level1) +{ + auto mgr = std::make_unique(); + AbilityRequest abilityRequest; + auto abilityRecord = UIAbilityRecord::CreateAbilityRecord(abilityRequest); + abilityRecord->SetPendingState(AbilityState::FOREGROUND); + abilityRecord->SetGameSAPreLaunch(false); + + mgr->CompleteForegroundSuccess(abilityRecord); + + EXPECT_EQ(abilityRecord->GetAbilityState(), AbilityState::FOREGROUND); + EXPECT_FALSE(abilityRecord->IsGameSAPreLaunch()); +} + +/** + * @tc.name: CompleteForegroundSuccess_GameSAPreLaunch_0003 + * @tc.desc: CompleteForegroundSuccess with GameSAPreLaunch = true and startedByCall = true + * @tc.type: FUNC + */ +HWTEST_F(UIAbilityLifecycleManagerTest, CompleteForegroundSuccess_GameSAPreLaunch_0003, TestSize.Level1) +{ + auto mgr = std::make_unique(); + AbilityRequest abilityRequest; + auto abilityRecord = UIAbilityRecord::CreateAbilityRecord(abilityRequest); + abilityRecord->SetPendingState(AbilityState::FOREGROUND); + abilityRecord->SetGameSAPreLaunch(true); + abilityRecord->SetStartedByCall(true); + abilityRecord->SetStartToForeground(true); + abilityRecord->isReady_ = true; + + mgr->CompleteForegroundSuccess(abilityRecord); + + EXPECT_EQ(abilityRecord->GetAbilityState(), AbilityState::FOREGROUND); + EXPECT_TRUE(abilityRecord->IsGameSAPreLaunch()); +} + +/** + * @tc.name: CompleteForegroundSuccess_GameSAPreLaunch_0004 + * @tc.desc: CompleteForegroundSuccess with GameSAPreLaunch = true and HasLastWant = true + * @tc.type: FUNC + */ +HWTEST_F(UIAbilityLifecycleManagerTest, CompleteForegroundSuccess_GameSAPreLaunch_0004, TestSize.Level1) +{ + auto mgr = std::make_unique(); + AbilityRequest abilityRequest; + Want lastWant; + lastWant.SetElementName("com.example.unittest", "MainAbility"); + auto abilityRecord = UIAbilityRecord::CreateAbilityRecord(abilityRequest); + abilityRecord->SetPendingState(AbilityState::FOREGROUND); + abilityRecord->SetGameSAPreLaunch(true); + abilityRecord->SetLastWant(std::make_shared(lastWant)); + + mgr->CompleteForegroundSuccess(abilityRecord); + + EXPECT_EQ(abilityRecord->GetAbilityState(), AbilityState::FOREGROUNDING); + EXPECT_TRUE(abilityRecord->IsGameSAPreLaunch()); +} } // namespace AAFwk } // namespace OHOS diff --git a/test/unittest/ui_ability_lifecycle_manager_third_test/mock/include/ffrt.h b/test/unittest/ui_ability_lifecycle_manager_third_test/mock/include/ffrt.h index a22f84beec..3dcb027e9e 100644 --- a/test/unittest/ui_ability_lifecycle_manager_third_test/mock/include/ffrt.h +++ b/test/unittest/ui_ability_lifecycle_manager_third_test/mock/include/ffrt.h @@ -23,6 +23,7 @@ #include "cpp/mutex.h" #include "cpp/condition_variable.h" +#include "mock_my_flag.h" namespace ffrt { struct task_attr { @@ -35,6 +36,7 @@ struct task_handle {}; inline void submit(std::function &&task, task_attr attr = {}) { + OHOS::AAFwk::MyFlag::ffrtSubmitFlag_++; if (task) { std::thread taskThread(task); taskThread.detach(); diff --git a/test/unittest/ui_ability_lifecycle_manager_third_test/mock/include/mock_my_flag.h b/test/unittest/ui_ability_lifecycle_manager_third_test/mock/include/mock_my_flag.h index c79101d18d..29a4072aec 100644 --- a/test/unittest/ui_ability_lifecycle_manager_third_test/mock/include/mock_my_flag.h +++ b/test/unittest/ui_ability_lifecycle_manager_third_test/mock/include/mock_my_flag.h @@ -21,6 +21,7 @@ namespace AAFwk { class MyFlag { public: static int flag_; + static int ffrtSubmitFlag_; }; } // namespace AAFwk } // namespace OHOS diff --git a/test/unittest/ui_ability_lifecycle_manager_third_test/mock/src/mock_my_flag.cpp b/test/unittest/ui_ability_lifecycle_manager_third_test/mock/src/mock_my_flag.cpp index e21af59a45..015ebc7822 100644 --- a/test/unittest/ui_ability_lifecycle_manager_third_test/mock/src/mock_my_flag.cpp +++ b/test/unittest/ui_ability_lifecycle_manager_third_test/mock/src/mock_my_flag.cpp @@ -18,5 +18,6 @@ namespace OHOS { namespace AAFwk { int MyFlag::flag_ = 0; +int MyFlag::ffrtSubmitFlag_ = 0; } // namespace AAFwk } // namespace OHOS diff --git a/test/unittest/ui_ability_lifecycle_manager_third_test/ui_ability_lifecycle_manager_third_test.cpp b/test/unittest/ui_ability_lifecycle_manager_third_test/ui_ability_lifecycle_manager_third_test.cpp index a9feaf6e19..053fcf292c 100644 --- a/test/unittest/ui_ability_lifecycle_manager_third_test/ui_ability_lifecycle_manager_third_test.cpp +++ b/test/unittest/ui_ability_lifecycle_manager_third_test/ui_ability_lifecycle_manager_third_test.cpp @@ -1840,5 +1840,83 @@ HWTEST_F(UIAbilityLifecycleManagerThirdTest, SyncLoadExitReasonTask_001, TestSiz EXPECT_TRUE(mgr->exitReasonTasks_.empty()); } + +/** + * @tc.name: UIAbilityLifecycleManager_HandleStartSelfTimeout_0100 + * @tc.desc: HandleStartSelfTimeout with null abilityRecord + * @tc.type: FUNC + */ +HWTEST_F(UIAbilityLifecycleManagerThirdTest, HandleStartSelfTimeout_001, TestSize.Level1) +{ + auto uiAbilityLifecycleManager = std::make_shared(); + MyFlag::ffrtSubmitFlag_ = 0; + uiAbilityLifecycleManager->HandleStartSelfTimeout(nullptr, false); + EXPECT_EQ(MyFlag::ffrtSubmitFlag_, 0); + uiAbilityLifecycleManager.reset(); +} + +/** + * @tc.name: UIAbilityLifecycleManager_HandleStartSelfTimeout_0200 + * @tc.desc: HandleStartSelfTimeout with native state not CREATED + * @tc.type: FUNC + */ +HWTEST_F(UIAbilityLifecycleManagerThirdTest, HandleStartSelfTimeout_002, TestSize.Level1) +{ + auto uiAbilityLifecycleManager = std::make_shared(); + UIAbilityRecordPtr abilityRecord = InitAbilityRecord(); + abilityRecord->SetNativeState(AbilityNativeState::ATTACHED); + MyFlag::ffrtSubmitFlag_ = 0; + uiAbilityLifecycleManager->HandleStartSelfTimeout(abilityRecord, false); + EXPECT_EQ(MyFlag::ffrtSubmitFlag_, 0); + uiAbilityLifecycleManager.reset(); +} + +/** + * @tc.name: UIAbilityLifecycleManager_HandleStartSelfTimeout_0300 + * @tc.desc: HandleStartSelfTimeout with isHalf=true, should not call ffrt::submit + * @tc.type: FUNC + */ +HWTEST_F(UIAbilityLifecycleManagerThirdTest, HandleStartSelfTimeout_003, TestSize.Level1) +{ + auto uiAbilityLifecycleManager = std::make_shared(); + UIAbilityRecordPtr abilityRecord = InitAbilityRecord(); + abilityRecord->SetNativeState(AbilityNativeState::CREATED); + MyFlag::ffrtSubmitFlag_ = 0; + uiAbilityLifecycleManager->HandleStartSelfTimeout(abilityRecord, true); + EXPECT_LE(MyFlag::ffrtSubmitFlag_, 1); + uiAbilityLifecycleManager.reset(); +} + +/** + * @tc.name: UIAbilityLifecycleManager_HandleStartSelfTimeout_0400 + * @tc.desc: HandleStartSelfTimeout with isHalf=false and native state CREATED, should call ffrt::submit for kill + * @tc.type: FUNC + */ +HWTEST_F(UIAbilityLifecycleManagerThirdTest, HandleStartSelfTimeout_004, TestSize.Level1) +{ + auto uiAbilityLifecycleManager = std::make_shared(); + UIAbilityRecordPtr abilityRecord = InitAbilityRecord(); + abilityRecord->SetNativeState(AbilityNativeState::CREATED); + MyFlag::ffrtSubmitFlag_ = 0; + uiAbilityLifecycleManager->HandleStartSelfTimeout(abilityRecord, false); + EXPECT_GE(MyFlag::ffrtSubmitFlag_, 1); + uiAbilityLifecycleManager.reset(); +} + +/** + * @tc.name: UIAbilityLifecycleManager_PostStartSelfTimeoutEvent_0100 + * @tc.desc: PostStartSelfTimeoutEvent should call ffrt::submit twice for half and full timeout + * @tc.type: FUNC + */ +HWTEST_F(UIAbilityLifecycleManagerThirdTest, PostStartSelfTimeoutEvent_001, TestSize.Level1) +{ + auto uiAbilityLifecycleManager = std::make_shared(); + UIAbilityRecordPtr abilityRecord = InitAbilityRecord(); + abilityRecord->SetNativeState(AbilityNativeState::NORMAL); + MyFlag::ffrtSubmitFlag_ = 0; + uiAbilityLifecycleManager->PostStartSelfTimeoutEvent(abilityRecord); + EXPECT_GE(MyFlag::ffrtSubmitFlag_, 2); + uiAbilityLifecycleManager.reset(); +} } // namespace AAFwk } // namespace OHOS \ No newline at end of file diff --git a/test/unittest/ui_ability_record_test/BUILD.gn b/test/unittest/ui_ability_record_test/BUILD.gn index 1cbb794b44..9d577ab32e 100644 --- a/test/unittest/ui_ability_record_test/BUILD.gn +++ b/test/unittest/ui_ability_record_test/BUILD.gn @@ -27,6 +27,7 @@ ohos_unittest("ui_ability_record_test") { "${ability_runtime_services_path}/abilitymgr/include/scene_board", "${ability_runtime_services_path}/common/include", "${ability_runtime_path}/interfaces/inner_api/ability_manager/include", + "${ability_runtime_path}/utils/global/constant", ] sources = [ @@ -42,6 +43,7 @@ ohos_unittest("ui_ability_record_test") { ] external_deps = [ + "ability_base:want", "googletest:gmock_main", "hilog:libhilog", "hitrace:hitrace_meter", diff --git a/test/unittest/ui_ability_record_test/mock/include/ability_record.h b/test/unittest/ui_ability_record_test/mock/include/ability_record.h index cbb359a577..c533a8867d 100644 --- a/test/unittest/ui_ability_record_test/mock/include/ability_record.h +++ b/test/unittest/ui_ability_record_test/mock/include/ability_record.h @@ -23,6 +23,7 @@ #include "ability_info.h" #include "ability_state.h" +#include "want.h" namespace OHOS { namespace AAFwk { @@ -32,8 +33,6 @@ enum class AbilityRecordType { MISSION_ABILITY, }; -class Want {}; - struct AbilityRequest { int32_t requestCode = 0; Want want; @@ -50,7 +49,7 @@ class AbilityRecord : public std::enable_shared_from_this { public: AbilityRecord(const Want &want, const AppExecFwk::AbilityInfo &abilityInfo, const AppExecFwk::ApplicationInfo &applicationInfo, int32_t requestCode) - : abilityInfo_(abilityInfo) {} + : abilityInfo_(abilityInfo), want_(want) {} virtual ~AbilityRecord() = default; virtual void Init(const AbilityRequest &abilityRequest); @@ -60,19 +59,40 @@ public: { pendingState_ = state; } + inline AbilityState GetPendingState() const { return pendingState_; } + + inline void SetGameSAPreLaunch(bool isGameSAPreLaunch) + { + isGameSAPreLaunch_ = isGameSAPreLaunch; + } + + inline bool IsGameSAPreLaunch() const + { + return isGameSAPreLaunch_; + } + + inline void SetIsNewWant(bool) {} + inline void SetWant(Want want) + { + want_ = want; + } protected: bool isPrelaunch_ = false; bool isHook_ = false; + bool isGameSAPreLaunch_ = false; AbilityState pendingState_ = AbilityState::INITIAL; std::atomic_bool isLastWantBackgroundDriven_ = false; std::mutex collaborateWantLock_; std::shared_ptr lifecycleDeal_; AppExecFwk::AbilityInfo abilityInfo_; + + mutable std::mutex wantLock_; + Want want_; }; } // namespace AAFwk } // namespace OHOS diff --git a/test/unittest/ui_ability_record_test/ui_ability_record_test.cpp b/test/unittest/ui_ability_record_test/ui_ability_record_test.cpp index 41afe57c5b..90bbafab8d 100644 --- a/test/unittest/ui_ability_record_test/ui_ability_record_test.cpp +++ b/test/unittest/ui_ability_record_test/ui_ability_record_test.cpp @@ -16,6 +16,7 @@ #include #include "ui_ability_record.h" +#include "global_constant.h" #include "native_ability_util.h" using namespace testing::ext; @@ -404,5 +405,93 @@ HWTEST_F(UIAbilityRecordTest, CreateAbilityRecord_ForegroundPhase_0100, TestSize EXPECT_EQ(abilityRecord->GetNativeState(), AbilityNativeState::NORMAL); } +/** + * @tc.name: UpdateWantByLastWant_0100 + * @tc.desc: ShouldUpdateWant is false, should return false directly. + * @tc.type: FUNC + */ +HWTEST_F(UIAbilityRecordTest, UpdateWantByLastWant_0100, TestSize.Level1) +{ + AbilityRequest abilityRequest; + auto abilityRecord = UIAbilityRecord::CreateAbilityRecord(abilityRequest); + + abilityRecord->SetShouldUpdateWant(false); + EXPECT_FALSE(abilityRecord->UpdateWantByLastWant()); +} + +/** + * @tc.name: UpdateWantByLastWant_0200 + * @tc.desc: ShouldUpdateWant is true but lastWant is nullptr, should return false. + * @tc.type: FUNC + */ +HWTEST_F(UIAbilityRecordTest, UpdateWantByLastWant_0200, TestSize.Level1) +{ + AbilityRequest abilityRequest; + auto abilityRecord = UIAbilityRecord::CreateAbilityRecord(abilityRequest); + + abilityRecord->SetLastWant(nullptr); + abilityRecord->SetShouldUpdateWant(true); + EXPECT_FALSE(abilityRecord->UpdateWantByLastWant()); + // ShouldUpdateWant should be reset to false + EXPECT_FALSE(abilityRecord->ShouldUpdateWant()); +} + +/** + * @tc.name: UpdateWantByLastWant_0300 + * @tc.desc: ShouldUpdateWant is true and lastWant is valid, should update want and return true. + * @tc.type: FUNC + */ +HWTEST_F(UIAbilityRecordTest, UpdateWantByLastWant_0300, TestSize.Level1) +{ + AbilityRequest abilityRequest; + abilityRequest.want.SetElementName("com.test", "MainAbility"); + auto abilityRecord = UIAbilityRecord::CreateAbilityRecord(abilityRequest); + + auto lastWant = std::make_shared(); + lastWant->SetElementName("com.test", "MainAbility"); + abilityRecord->SetLastWant(lastWant); + abilityRecord->SetShouldUpdateWant(true); + + EXPECT_TRUE(abilityRecord->UpdateWantByLastWant()); + + // ShouldUpdateWant should be reset to false + EXPECT_FALSE(abilityRecord->ShouldUpdateWant()); + // lastWant should be cleared + EXPECT_FALSE(abilityRecord->HasLastWant()); +} + +/** + * @tc.name: UpdateWantByLastWant_0400 + * @tc.desc: Call UpdateWantByLastWant twice, second call should return false. + * @tc.type: FUNC + */ +HWTEST_F(UIAbilityRecordTest, UpdateWantByLastWant_0400, TestSize.Level1) +{ + AbilityRequest abilityRequest; + auto abilityRecord = UIAbilityRecord::CreateAbilityRecord(abilityRequest); + + auto lastWant = std::make_shared(); + lastWant->SetElementName("com.test", "MainAbility"); + abilityRecord->SetLastWant(lastWant); + abilityRecord->SetShouldUpdateWant(true); + + EXPECT_TRUE(abilityRecord->UpdateWantByLastWant()); + // Second call: ShouldUpdateWant has been reset to false + EXPECT_FALSE(abilityRecord->UpdateWantByLastWant()); +} + +/** + * @tc.name: SetGameSAPreLaunch_0100 + * @tc.desc: Call SetGameSAPreLaunch + * @tc.type: FUNC + */ +HWTEST_F(UIAbilityRecordTest, SetGameSAPreLaunch_0100, TestSize.Level1) +{ + AbilityRequest abilityRequest; + abilityRequest.want.SetParam(AbilityRuntime::GlobalConstant::GAME_PRELAUNCH, true); + auto abilityRecord = UIAbilityRecord::CreateAbilityRecord(abilityRequest); + + EXPECT_TRUE(abilityRecord->IsGameSAPreLaunch()); +} } // namespace AAFwk } // namespace OHOS diff --git a/test/unittest/ui_extension/BUILD.gn b/test/unittest/ui_extension/BUILD.gn index 7d5692103b..26f57536e7 100644 --- a/test/unittest/ui_extension/BUILD.gn +++ b/test/unittest/ui_extension/BUILD.gn @@ -19,5 +19,6 @@ group("unittest") { "extension_record_manager_test:unittest", "ui_extension_get_host_info_test:unittest", "ui_extension_session_info_test:unittest", + "ui_extension_test:unittest", ] } diff --git a/test/unittest/ui_extension/extension_record_manager_test/extension_record_manager_test.cpp b/test/unittest/ui_extension/extension_record_manager_test/extension_record_manager_test.cpp index 1e6a995a40..e9d9a95ed2 100755 --- a/test/unittest/ui_extension/extension_record_manager_test/extension_record_manager_test.cpp +++ b/test/unittest/ui_extension/extension_record_manager_test/extension_record_manager_test.cpp @@ -1933,5 +1933,289 @@ HWTEST_F(ExtensionRecordManagerTest, RollbackAgentUILaunchRecord_0100, TestSize. TAG_LOGI(AAFwkTag::TEST, "RollbackAgentUILaunchRecord_0100 end"); } + +/** + * @tc.name: UpdateProcessName_CallerInstance_0100 + * @tc.desc: Test UpdateProcessName with PROCESS_MODE_CALLER_INSTANCE, appIndex == 0. + * @tc.type: FUNC + * @tc.require: issue + */ +HWTEST_F(ExtensionRecordManagerTest, UpdateProcessName_CallerInstance_0100, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "begin."); + auto extRecordMgr = std::make_shared(0); + ASSERT_NE(extRecordMgr, nullptr); + + // Create caller ability record and register with Token system + AAFwk::AbilityRequest callerAbilityRequest; + callerAbilityRequest.abilityInfo.bundleName = "com.test.caller"; + callerAbilityRequest.abilityInfo.name = "CallerAbility"; + auto callerAbilityRecord = AAFwk::BaseExtensionRecord::CreateBaseExtensionRecord(callerAbilityRequest); + ASSERT_NE(callerAbilityRecord, nullptr); + callerAbilityRecord->SetPid(5678); + sptr callerToken = callerAbilityRecord->GetToken(); + ASSERT_NE(callerToken, nullptr); + + // Create target extension record + AAFwk::AbilityRequest abilityRequest; + abilityRequest.abilityInfo.bundleName = "com.test.target"; + abilityRequest.abilityInfo.name = "TargetAbility"; + abilityRequest.callerToken = callerToken; + auto abilityRecord = AAFwk::BaseExtensionRecord::CreateBaseExtensionRecord(abilityRequest); + ASSERT_NE(abilityRecord, nullptr); + std::shared_ptr extRecord = std::make_shared(abilityRecord); + extRecord->processMode_ = PROCESS_MODE_CALLER_INSTANCE; + + std::string expectedProcess = std::string("com.test.target") + ":" + "TargetAbility" + ":5678"; + int32_t result = extRecordMgr->UpdateProcessName(abilityRequest, extRecord); + EXPECT_EQ(result, ERR_OK); + EXPECT_EQ(abilityRecord->GetProcessName(), expectedProcess); + + TAG_LOGI(AAFwkTag::TEST, "end."); +} + +/** + * @tc.name: UpdateProcessName_CallerInstance_0200 + * @tc.desc: Test UpdateProcessName with PROCESS_MODE_CALLER_INSTANCE, appIndex > 0. + * @tc.type: FUNC + * @tc.require: issue + */ +HWTEST_F(ExtensionRecordManagerTest, UpdateProcessName_CallerInstance_0200, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "begin."); + auto extRecordMgr = std::make_shared(0); + ASSERT_NE(extRecordMgr, nullptr); + + // Create caller ability record + AAFwk::AbilityRequest callerAbilityRequest; + callerAbilityRequest.abilityInfo.bundleName = "com.test.caller"; + callerAbilityRequest.abilityInfo.name = "CallerAbility"; + auto callerAbilityRecord = AAFwk::BaseExtensionRecord::CreateBaseExtensionRecord(callerAbilityRequest); + ASSERT_NE(callerAbilityRecord, nullptr); + callerAbilityRecord->SetPid(1234); + sptr callerToken = callerAbilityRecord->GetToken(); + ASSERT_NE(callerToken, nullptr); + + // Create target extension record with appIndex > 0 + AAFwk::AbilityRequest abilityRequest; + abilityRequest.abilityInfo.bundleName = "com.test.target"; + abilityRequest.abilityInfo.name = "TargetAbility"; + abilityRequest.callerToken = callerToken; + auto abilityRecord = AAFwk::BaseExtensionRecord::CreateBaseExtensionRecord(abilityRequest); + ASSERT_NE(abilityRecord, nullptr); + abilityRecord->SetAppIndex(2); + std::shared_ptr extRecord = std::make_shared(abilityRecord); + extRecord->processMode_ = PROCESS_MODE_CALLER_INSTANCE; + + // processAppIndex = abilityRecord->GetAppIndex() = 2, appendAppIndex adds ":2" + std::string expectedProcess = std::string("com.test.target") + ":" + "TargetAbility" + ":1234" + ":" + "2"; + int32_t result = extRecordMgr->UpdateProcessName(abilityRequest, extRecord); + EXPECT_EQ(result, ERR_OK); + EXPECT_EQ(abilityRecord->GetProcessName(), expectedProcess); + + TAG_LOGI(AAFwkTag::TEST, "end."); +} + +/** + * @tc.name: UpdateProcessName_AgentUI_0100 + * @tc.desc: Test UpdateProcessName with PROCESS_MODE_AGENT_UI. + * @tc.type: FUNC + * @tc.require: issue + */ +HWTEST_F(ExtensionRecordManagerTest, UpdateProcessName_AgentUI_0100, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "begin."); + auto extRecordMgr = std::make_shared(0); + ASSERT_NE(extRecordMgr, nullptr); + + AAFwk::AbilityRequest abilityRequest; + abilityRequest.abilityInfo.bundleName = "com.test.agent"; + abilityRequest.abilityInfo.name = "AgentAbility"; + auto abilityRecord = AAFwk::BaseExtensionRecord::CreateBaseExtensionRecord(abilityRequest); + ASSERT_NE(abilityRecord, nullptr); + std::shared_ptr extRecord = std::make_shared(abilityRecord); + extRecord->processMode_ = PROCESS_MODE_AGENT_UI; + + std::string expectedProcess = std::string("com.test.agent") + ":" + "agent"; + int32_t result = extRecordMgr->UpdateProcessName(abilityRequest, extRecord); + EXPECT_EQ(result, ERR_OK); + EXPECT_EQ(abilityRecord->GetProcessName(), expectedProcess); + + TAG_LOGI(AAFwkTag::TEST, "end."); +} + +/** + * @tc.name: UpdateProcessName_RunWithMainProcess_0200 + * @tc.desc: Test UpdateProcessName PROCESS_MODE_RUN_WITH_MAIN_PROCESS with appIndex > 0, no appInfo.process. + * @tc.type: FUNC + * @tc.require: issue + */ +HWTEST_F(ExtensionRecordManagerTest, UpdateProcessName_RunWithMainProcess_0200, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "begin."); + auto extRecordMgr = std::make_shared(0); + ASSERT_NE(extRecordMgr, nullptr); + + AAFwk::AbilityRequest abilityRequest; + abilityRequest.abilityInfo.bundleName = "com.test.mainprocess"; + abilityRequest.abilityInfo.name = "MainProcessAbility"; + auto abilityRecord = AAFwk::BaseExtensionRecord::CreateBaseExtensionRecord(abilityRequest); + ASSERT_NE(abilityRecord, nullptr); + abilityRecord->SetAppIndex(1); + std::shared_ptr extRecord = std::make_shared(abilityRecord); + extRecord->processMode_ = PROCESS_MODE_RUN_WITH_MAIN_PROCESS; + + std::string expectedProcess = std::string("com.test.mainprocess") + ":" + "1"; + int32_t result = extRecordMgr->UpdateProcessName(abilityRequest, extRecord); + EXPECT_EQ(result, ERR_OK); + EXPECT_EQ(abilityRecord->GetProcessName(), expectedProcess); + + TAG_LOGI(AAFwkTag::TEST, "end."); +} + +/** + * @tc.name: UpdateProcessName_RunWithMainProcess_0300 + * @tc.desc: Test UpdateProcessName PROCESS_MODE_RUN_WITH_MAIN_PROCESS with appInfo.process and appIndex > 0. + * @tc.type: FUNC + * @tc.require: issue + */ +HWTEST_F(ExtensionRecordManagerTest, UpdateProcessName_RunWithMainProcess_0300, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "begin."); + auto extRecordMgr = std::make_shared(0); + ASSERT_NE(extRecordMgr, nullptr); + + AAFwk::AbilityRequest abilityRequest; + abilityRequest.abilityInfo.bundleName = "com.test.mainprocess"; + abilityRequest.abilityInfo.name = "MainProcessAbility"; + abilityRequest.appInfo.process = "custom_process"; + auto abilityRecord = AAFwk::BaseExtensionRecord::CreateBaseExtensionRecord(abilityRequest); + ASSERT_NE(abilityRecord, nullptr); + abilityRecord->SetAppIndex(3); + std::shared_ptr extRecord = std::make_shared(abilityRecord); + extRecord->processMode_ = PROCESS_MODE_RUN_WITH_MAIN_PROCESS; + + std::string expectedProcess = std::string("custom_process") + ":" + "3"; + int32_t result = extRecordMgr->UpdateProcessName(abilityRequest, extRecord); + EXPECT_EQ(result, ERR_OK); + EXPECT_EQ(abilityRecord->GetProcessName(), expectedProcess); + + TAG_LOGI(AAFwkTag::TEST, "end."); +} + +/** + * @tc.name: UpdateProcessName_Default_AppIndex_0100 + * @tc.desc: Test UpdateProcessName default case with empty moduleProcess and appIndex > 0. + * @tc.type: FUNC + * @tc.require: issue + */ +HWTEST_F(ExtensionRecordManagerTest, UpdateProcessName_Default_AppIndex_0100, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "begin."); + auto extRecordMgr = std::make_shared(0); + ASSERT_NE(extRecordMgr, nullptr); + + AAFwk::AbilityRequest abilityRequest; + abilityRequest.abilityInfo.bundleName = "com.test.default"; + abilityRequest.abilityInfo.name = "DefaultAbility"; + abilityRequest.abilityInfo.extensionTypeName = "UIExtension"; + abilityRequest.moduleProcess = ""; + auto abilityRecord = AAFwk::BaseExtensionRecord::CreateBaseExtensionRecord(abilityRequest); + ASSERT_NE(abilityRecord, nullptr); + abilityRecord->SetAppIndex(1); + std::shared_ptr extRecord = std::make_shared(abilityRecord); + extRecord->processMode_ = 0; + + // default branch: moduleProcess empty, appIndex > 0 + std::string expectedProcess = std::string("com.test.default") + ":" + "UIExtension" + ":" + "1"; + int32_t result = extRecordMgr->UpdateProcessName(abilityRequest, extRecord); + EXPECT_EQ(result, ERR_OK); + EXPECT_EQ(abilityRecord->GetProcessName(), expectedProcess); + + TAG_LOGI(AAFwkTag::TEST, "end."); +} + +/** + * @tc.name: UpdateProcessName_Instance_AppIndex_0100 + * @tc.desc: Test UpdateProcessName PROCESS_MODE_INSTANCE with appIndex > 0 (appendAppIndex triggers). + * @tc.type: FUNC + * @tc.require: issue + */ +HWTEST_F(ExtensionRecordManagerTest, UpdateProcessName_Instance_AppIndex_0100, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "begin."); + auto extRecordMgr = std::make_shared(0); + ASSERT_NE(extRecordMgr, nullptr); + + AAFwk::AbilityRequest abilityRequest; + abilityRequest.abilityInfo.bundleName = "com.test.instance"; + abilityRequest.abilityInfo.name = "InstanceAbility"; + auto abilityRecord = AAFwk::BaseExtensionRecord::CreateBaseExtensionRecord(abilityRequest); + ASSERT_NE(abilityRecord, nullptr); + abilityRecord->SetAppIndex(2); + std::shared_ptr extRecord = std::make_shared(abilityRecord); + extRecord->processMode_ = PROCESS_MODE_INSTANCE; + + auto extAbilityId = abilityRecord->GetUIExtensionAbilityId(); + std::string expectedProcess = std::string("com.test.instance") + ":" + "InstanceAbility" + + ":" + std::to_string(extAbilityId) + ":" + "2"; + int32_t result = extRecordMgr->UpdateProcessName(abilityRequest, extRecord); + EXPECT_EQ(result, ERR_OK); + EXPECT_EQ(abilityRecord->GetProcessName(), expectedProcess); + + TAG_LOGI(AAFwkTag::TEST, "end."); +} + +/** + * @tc.name: UpdateProcessName_NullAbilityRecord_0100 + * @tc.desc: Test UpdateProcessName with null abilityRecord inside extension record. + * @tc.type: FUNC + * @tc.require: issue + */ +HWTEST_F(ExtensionRecordManagerTest, UpdateProcessName_NullAbilityRecord_0100, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "begin."); + auto extRecordMgr = std::make_shared(0); + ASSERT_NE(extRecordMgr, nullptr); + + AAFwk::AbilityRequest abilityRequest; + abilityRequest.abilityInfo.bundleName = "com.test.null"; + abilityRequest.abilityInfo.name = "NullAbility"; + std::shared_ptr extRecord = std::make_shared(nullptr); + ASSERT_NE(extRecord, nullptr); + extRecord->abilityRecord_ = nullptr; + + int32_t result = extRecordMgr->UpdateProcessName(abilityRequest, extRecord); + EXPECT_EQ(result, ERR_INVALID_VALUE); + + TAG_LOGI(AAFwkTag::TEST, "end."); +} + +/** + * @tc.name: UpdateProcessName_CallerInstance_NullCaller_0100 + * @tc.desc: Test UpdateProcessName PROCESS_MODE_CALLER_INSTANCE with null callerRecord. + * @tc.type: FUNC + * @tc.require: issue + */ +HWTEST_F(ExtensionRecordManagerTest, UpdateProcessName_CallerInstance_NullCaller_0100, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "begin."); + auto extRecordMgr = std::make_shared(0); + ASSERT_NE(extRecordMgr, nullptr); + + AAFwk::AbilityRequest abilityRequest; + abilityRequest.abilityInfo.bundleName = "com.test.target"; + abilityRequest.abilityInfo.name = "TargetAbility"; + // callerToken is null by default, so callerRecord will be null + auto abilityRecord = AAFwk::BaseExtensionRecord::CreateBaseExtensionRecord(abilityRequest); + ASSERT_NE(abilityRecord, nullptr); + std::shared_ptr extRecord = std::make_shared(abilityRecord); + extRecord->processMode_ = PROCESS_MODE_CALLER_INSTANCE; + + int32_t result = extRecordMgr->UpdateProcessName(abilityRequest, extRecord); + EXPECT_EQ(result, ERR_INVALID_VALUE); + + TAG_LOGI(AAFwkTag::TEST, "end."); +} } // namespace AbilityRuntime } // namespace OHOS diff --git a/test/unittest/ui_extension/ui_extension_test/BUILD.gn b/test/unittest/ui_extension/ui_extension_test/BUILD.gn new file mode 100644 index 0000000000..f53f9a05b1 --- /dev/null +++ b/test/unittest/ui_extension/ui_extension_test/BUILD.gn @@ -0,0 +1,74 @@ +# Copyright (c) 2025 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_out_path = "ability_runtime/ability_runtime/ui_extension" + +ohos_unittest("ui_extension_test") { + module_out_path = module_out_path + + include_dirs = [ + "${ability_runtime_test_path}/mock/frameworks_kits_ability_native_test/include", + "${ability_runtime_path}/interfaces/kits/native/ability/native/ui_extension_ability", + "${ability_runtime_path}/interfaces/kits/native/ability/native/ui_extension_base", + ] + + sources = [ "ui_extension_test.cpp" ] + + deps = [ + "${ability_runtime_innerkits_path}/ability_manager:ability_manager", + "${ability_runtime_innerkits_path}/ability_manager:ability_start_options", + "${ability_runtime_innerkits_path}/runtime:runtime", + "${ability_runtime_native_path}/ability:ability_context_native", + "${ability_runtime_native_path}/ability/native:abilitykit_native", + "${ability_runtime_native_path}/ability/native:abilitykit_utils", + "${ability_runtime_native_path}/ability/native:extensionkit_native", + "${ability_runtime_native_path}/ability/native:ui_extension", + "${ability_runtime_native_path}/appkit:app_context", + "${ability_runtime_native_path}/appkit:appkit_native", + ] + + external_deps = [ + "ability_base:base", + "ability_base:extractortool", + "ability_base:session_info", + "ability_base:want", + "bundle_framework:appexecfwk_base", + "c_utils:utils", + "eventhandler:libeventhandler", + "googletest:gmock_main", + "googletest:gtest_main", + "hilog:libhilog", + "init:libbegetutil", + "ipc:ipc_core", + "ipc:ipc_napi", + "napi:ace_napi", + "samgr:samgr_proxy", + ] + + if (ability_runtime_graphics) { + external_deps += [ + "form_fwk:fmskit_native", + "i18n:intl_util", + "window_manager:libwsutils", + "window_manager:scene_session", + ] + } +} + +group("unittest") { + testonly = true + deps = [ ":ui_extension_test" ] +} diff --git a/test/unittest/ui_extension/ui_extension_test/ui_extension_test.cpp b/test/unittest/ui_extension/ui_extension_test/ui_extension_test.cpp new file mode 100644 index 0000000000..d8ad896fa6 --- /dev/null +++ b/test/unittest/ui_extension/ui_extension_test/ui_extension_test.cpp @@ -0,0 +1,756 @@ +/* + * 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 + +#include "ability_handler.h" +#include "application_context.h" +#include "context_impl.h" +#include "mock_ability_token.h" +#include "mock_window.h" +#include "ohos_application.h" +#include "runtime.h" +#include "session_info.h" +#include "ui_extension.h" +#include "ui_extension_context.h" +#include "ui_extension_window_command.h" + +namespace OHOS { +namespace AbilityRuntime { +using namespace testing::ext; +using namespace AAFwk; +using namespace AppExecFwk; +namespace { +const uint64_t TEST_COMPONENT_ID = 100; +const uint64_t TEST_COMPONENT_ID_2 = 200; +} + +class TestUIExtension : public UIExtension { +public: + using UIExtension::HandleSessionCreate; + using UIExtension::ForegroundWindow; + using UIExtension::BackgroundWindow; + using UIExtension::DestroyWindow; + using UIExtension::ForegroundWindowWithInsightIntent; + using UIExtension::ConfigurationUpdated; + using UIExtension::uiWindowMap_; + using UIExtension::foregroundWindows_; +}; + +class UIExtensionTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +public: + TestUIExtension* extension_ = nullptr; +}; + +void UIExtensionTest::SetUpTestCase(void) {} + +void UIExtensionTest::TearDownTestCase(void) {} + +void UIExtensionTest::SetUp() +{ + extension_ = new TestUIExtension(); +} + +void UIExtensionTest::TearDown() +{ + if (extension_ != nullptr) { + delete extension_; + extension_ = nullptr; + } +} + +/** + * @tc.number: UIExtensionTest_Create_0100 + * @tc.name: Create + * @tc.desc: Create with null runtime returns UIExtension instance. + */ +HWTEST_F(UIExtensionTest, UIExtensionTest_Create_0100, Function | MediumTest | Level1) +{ + EXPECT_NE(extension_, nullptr); + auto* result = UIExtension::Create(nullptr); + EXPECT_NE(result, nullptr); + delete result; +} + +/** + * @tc.number: UIExtensionTest_Create_0200 + * @tc.name: Create + * @tc.desc: Create with JS runtime returns JsUIExtension instance. + */ +HWTEST_F(UIExtensionTest, UIExtensionTest_Create_0200, Function | MediumTest | Level1) +{ + Runtime::Options options; + auto runtime = Runtime::Create(options); + auto* result = UIExtension::Create(runtime); + EXPECT_NE(result, nullptr); + delete result; +} + +/** + * @tc.number: UIExtensionTest_HandleSessionCreate_0100 + * @tc.name: HandleSessionCreate + * @tc.desc: HandleSessionCreate always returns true. + */ +HWTEST_F(UIExtensionTest, UIExtensionTest_HandleSessionCreate_0100, Function | MediumTest | Level1) +{ + AAFwk::Want want; + sptr sessionInfo = new AAFwk::SessionInfo(); + sessionInfo->uiExtensionComponentId = TEST_COMPONENT_ID; + auto result = extension_->HandleSessionCreate(want, sessionInfo); + EXPECT_EQ(result, true); +} + +/** + * @tc.number: UIExtensionTest_HandleSessionCreate_0200 + * @tc.name: HandleSessionCreate + * @tc.desc: HandleSessionCreate returns true with null sessionInfo. + */ +HWTEST_F(UIExtensionTest, UIExtensionTest_HandleSessionCreate_0200, Function | MediumTest | Level1) +{ + AAFwk::Want want; + auto result = extension_->HandleSessionCreate(want, nullptr); + EXPECT_EQ(result, true); +} + +/** + * @tc.number: UIExtensionTest_ForegroundWindowWithInsightIntent_0100 + * @tc.name: ForegroundWindowWithInsightIntent + * @tc.desc: ForegroundWindowWithInsightIntent always returns true. + */ +HWTEST_F(UIExtensionTest, UIExtensionTest_ForegroundWindowWithInsightIntent_0100, Function | MediumTest | Level1) +{ + AAFwk::Want want; + sptr sessionInfo = new AAFwk::SessionInfo(); + auto result = extension_->ForegroundWindowWithInsightIntent(want, sessionInfo, true); + EXPECT_EQ(result, true); +} + +/** + * @tc.number: UIExtensionTest_DestroyWindow_0100 + * @tc.name: DestroyWindow + * @tc.desc: DestroyWindow with valid sessionInfo does not crash. + */ +HWTEST_F(UIExtensionTest, UIExtensionTest_DestroyWindow_0100, Function | MediumTest | Level1) +{ + sptr sessionInfo = new AAFwk::SessionInfo(); + sessionInfo->uiExtensionComponentId = TEST_COMPONENT_ID; + extension_->DestroyWindow(sessionInfo); + EXPECT_EQ(extension_->uiWindowMap_.size(), 0u); +} + +/** + * @tc.number: UIExtensionTest_DestroyWindow_0200 + * @tc.name: DestroyWindow + * @tc.desc: DestroyWindow with null sessionInfo does not crash. + */ +HWTEST_F(UIExtensionTest, UIExtensionTest_DestroyWindow_0200, Function | MediumTest | Level1) +{ + extension_->DestroyWindow(nullptr); + EXPECT_EQ(extension_->uiWindowMap_.size(), 0u); +} + +/** + * @tc.number: UIExtensionTest_ConfigurationUpdated_0100 + * @tc.name: ConfigurationUpdated + * @tc.desc: ConfigurationUpdated is empty, verify no crash. + */ +HWTEST_F(UIExtensionTest, UIExtensionTest_ConfigurationUpdated_0100, Function | MediumTest | Level1) +{ + extension_->foregroundWindows_.clear(); + extension_->ConfigurationUpdated(); + EXPECT_EQ(extension_->foregroundWindows_.size(), 0u); +} + +/** + * @tc.number: UIExtensionTest_BackgroundWindow_0100 + * @tc.name: BackgroundWindow + * @tc.desc: BackgroundWindow with null sessionInfo does not crash. + */ +HWTEST_F(UIExtensionTest, UIExtensionTest_BackgroundWindow_0100, Function | MediumTest | Level1) +{ + extension_->BackgroundWindow(nullptr); + EXPECT_EQ(extension_->foregroundWindows_.size(), 0u); +} + +/** + * @tc.number: UIExtensionTest_BackgroundWindow_0200 + * @tc.name: BackgroundWindow + * @tc.desc: BackgroundWindow with sessionInfo not in window map returns early. + */ +HWTEST_F(UIExtensionTest, UIExtensionTest_BackgroundWindow_0200, Function | MediumTest | Level1) +{ + sptr sessionInfo = new AAFwk::SessionInfo(); + sessionInfo->uiExtensionComponentId = TEST_COMPONENT_ID; + extension_->uiWindowMap_.clear(); + extension_->BackgroundWindow(sessionInfo); + EXPECT_EQ(extension_->foregroundWindows_.size(), 0u); +} + +/** + * @tc.number: UIExtensionTest_BackgroundWindow_0300 + * @tc.name: BackgroundWindow + * @tc.desc: BackgroundWindow with existing window removes from foreground set and calls Hide. + */ +HWTEST_F(UIExtensionTest, UIExtensionTest_BackgroundWindow_0300, Function | MediumTest | Level1) +{ + sptr sessionInfo = new AAFwk::SessionInfo(); + sessionInfo->uiExtensionComponentId = TEST_COMPONENT_ID; + auto mockWindow = new Rosen::MockWindow(); + extension_->uiWindowMap_[TEST_COMPONENT_ID] = mockWindow; + extension_->foregroundWindows_.emplace(TEST_COMPONENT_ID); + EXPECT_EQ(extension_->foregroundWindows_.count(TEST_COMPONENT_ID), 1u); + + extension_->BackgroundWindow(sessionInfo); + EXPECT_EQ(extension_->foregroundWindows_.count(TEST_COMPONENT_ID), 0u); + EXPECT_EQ(extension_->uiWindowMap_.count(TEST_COMPONENT_ID), 1u); + extension_->uiWindowMap_.clear(); +} + +/** + * @tc.number: UIExtensionTest_BackgroundWindow_0400 + * @tc.name: BackgroundWindow + * @tc.desc: BackgroundWindow with nullptr window in map does not remove from foreground. + */ +HWTEST_F(UIExtensionTest, UIExtensionTest_BackgroundWindow_0400, Function | MediumTest | Level1) +{ + sptr sessionInfo = new AAFwk::SessionInfo(); + sessionInfo->uiExtensionComponentId = TEST_COMPONENT_ID; + extension_->uiWindowMap_[TEST_COMPONENT_ID] = nullptr; + extension_->foregroundWindows_.emplace(TEST_COMPONENT_ID); + EXPECT_EQ(extension_->foregroundWindows_.count(TEST_COMPONENT_ID), 1u); + + extension_->BackgroundWindow(sessionInfo); + EXPECT_EQ(extension_->foregroundWindows_.count(TEST_COMPONENT_ID), 1u); + extension_->uiWindowMap_.clear(); + extension_->foregroundWindows_.clear(); +} + +/** + * @tc.number: UIExtensionTest_ForegroundWindow_0100 + * @tc.name: ForegroundWindow + * @tc.desc: ForegroundWindow with no window in map does not add to foreground. + */ +HWTEST_F(UIExtensionTest, UIExtensionTest_ForegroundWindow_0100, Function | MediumTest | Level1) +{ + AAFwk::Want want; + sptr sessionInfo = new AAFwk::SessionInfo(); + sessionInfo->uiExtensionComponentId = TEST_COMPONENT_ID; + extension_->uiWindowMap_.clear(); + extension_->ForegroundWindow(want, sessionInfo); + EXPECT_EQ(extension_->foregroundWindows_.count(TEST_COMPONENT_ID), 0u); +} + +/** + * @tc.number: UIExtensionTest_ForegroundWindow_0200 + * @tc.name: ForegroundWindow + * @tc.desc: ForegroundWindow with existing window calls Show and adds to foreground. + */ +HWTEST_F(UIExtensionTest, UIExtensionTest_ForegroundWindow_0200, Function | MediumTest | Level1) +{ + AAFwk::Want want; + sptr sessionInfo = new AAFwk::SessionInfo(); + sessionInfo->uiExtensionComponentId = TEST_COMPONENT_ID; + auto mockWindow = new Rosen::MockWindow(); + extension_->uiWindowMap_[TEST_COMPONENT_ID] = mockWindow; + EXPECT_EQ(extension_->foregroundWindows_.count(TEST_COMPONENT_ID), 0u); + + extension_->ForegroundWindow(want, sessionInfo); + EXPECT_EQ(extension_->foregroundWindows_.count(TEST_COMPONENT_ID), 1u); + extension_->uiWindowMap_.clear(); + extension_->foregroundWindows_.clear(); +} + +/** + * @tc.number: UIExtensionTest_ForegroundWindow_0300 + * @tc.name: ForegroundWindow + * @tc.desc: ForegroundWindow with nullptr window in map does not add to foreground. + */ +HWTEST_F(UIExtensionTest, UIExtensionTest_ForegroundWindow_0300, Function | MediumTest | Level1) +{ + AAFwk::Want want; + sptr sessionInfo = new AAFwk::SessionInfo(); + sessionInfo->uiExtensionComponentId = TEST_COMPONENT_ID; + extension_->uiWindowMap_[TEST_COMPONENT_ID] = nullptr; + + extension_->ForegroundWindow(want, sessionInfo); + EXPECT_EQ(extension_->foregroundWindows_.count(TEST_COMPONENT_ID), 0u); + extension_->uiWindowMap_.clear(); +} + +/** + * @tc.number: UIExtensionTest_OnCommandWindow_0100 + * @tc.name: OnCommandWindow + * @tc.desc: OnCommandWindow with null sessionInfo returns early without crash. + */ +HWTEST_F(UIExtensionTest, UIExtensionTest_OnCommandWindow_0100, Function | MediumTest | Level1) +{ + AAFwk::Want want; + extension_->uiWindowMap_.clear(); + extension_->foregroundWindows_.clear(); + extension_->OnCommandWindow(want, nullptr, AAFwk::WIN_CMD_FOREGROUND); + EXPECT_EQ(extension_->uiWindowMap_.size(), 0u); +} + +/** + * @tc.number: UIExtensionTest_OnCommandWindow_0200 + * @tc.name: OnCommandWindow + * @tc.desc: OnCommandWindow with WIN_CMD_FOREGROUND dispatches to ForegroundWindow. + */ +HWTEST_F(UIExtensionTest, UIExtensionTest_OnCommandWindow_0200, Function | MediumTest | Level1) +{ + AAFwk::Want want; + sptr sessionInfo = new AAFwk::SessionInfo(); + sessionInfo->uiExtensionComponentId = TEST_COMPONENT_ID; + auto mockWindow = new Rosen::MockWindow(); + extension_->uiWindowMap_[TEST_COMPONENT_ID] = mockWindow; + + extension_->OnCommandWindow(want, sessionInfo, AAFwk::WIN_CMD_FOREGROUND); + EXPECT_EQ(extension_->foregroundWindows_.count(TEST_COMPONENT_ID), 1u); + extension_->uiWindowMap_.clear(); + extension_->foregroundWindows_.clear(); +} + +/** + * @tc.number: UIExtensionTest_OnCommandWindow_0300 + * @tc.name: OnCommandWindow + * @tc.desc: OnCommandWindow with WIN_CMD_BACKGROUND dispatches to BackgroundWindow. + */ +HWTEST_F(UIExtensionTest, UIExtensionTest_OnCommandWindow_0300, Function | MediumTest | Level1) +{ + AAFwk::Want want; + sptr sessionInfo = new AAFwk::SessionInfo(); + sessionInfo->uiExtensionComponentId = TEST_COMPONENT_ID; + auto mockWindow = new Rosen::MockWindow(); + extension_->uiWindowMap_[TEST_COMPONENT_ID] = mockWindow; + extension_->foregroundWindows_.emplace(TEST_COMPONENT_ID); + + extension_->OnCommandWindow(want, sessionInfo, AAFwk::WIN_CMD_BACKGROUND); + EXPECT_EQ(extension_->foregroundWindows_.count(TEST_COMPONENT_ID), 0u); + extension_->uiWindowMap_.clear(); +} + +/** + * @tc.number: UIExtensionTest_OnCommandWindow_0400 + * @tc.name: OnCommandWindow + * @tc.desc: OnCommandWindow with WIN_CMD_DESTROY dispatches to DestroyWindow. + */ +HWTEST_F(UIExtensionTest, UIExtensionTest_OnCommandWindow_0400, Function | MediumTest | Level1) +{ + AAFwk::Want want; + sptr sessionInfo = new AAFwk::SessionInfo(); + sessionInfo->uiExtensionComponentId = TEST_COMPONENT_ID; + + extension_->OnCommandWindow(want, sessionInfo, AAFwk::WIN_CMD_DESTROY); + EXPECT_EQ(extension_->uiWindowMap_.size(), 0u); +} + +/** + * @tc.number: UIExtensionTest_OnCommandWindowDone_0100 + * @tc.name: OnCommandWindowDone + * @tc.desc: OnCommandWindowDone with null context returns early without crash. + */ +HWTEST_F(UIExtensionTest, UIExtensionTest_OnCommandWindowDone_0100, Function | MediumTest | Level1) +{ + sptr sessionInfo = new AAFwk::SessionInfo(); + sessionInfo->uiExtensionComponentId = TEST_COMPONENT_ID; + extension_->uiWindowMap_.clear(); + extension_->foregroundWindows_.clear(); + extension_->OnCommandWindowDone(sessionInfo, AAFwk::WIN_CMD_FOREGROUND); + EXPECT_EQ(extension_->uiWindowMap_.size(), 0u); + EXPECT_EQ(extension_->foregroundWindows_.size(), 0u); +} + +/** + * @tc.number: UIExtensionTest_OnCommandWindowDone_0200 + * @tc.name: OnCommandWindowDone + * @tc.desc: OnCommandWindowDone with empty window map determines ABILITY_CMD_DESTROY. + */ +HWTEST_F(UIExtensionTest, UIExtensionTest_OnCommandWindowDone_0200, Function | MediumTest | Level1) +{ + auto abilityInfo = std::make_shared(); + sptr token = new AppExecFwk::MockAbilityToken(); + auto record = std::make_shared(abilityInfo, token, nullptr, 0); + auto application = std::make_shared(); + auto contextImpl = std::make_shared(); + auto applicationContext = ApplicationContext::GetInstance(); + applicationContext->AttachContextImpl(contextImpl); + application->SetApplicationContext(applicationContext); + auto handler = std::make_shared(nullptr); + extension_->Init(record, application, handler, token); + + extension_->uiWindowMap_.clear(); + extension_->foregroundWindows_.clear(); + + sptr sessionInfo = new AAFwk::SessionInfo(); + sessionInfo->uiExtensionComponentId = TEST_COMPONENT_ID; + extension_->OnCommandWindowDone(sessionInfo, AAFwk::WIN_CMD_DESTROY); + EXPECT_EQ(extension_->uiWindowMap_.empty(), true); + EXPECT_EQ(extension_->foregroundWindows_.empty(), true); +} + +/** + * @tc.number: UIExtensionTest_OnCommandWindowDone_0300 + * @tc.name: OnCommandWindowDone + * @tc.desc: OnCommandWindowDone with windows but no foreground determines ABILITY_CMD_BACKGROUND. + */ +HWTEST_F(UIExtensionTest, UIExtensionTest_OnCommandWindowDone_0300, Function | MediumTest | Level1) +{ + auto abilityInfo = std::make_shared(); + sptr token = new AppExecFwk::MockAbilityToken(); + auto record = std::make_shared(abilityInfo, token, nullptr, 0); + auto application = std::make_shared(); + auto contextImpl = std::make_shared(); + auto applicationContext = ApplicationContext::GetInstance(); + applicationContext->AttachContextImpl(contextImpl); + application->SetApplicationContext(applicationContext); + auto handler = std::make_shared(nullptr); + extension_->Init(record, application, handler, token); + + auto mockWindow = new Rosen::MockWindow(); + extension_->uiWindowMap_[TEST_COMPONENT_ID] = mockWindow; + extension_->foregroundWindows_.clear(); + + sptr sessionInfo = new AAFwk::SessionInfo(); + sessionInfo->uiExtensionComponentId = TEST_COMPONENT_ID; + extension_->OnCommandWindowDone(sessionInfo, AAFwk::WIN_CMD_BACKGROUND); + EXPECT_EQ(extension_->uiWindowMap_.empty(), false); + EXPECT_EQ(extension_->foregroundWindows_.empty(), true); + extension_->uiWindowMap_.clear(); +} + +/** + * @tc.number: UIExtensionTest_OnCommandWindowDone_0400 + * @tc.name: OnCommandWindowDone + * @tc.desc: OnCommandWindowDone with foreground windows determines ABILITY_CMD_FOREGROUND. + */ +HWTEST_F(UIExtensionTest, UIExtensionTest_OnCommandWindowDone_0400, Function | MediumTest | Level1) +{ + auto abilityInfo = std::make_shared(); + sptr token = new AppExecFwk::MockAbilityToken(); + auto record = std::make_shared(abilityInfo, token, nullptr, 0); + auto application = std::make_shared(); + auto contextImpl = std::make_shared(); + auto applicationContext = ApplicationContext::GetInstance(); + applicationContext->AttachContextImpl(contextImpl); + application->SetApplicationContext(applicationContext); + auto handler = std::make_shared(nullptr); + extension_->Init(record, application, handler, token); + + auto mockWindow = new Rosen::MockWindow(); + extension_->uiWindowMap_[TEST_COMPONENT_ID] = mockWindow; + extension_->foregroundWindows_.emplace(TEST_COMPONENT_ID); + + sptr sessionInfo = new AAFwk::SessionInfo(); + sessionInfo->uiExtensionComponentId = TEST_COMPONENT_ID; + extension_->OnCommandWindowDone(sessionInfo, AAFwk::WIN_CMD_FOREGROUND); + EXPECT_EQ(extension_->foregroundWindows_.count(TEST_COMPONENT_ID), 1u); + extension_->uiWindowMap_.clear(); + extension_->foregroundWindows_.clear(); +} + +/** + * @tc.number: UIExtensionTest_OnInsightIntentExecuteDone_0100 + * @tc.name: OnInsightIntentExecuteDone + * @tc.desc: OnInsightIntentExecuteDone with null sessionInfo does not crash. + */ +HWTEST_F(UIExtensionTest, UIExtensionTest_OnInsightIntentExecuteDone_0100, Function | MediumTest | Level1) +{ + AppExecFwk::InsightIntentExecuteResult result; + extension_->uiWindowMap_.clear(); + extension_->foregroundWindows_.clear(); + extension_->OnInsightIntentExecuteDone(nullptr, result); + EXPECT_EQ(extension_->uiWindowMap_.size(), 0u); +} + +/** + * @tc.number: UIExtensionTest_OnInsightIntentExecuteDone_0200 + * @tc.name: OnInsightIntentExecuteDone + * @tc.desc: OnInsightIntentExecuteDone with window in map adds to foreground and calls Show. + */ +HWTEST_F(UIExtensionTest, UIExtensionTest_OnInsightIntentExecuteDone_0200, Function | MediumTest | Level1) +{ + sptr sessionInfo = new AAFwk::SessionInfo(); + sessionInfo->uiExtensionComponentId = TEST_COMPONENT_ID; + auto mockWindow = new Rosen::MockWindow(); + extension_->uiWindowMap_[TEST_COMPONENT_ID] = mockWindow; + extension_->foregroundWindows_.clear(); + + AppExecFwk::InsightIntentExecuteResult result; + result.isNeedDelayResult = false; + extension_->OnInsightIntentExecuteDone(sessionInfo, result); + EXPECT_EQ(extension_->foregroundWindows_.count(TEST_COMPONENT_ID), 1u); + extension_->uiWindowMap_.clear(); + extension_->foregroundWindows_.clear(); +} + +/** + * @tc.number: UIExtensionTest_OnInsightIntentExecuteDone_0300 + * @tc.name: OnInsightIntentExecuteDone + * @tc.desc: OnInsightIntentExecuteDone with no window in map does not add to foreground. + */ +HWTEST_F(UIExtensionTest, UIExtensionTest_OnInsightIntentExecuteDone_0300, Function | MediumTest | Level1) +{ + sptr sessionInfo = new AAFwk::SessionInfo(); + sessionInfo->uiExtensionComponentId = TEST_COMPONENT_ID; + extension_->uiWindowMap_.clear(); + extension_->foregroundWindows_.clear(); + + AppExecFwk::InsightIntentExecuteResult result; + extension_->OnInsightIntentExecuteDone(sessionInfo, result); + EXPECT_EQ(extension_->foregroundWindows_.count(TEST_COMPONENT_ID), 0u); +} + +/** + * @tc.number: UIExtensionTest_OnInsightIntentExecuteDone_0400 + * @tc.name: OnInsightIntentExecuteDone + * @tc.desc: OnInsightIntentExecuteDone with nullptr window in map does not add to foreground. + */ +HWTEST_F(UIExtensionTest, UIExtensionTest_OnInsightIntentExecuteDone_0400, Function | MediumTest | Level1) +{ + sptr sessionInfo = new AAFwk::SessionInfo(); + sessionInfo->uiExtensionComponentId = TEST_COMPONENT_ID; + extension_->uiWindowMap_[TEST_COMPONENT_ID] = nullptr; + extension_->foregroundWindows_.clear(); + + AppExecFwk::InsightIntentExecuteResult result; + extension_->OnInsightIntentExecuteDone(sessionInfo, result); + EXPECT_EQ(extension_->foregroundWindows_.count(TEST_COMPONENT_ID), 0u); + extension_->uiWindowMap_.clear(); +} + +/** + * @tc.number: UIExtensionTest_RegisterUiExtensionDelayResultCallback_0100 + * @tc.name: RegisterUiExtensionDelayResultCallback + * @tc.desc: RegisterUiExtensionDelayResultCallback with no window in map does not crash. + */ +HWTEST_F(UIExtensionTest, UIExtensionTest_RegisterUiExtensionDelayResultCallback_0100, Function | MediumTest | Level1) +{ + sptr sessionInfo = new AAFwk::SessionInfo(); + sessionInfo->uiExtensionComponentId = TEST_COMPONENT_ID; + extension_->uiWindowMap_.clear(); + extension_->RegisterUiExtensionDelayResultCallback(1, sessionInfo); + EXPECT_EQ(extension_->uiWindowMap_.size(), 0u); +} + +/** + * @tc.number: UIExtensionTest_RegisterUiExtensionDelayResultCallback_0200 + * @tc.name: RegisterUiExtensionDelayResultCallback + * @tc.desc: RegisterUiExtensionDelayResultCallback with window in map registers callback. + */ +HWTEST_F(UIExtensionTest, UIExtensionTest_RegisterUiExtensionDelayResultCallback_0200, Function | MediumTest | Level1) +{ + sptr sessionInfo = new AAFwk::SessionInfo(); + sessionInfo->uiExtensionComponentId = TEST_COMPONENT_ID; + auto mockWindow = new Rosen::MockWindow(); + extension_->uiWindowMap_[TEST_COMPONENT_ID] = mockWindow; + EXPECT_EQ(extension_->foregroundWindows_.count(TEST_COMPONENT_ID), 0u); + + extension_->RegisterUiExtensionDelayResultCallback(1, sessionInfo, false); + EXPECT_EQ(extension_->uiWindowMap_.count(TEST_COMPONENT_ID), 1u); + extension_->uiWindowMap_.clear(); +} + +/** + * @tc.number: UIExtensionTest_RegisterUiExtensionDelayResultCallback_0300 + * @tc.name: RegisterUiExtensionDelayResultCallback + * @tc.desc: RegisterUiExtensionDelayResultCallback with nullptr window in map does not register. + */ +HWTEST_F(UIExtensionTest, UIExtensionTest_RegisterUiExtensionDelayResultCallback_0300, Function | MediumTest | Level1) +{ + sptr sessionInfo = new AAFwk::SessionInfo(); + sessionInfo->uiExtensionComponentId = TEST_COMPONENT_ID; + extension_->uiWindowMap_[TEST_COMPONENT_ID] = nullptr; + + extension_->RegisterUiExtensionDelayResultCallback(1, sessionInfo); + EXPECT_EQ(extension_->uiWindowMap_.count(TEST_COMPONENT_ID), 1u); + extension_->uiWindowMap_.clear(); +} + +/** + * @tc.number: UIExtensionTest_OnStopCallBack_0100 + * @tc.name: OnStopCallBack + * @tc.desc: OnStopCallBack with null context does not crash. + */ +HWTEST_F(UIExtensionTest, UIExtensionTest_OnStopCallBack_0100, Function | MediumTest | Level1) +{ + extension_->uiWindowMap_.clear(); + extension_->foregroundWindows_.clear(); + extension_->OnStopCallBack(); + EXPECT_EQ(extension_->uiWindowMap_.size(), 0u); + EXPECT_EQ(extension_->foregroundWindows_.size(), 0u); +} + +/** + * @tc.number: UIExtensionTest_OnCommand_0100 + * @tc.name: OnCommand + * @tc.desc: OnCommand with valid parameters does not crash and does not affect window state. + */ +HWTEST_F(UIExtensionTest, UIExtensionTest_OnCommand_0100, Function | MediumTest | Level1) +{ + extension_->uiWindowMap_.clear(); + extension_->foregroundWindows_.clear(); + AAFwk::Want want; + extension_->OnCommand(want, false, 1); + EXPECT_EQ(extension_->uiWindowMap_.size(), 0u); + EXPECT_EQ(extension_->foregroundWindows_.size(), 0u); +} + +/** + * @tc.number: UIExtensionTest_OnCommand_0200 + * @tc.name: OnCommand + * @tc.desc: OnCommand with restart=true does not crash. + */ +HWTEST_F(UIExtensionTest, UIExtensionTest_OnCommand_0200, Function | MediumTest | Level1) +{ + extension_->uiWindowMap_.clear(); + extension_->foregroundWindows_.clear(); + AAFwk::Want want; + want.SetElementName("com.test", "TestAbility"); + extension_->OnCommand(want, true, 2); + EXPECT_EQ(extension_->uiWindowMap_.size(), 0u); +} + +/** + * @tc.number: UIExtensionTest_Init_0100 + * @tc.name: Init + * @tc.desc: Init with valid parameters succeeds. + */ +HWTEST_F(UIExtensionTest, UIExtensionTest_Init_0100, Function | MediumTest | Level1) +{ + auto abilityInfo = std::make_shared(); + sptr token = new AppExecFwk::MockAbilityToken(); + auto record = std::make_shared(abilityInfo, token, nullptr, 0); + auto application = std::make_shared(); + auto contextImpl = std::make_shared(); + auto applicationContext = ApplicationContext::GetInstance(); + applicationContext->AttachContextImpl(contextImpl); + application->SetApplicationContext(applicationContext); + auto handler = std::make_shared(nullptr); + extension_->Init(record, application, handler, token); + EXPECT_NE(extension_->GetContext(), nullptr); +} + +/** + * @tc.number: UIExtensionTest_CreateAndInitContext_0100 + * @tc.name: CreateAndInitContext + * @tc.desc: CreateAndInitContext with valid parameters returns context. + */ +HWTEST_F(UIExtensionTest, UIExtensionTest_CreateAndInitContext_0100, Function | MediumTest | Level1) +{ + auto abilityInfo = std::make_shared(); + sptr token = new AppExecFwk::MockAbilityToken(); + auto record = std::make_shared(abilityInfo, token, nullptr, 0); + auto application = std::make_shared(); + auto contextImpl = std::make_shared(); + auto applicationContext = ApplicationContext::GetInstance(); + applicationContext->AttachContextImpl(contextImpl); + application->SetApplicationContext(applicationContext); + auto handler = std::make_shared(nullptr); + + auto context = extension_->CreateAndInitContext(record, application, handler, token); + EXPECT_NE(context, nullptr); +} + +/** + * @tc.number: UIExtensionTest_ForegroundWindow_MultipleWindows_0100 + * @tc.name: ForegroundWindow MultipleWindows + * @tc.desc: ForegroundWindow with multiple windows in map manages foreground set correctly. + */ +HWTEST_F(UIExtensionTest, UIExtensionTest_ForegroundWindow_MultipleWindows_0100, Function | MediumTest | Level1) +{ + AAFwk::Want want; + auto mockWindow1 = new Rosen::MockWindow(); + auto mockWindow2 = new Rosen::MockWindow(); + extension_->uiWindowMap_[TEST_COMPONENT_ID] = mockWindow1; + extension_->uiWindowMap_[TEST_COMPONENT_ID_2] = mockWindow2; + extension_->foregroundWindows_.clear(); + + sptr sessionInfo1 = new AAFwk::SessionInfo(); + sessionInfo1->uiExtensionComponentId = TEST_COMPONENT_ID; + extension_->ForegroundWindow(want, sessionInfo1); + EXPECT_EQ(extension_->foregroundWindows_.size(), 1u); + EXPECT_EQ(extension_->foregroundWindows_.count(TEST_COMPONENT_ID), 1u); + + sptr sessionInfo2 = new AAFwk::SessionInfo(); + sessionInfo2->uiExtensionComponentId = TEST_COMPONENT_ID_2; + extension_->ForegroundWindow(want, sessionInfo2); + EXPECT_EQ(extension_->foregroundWindows_.size(), 2u); + EXPECT_EQ(extension_->foregroundWindows_.count(TEST_COMPONENT_ID_2), 1u); + + extension_->uiWindowMap_.clear(); + extension_->foregroundWindows_.clear(); +} + +/** + * @tc.number: UIExtensionTest_BackgroundWindow_MultipleWindows_0100 + * @tc.name: BackgroundWindow MultipleWindows + * @tc.desc: BackgroundWindow with multiple foreground windows removes only target. + */ +HWTEST_F(UIExtensionTest, UIExtensionTest_BackgroundWindow_MultipleWindows_0100, Function | MediumTest | Level1) +{ + auto mockWindow1 = new Rosen::MockWindow(); + auto mockWindow2 = new Rosen::MockWindow(); + extension_->uiWindowMap_[TEST_COMPONENT_ID] = mockWindow1; + extension_->uiWindowMap_[TEST_COMPONENT_ID_2] = mockWindow2; + extension_->foregroundWindows_.emplace(TEST_COMPONENT_ID); + extension_->foregroundWindows_.emplace(TEST_COMPONENT_ID_2); + + sptr sessionInfo = new AAFwk::SessionInfo(); + sessionInfo->uiExtensionComponentId = TEST_COMPONENT_ID; + extension_->BackgroundWindow(sessionInfo); + EXPECT_EQ(extension_->foregroundWindows_.size(), 1u); + EXPECT_EQ(extension_->foregroundWindows_.count(TEST_COMPONENT_ID), 0u); + EXPECT_EQ(extension_->foregroundWindows_.count(TEST_COMPONENT_ID_2), 1u); + + extension_->uiWindowMap_.clear(); + extension_->foregroundWindows_.clear(); +} + +/** + * @tc.number: UIExtensionTest_OnConfigurationUpdated_0100 + * @tc.name: OnConfigurationUpdated + * @tc.desc: OnConfigurationUpdated with null context does not crash. + */ +HWTEST_F(UIExtensionTest, UIExtensionTest_OnConfigurationUpdated_0100, Function | MediumTest | Level1) +{ + extension_->uiWindowMap_.clear(); + extension_->foregroundWindows_.clear(); + AppExecFwk::Configuration config; + extension_->OnConfigurationUpdated(config); + EXPECT_EQ(extension_->uiWindowMap_.size(), 0u); + EXPECT_EQ(extension_->foregroundWindows_.size(), 0u); +} + +/** + * @tc.number: UIExtensionTest_OnAbilityConfigurationUpdated_0100 + * @tc.name: OnAbilityConfigurationUpdated + * @tc.desc: OnAbilityConfigurationUpdated with null context does not crash. + */ +HWTEST_F(UIExtensionTest, UIExtensionTest_OnAbilityConfigurationUpdated_0100, Function | MediumTest | Level1) +{ + extension_->uiWindowMap_.clear(); + extension_->foregroundWindows_.clear(); + AppExecFwk::Configuration config; + extension_->OnAbilityConfigurationUpdated(config); + EXPECT_EQ(extension_->uiWindowMap_.size(), 0u); + EXPECT_EQ(extension_->foregroundWindows_.size(), 0u); +} +} // namespace AbilityRuntime +} // namespace OHOS diff --git a/test/unittest/ui_extension_context_second_test/BUILD.gn b/test/unittest/ui_extension_context_second_test/BUILD.gn index b56cb02367..b911f6476d 100644 --- a/test/unittest/ui_extension_context_second_test/BUILD.gn +++ b/test/unittest/ui_extension_context_second_test/BUILD.gn @@ -27,6 +27,7 @@ ohos_unittest("ui_extension_context_second_test") { "${ability_runtime_test_path}/mock/frameworks_kits_ability_native_test/include", "${ability_runtime_test_path}/mock/services_appmgr_test/include", "${ability_runtime_path}/interfaces/kits/native/ability/native/ui_extension_ability", + "${ability_runtime_path}/interfaces/kits/native/ability/native/ui_extension_base", ] if (target_cpu == "arm") { diff --git a/test/unittest/ui_extension_context_second_test/ui_extension_context_second_test.cpp b/test/unittest/ui_extension_context_second_test/ui_extension_context_second_test.cpp index a3959d6cd1..69d94cdaf1 100644 --- a/test/unittest/ui_extension_context_second_test/ui_extension_context_second_test.cpp +++ b/test/unittest/ui_extension_context_second_test/ui_extension_context_second_test.cpp @@ -1735,5 +1735,514 @@ HWTEST_F(UIExtensionContextTest, CleanupAnimationResources_0100, TestSize.Level1 context->eventHandler_ = nullptr; TAG_LOGI(AAFwkTag::TEST, "CleanupAnimationResources_0100 end"); } + +/** + * @tc.number: CleanupAnimationResources_0200 + * @tc.name: CleanupAnimationResources with null eventHandler + * @tc.desc: CleanupAnimationResources when eventHandler is null does not crash. + */ +HWTEST_F(UIExtensionContextTest, CleanupAnimationResources_0200, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "CleanupAnimationResources_0200 start"); + auto context = std::make_shared(); + ASSERT_NE(context, nullptr); + + context->eventHandler_ = nullptr; + int32_t terminateRequestId = 999; + // Should not crash when eventHandler is null + context->CleanupAnimationResources(terminateRequestId); + EXPECT_EQ(context->eventHandler_, nullptr); + + TAG_LOGI(AAFwkTag::TEST, "CleanupAnimationResources_0200 end"); +} + +// ==================== ExecuteTerminationWithTimeout Tests ==================== + +/** + * @tc.number: ExecuteTerminationWithTimeout_0100 + * @tc.name: ExecuteTerminationWithTimeout request not found + * @tc.desc: ExecuteTerminationWithTimeout returns early when request not found. + */ +HWTEST_F(UIExtensionContextTest, ExecuteTerminationWithTimeout_0100, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "ExecuteTerminationWithTimeout_0100 start"); + auto context = std::make_shared(); + ASSERT_NE(context, nullptr); + context->screenMode_ = AAFwk::EMBEDDED_FULL_SCREEN_MODE; + + // No pending request registered + EXPECT_TRUE(context->pendingTerminateRequests_.empty()); + + sptr token; + int32_t terminateRequestId = 111; + // Should not crash - request not found, early return + context->ExecuteTerminationWithTimeout(token, terminateRequestId); + EXPECT_TRUE(context->pendingTerminateRequests_.empty()); + + TAG_LOGI(AAFwkTag::TEST, "ExecuteTerminationWithTimeout_0100 end"); +} + +/** + * @tc.number: ExecuteTerminationWithTimeout_0200 + * @tc.name: ExecuteTerminationWithTimeout request already handled + * @tc.desc: ExecuteTerminationWithTimeout returns early when request already handled. + */ +HWTEST_F(UIExtensionContextTest, ExecuteTerminationWithTimeout_0200, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "ExecuteTerminationWithTimeout_0200 start"); + auto context = std::make_shared(); + ASSERT_NE(context, nullptr); + context->screenMode_ = AAFwk::EMBEDDED_FULL_SCREEN_MODE; + + int32_t terminateRequestId = 222; + UIExtensionContext::PendingTerminateRequest request; + request.resultCode = 0; + request.want = {}; + request.callback = [](ErrCode) {}; + request.hasResult = false; + request.handled = true; // Already handled + context->pendingTerminateRequests_[terminateRequestId] = request; + + bool callbackExecuted = false; + context->pendingTerminateRequests_[terminateRequestId].callback = + [&callbackExecuted](ErrCode) { callbackExecuted = true; }; + + sptr token; + context->ExecuteTerminationWithTimeout(token, terminateRequestId); + // Should not invoke callback because request is already handled + EXPECT_FALSE(callbackExecuted); + // Request should still be in map (not erased for already-handled case) + EXPECT_FALSE(context->pendingTerminateRequests_.empty()); + + context->pendingTerminateRequests_.clear(); + TAG_LOGI(AAFwkTag::TEST, "ExecuteTerminationWithTimeout_0200 end"); +} + +/** + * @tc.number: ExecuteTerminationWithTimeout_0300 + * @tc.name: ExecuteTerminationWithTimeout with hasResult=false + * @tc.desc: ExecuteTerminationWithTimeout executes termination without transfer. + */ +HWTEST_F(UIExtensionContextTest, ExecuteTerminationWithTimeout_0300, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "ExecuteTerminationWithTimeout_0300 start"); + auto context = std::make_shared(); + ASSERT_NE(context, nullptr); + context->screenMode_ = AAFwk::EMBEDDED_FULL_SCREEN_MODE; + context->eventHandler_ = std::make_shared(AppExecFwk::EventRunner::Create()); + + int32_t terminateRequestId = 333; + UIExtensionContext::PendingTerminateRequest request; + request.resultCode = 0; + request.want = {}; + request.callback = [](ErrCode) {}; + request.hasResult = false; // No result to transfer + request.handled = false; + context->pendingTerminateRequests_[terminateRequestId] = request; + + sptr token; + context->ExecuteTerminationWithTimeout(token, terminateRequestId); + // Request should be removed after processing + EXPECT_TRUE(context->pendingTerminateRequests_.empty()); + + context->eventHandler_ = nullptr; + TAG_LOGI(AAFwkTag::TEST, "ExecuteTerminationWithTimeout_0300 end"); +} + +/** + * @tc.number: ExecuteTerminationWithTimeout_0400 + * @tc.name: ExecuteTerminationWithTimeout with hasResult=true + * @tc.desc: ExecuteTerminationWithTimeout transfers result before termination. + */ +HWTEST_F(UIExtensionContextTest, ExecuteTerminationWithTimeout_0400, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "ExecuteTerminationWithTimeout_0400 start"); + auto context = std::make_shared(); + ASSERT_NE(context, nullptr); + context->screenMode_ = AAFwk::EMBEDDED_FULL_SCREEN_MODE; + context->eventHandler_ = std::make_shared(AppExecFwk::EventRunner::Create()); + + int32_t terminateRequestId = 444; + UIExtensionContext::PendingTerminateRequest request; + request.resultCode = 100; + AAFwk::Want want; + request.want = want; + request.callback = [](ErrCode) {}; + request.hasResult = true; // Has result to transfer + request.handled = false; + context->pendingTerminateRequests_[terminateRequestId] = request; + + sptr token; + context->ExecuteTerminationWithTimeout(token, terminateRequestId); + // Request should be removed after processing + EXPECT_TRUE(context->pendingTerminateRequests_.empty()); + + context->eventHandler_ = nullptr; + TAG_LOGI(AAFwkTag::TEST, "ExecuteTerminationWithTimeout_0400 end"); +} + +/** + * @tc.number: ExecuteTerminationWithTimeout_0500 + * @tc.name: ExecuteTerminationWithTimeout with callback invoked + * @tc.desc: ExecuteTerminationWithTimeout invokes callback with error code. + */ +HWTEST_F(UIExtensionContextTest, ExecuteTerminationWithTimeout_0500, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "ExecuteTerminationWithTimeout_0500 start"); + auto context = std::make_shared(); + ASSERT_NE(context, nullptr); + context->screenMode_ = AAFwk::EMBEDDED_FULL_SCREEN_MODE; + context->eventHandler_ = std::make_shared(AppExecFwk::EventRunner::Create()); + + int32_t terminateRequestId = 555; + ErrCode receivedErrCode = ERR_OK; + UIExtensionContext::PendingTerminateRequest request; + request.resultCode = 0; + request.want = {}; + request.callback = [&receivedErrCode](ErrCode err) { receivedErrCode = err; }; + request.hasResult = false; + request.handled = false; + context->pendingTerminateRequests_[terminateRequestId] = request; + + sptr token; + context->ExecuteTerminationWithTimeout(token, terminateRequestId); + // Callback should be invoked with actual TerminateAbility result + EXPECT_NE(receivedErrCode, ERR_OK); // Will fail due to invalid token + EXPECT_TRUE(context->pendingTerminateRequests_.empty()); + + context->eventHandler_ = nullptr; + TAG_LOGI(AAFwkTag::TEST, "ExecuteTerminationWithTimeout_0500 end"); +} + +// ==================== TransferAbilityResultToWindow Tests ==================== + +/** + * @tc.number: TransferAbilityResultToWindow_0100 + * @tc.name: TransferAbilityResultToWindow with invalid token + * @tc.desc: TransferAbilityResultToWindow returns error when token is invalid. + */ +HWTEST_F(UIExtensionContextTest, TransferAbilityResultToWindow_0100, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "TransferAbilityResultToWindow_0100 start"); + auto context = std::make_shared(); + ASSERT_NE(context, nullptr); + + int32_t resultCode = 100; + AAFwk::Want want; + auto result = context->TransferAbilityResultToWindow(resultCode, want); + // Will fail due to invalid token/AbilityManagerClient + EXPECT_NE(result, ERR_OK); + + TAG_LOGI(AAFwkTag::TEST, "TransferAbilityResultToWindow_0100 end"); +} + +// ==================== TerminateSelfWithAnimation failure cleanup Tests ==================== + +/** + * @tc.number: TerminateSelfWithAnimation_0200 + * @tc.name: TerminateSelfWithAnimation without animation callback + * @tc.desc: TerminateSelfWithAnimation falls back when no animation callback registered. + */ +HWTEST_F(UIExtensionContextTest, TerminateSelfWithAnimation_0200, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "TerminateSelfWithAnimation_0200 start"); + auto context = std::make_shared(); + ASSERT_NE(context, nullptr); + context->screenMode_ = AAFwk::EMBEDDED_FULL_SCREEN_MODE; + context->terminateSelfWithAnimationCallback_ = nullptr; // No animation callback + + ErrCode receivedErr = ERR_OK; + TerminateSelfResultCallback callback = [&receivedErr](ErrCode err) { receivedErr = err; }; + + context->TerminateSelfWithAnimation(std::move(callback)); + // Falls back to TerminateSelfInner because no animation callback + // TerminateSelfInner processes and removes request + EXPECT_TRUE(context->pendingTerminateRequests_.empty()); + + TAG_LOGI(AAFwkTag::TEST, "TerminateSelfWithAnimation_0200 end"); +} + +/** + * @tc.number: TerminateSelfWithAnimation_0300 + * @tc.name: TerminateSelfWithAnimation verifies hasResult=false + * @tc.desc: TerminateSelfWithAnimation creates request with hasResult=false. + */ +HWTEST_F(UIExtensionContextTest, TerminateSelfWithAnimation_0300, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "TerminateSelfWithAnimation_0300 start"); + auto context = std::make_shared(); + ASSERT_NE(context, nullptr); + context->screenMode_ = AAFwk::EMBEDDED_FULL_SCREEN_MODE; + + TerminateSelfWithAnimationCallback animCallback = [](int32_t) {}; + context->terminateSelfWithAnimationCallback_ = animCallback; + context->eventHandler_ = std::make_shared(AppExecFwk::EventRunner::Create()); + + TerminateSelfResultCallback callback = [](ErrCode) {}; + auto result = context->TerminateSelfWithAnimation(std::move(callback)); + EXPECT_EQ(result, ERR_OK); + + // Verify hasResult is false + EXPECT_FALSE(context->pendingTerminateRequests_.empty()); + auto& req = context->pendingTerminateRequests_.begin()->second; + EXPECT_FALSE(req.hasResult); + + context->pendingTerminateRequests_.clear(); + context->terminateSelfWithAnimationCallback_ = nullptr; + context->eventHandler_ = nullptr; + TAG_LOGI(AAFwkTag::TEST, "TerminateSelfWithAnimation_0300 end"); +} + +// ==================== TerminateSelfWithResultAndAnimation failure Tests ==================== + +/** + * @tc.number: TerminateSelfWithResultAndAnimation_0200 + * @tc.name: TerminateSelfWithResultAndAnimation without animation callback + * @tc.desc: TerminateSelfWithResultAndAnimation falls back when no callback. + */ +HWTEST_F(UIExtensionContextTest, TerminateSelfWithResultAndAnimation_0200, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "TerminateSelfWithResultAndAnimation_0200 start"); + auto context = std::make_shared(); + ASSERT_NE(context, nullptr); + context->screenMode_ = AAFwk::EMBEDDED_FULL_SCREEN_MODE; + context->terminateSelfWithAnimationCallback_ = nullptr; // No animation callback + + int32_t resultCode = 200; + AAFwk::Want want; + bool callbackInvoked = false; + ErrCode receivedErr = ERR_OK; + TerminateSelfResultCallback callback = [&callbackInvoked, &receivedErr](ErrCode err) { + callbackInvoked = true; + receivedErr = err; + }; + + context->TerminateSelfWithResultAndAnimation(resultCode, want, std::move(callback)); + // Falls back to TerminateSelfInner, processes and removes request + EXPECT_TRUE(context->pendingTerminateRequests_.empty()); + + TAG_LOGI(AAFwkTag::TEST, "TerminateSelfWithResultAndAnimation_0200 end"); +} + +/** + * @tc.number: TerminateSelfWithResultAndAnimation_0300 + * @tc.name: TerminateSelfWithResultAndAnimation verifies result data + * @tc.desc: TerminateSelfWithResultAndAnimation stores correct result and want. + */ +HWTEST_F(UIExtensionContextTest, TerminateSelfWithResultAndAnimation_0300, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "TerminateSelfWithResultAndAnimation_0300 start"); + auto context = std::make_shared(); + ASSERT_NE(context, nullptr); + context->screenMode_ = AAFwk::EMBEDDED_FULL_SCREEN_MODE; + + TerminateSelfWithAnimationCallback animCallback = [](int32_t) {}; + context->terminateSelfWithAnimationCallback_ = animCallback; + context->eventHandler_ = std::make_shared(AppExecFwk::EventRunner::Create()); + + int32_t resultCode = 123; + AAFwk::Want want; + want.SetParam("test_key", 456); + TerminateSelfResultCallback callback = [](ErrCode) {}; + + context->TerminateSelfWithResultAndAnimation(resultCode, want, std::move(callback)); + + EXPECT_FALSE(context->pendingTerminateRequests_.empty()); + auto& req = context->pendingTerminateRequests_.begin()->second; + EXPECT_TRUE(req.hasResult); + EXPECT_EQ(req.resultCode, 123); + + context->pendingTerminateRequests_.clear(); + context->terminateSelfWithAnimationCallback_ = nullptr; + context->eventHandler_ = nullptr; + TAG_LOGI(AAFwkTag::TEST, "TerminateSelfWithResultAndAnimation_0300 end"); +} + +// ==================== TerminateSelfInner additional branch Tests ==================== + +/** + * @tc.number: TerminateSelfInner_0300 + * @tc.name: TerminateSelfInner request not found + * @tc.desc: TerminateSelfInner returns ERR_OK when request not found. + */ +HWTEST_F(UIExtensionContextTest, TerminateSelfInner_0300, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "TerminateSelfInner_0300 start"); + auto context = std::make_shared(); + ASSERT_NE(context, nullptr); + context->screenMode_ = AAFwk::EMBEDDED_FULL_SCREEN_MODE; + + // No pending request registered + int32_t terminateRequestId = 9999; + auto result = context->TerminateSelfInner(terminateRequestId); + EXPECT_EQ(result, ERR_OK); + + TAG_LOGI(AAFwkTag::TEST, "TerminateSelfInner_0300 end"); +} + +/** + * @tc.number: TerminateSelfInner_0400 + * @tc.name: TerminateSelfInner with hasResult=true + * @tc.desc: TerminateSelfInner transfers result when hasResult=true. + */ +HWTEST_F(UIExtensionContextTest, TerminateSelfInner_0400, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "TerminateSelfInner_0400 start"); + auto context = std::make_shared(); + ASSERT_NE(context, nullptr); + context->screenMode_ = AAFwk::EMBEDDED_FULL_SCREEN_MODE; + context->eventHandler_ = std::make_shared(AppExecFwk::EventRunner::Create()); + context->terminateSelfWithAnimationCallback_ = [](int32_t) {}; + + int32_t terminateRequestId = 777; + UIExtensionContext::PendingTerminateRequest request; + request.resultCode = 100; + AAFwk::Want want; + want.SetParam("result_key", 789); + request.want = want; + request.callback = [](ErrCode) {}; + request.hasResult = true; // Has result to transfer + request.handled = false; + context->pendingTerminateRequests_[terminateRequestId] = request; + + auto result = context->TerminateSelfInner(terminateRequestId); + // TerminateAbility fails due to invalid token, but transfer was attempted + EXPECT_NE(result, ERR_OK); + EXPECT_TRUE(context->pendingTerminateRequests_.empty()); + + context->eventHandler_ = nullptr; + context->terminateSelfWithAnimationCallback_ = nullptr; + TAG_LOGI(AAFwkTag::TEST, "TerminateSelfInner_0400 end"); +} + +/** + * @tc.number: TerminateSelfInner_0500 + * @tc.name: TerminateSelfInner with null callback + * @tc.desc: TerminateSelfInner does not crash when callback is null. + */ +HWTEST_F(UIExtensionContextTest, TerminateSelfInner_0500, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "TerminateSelfInner_0500 start"); + auto context = std::make_shared(); + ASSERT_NE(context, nullptr); + context->screenMode_ = AAFwk::EMBEDDED_FULL_SCREEN_MODE; + context->eventHandler_ = std::make_shared(AppExecFwk::EventRunner::Create()); + context->terminateSelfWithAnimationCallback_ = [](int32_t) {}; + + int32_t terminateRequestId = 888; + UIExtensionContext::PendingTerminateRequest request; + request.resultCode = 0; + request.want = {}; + request.callback = nullptr; // Null callback + request.hasResult = false; + request.handled = false; + context->pendingTerminateRequests_[terminateRequestId] = request; + + // Should not crash with null callback + context->TerminateSelfInner(terminateRequestId); + EXPECT_TRUE(context->pendingTerminateRequests_.empty()); + + context->eventHandler_ = nullptr; + context->terminateSelfWithAnimationCallback_ = nullptr; + TAG_LOGI(AAFwkTag::TEST, "TerminateSelfInner_0500 end"); +} + +// ==================== HandleTerminateWithAnimation additional branch Tests ==================== + +/** + * @tc.number: HandleTerminateWithAnimation_0400 + * @tc.name: HandleTerminateWithAnimation with callback but no eventHandler + * @tc.desc: HandleTerminateWithAnimation creates eventHandler when null. + */ +HWTEST_F(UIExtensionContextTest, HandleTerminateWithAnimation_0400, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "HandleTerminateWithAnimation_0400 start"); + auto context = std::make_shared(); + ASSERT_NE(context, nullptr); + context->screenMode_ = AAFwk::EMBEDDED_FULL_SCREEN_MODE; + context->eventHandler_ = nullptr; // No eventHandler + + int32_t receivedRequestId = 0; + TerminateSelfWithAnimationCallback callback = + [&receivedRequestId](int32_t requestId) { + receivedRequestId = requestId; + }; + context->terminateSelfWithAnimationCallback_ = callback; + + // Create a pending request for TerminateSelfInner fallback + int32_t terminateRequestId = 101; + UIExtensionContext::PendingTerminateRequest request; + request.resultCode = 0; + request.want = {}; + request.callback = [](ErrCode) {}; + request.hasResult = false; + request.handled = false; + context->pendingTerminateRequests_[terminateRequestId] = request; + + auto result = context->HandleTerminateWithAnimation(terminateRequestId); + // GetOrCreateEventHandler should create a new handler and PostTask should succeed + EXPECT_EQ(result, ERR_OK); + EXPECT_EQ(receivedRequestId, terminateRequestId); + EXPECT_NE(context->eventHandler_, nullptr); + + // Clean up pending request (timeout task is pending) + context->pendingTerminateRequests_.clear(); + context->eventHandler_ = nullptr; + context->terminateSelfWithAnimationCallback_ = nullptr; + TAG_LOGI(AAFwkTag::TEST, "HandleTerminateWithAnimation_0400 end"); +} + +/** + * @tc.number: HandleTerminateWithAnimation_0500 + * @tc.name: HandleTerminateWithAnimation callback retained after call + * @tc.desc: HandleTerminateWithAnimation retains callback for repeated calls. + */ +HWTEST_F(UIExtensionContextTest, HandleTerminateWithAnimation_0500, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "HandleTerminateWithAnimation_0500 start"); + auto context = std::make_shared(); + ASSERT_NE(context, nullptr); + context->screenMode_ = AAFwk::EMBEDDED_FULL_SCREEN_MODE; + context->eventHandler_ = std::make_shared(AppExecFwk::EventRunner::Create()); + + int callCount = 0; + TerminateSelfWithAnimationCallback callback = [&callCount](int32_t) { callCount++; }; + context->terminateSelfWithAnimationCallback_ = callback; + + // First call + context->HandleTerminateWithAnimation(1); + EXPECT_EQ(callCount, 1); + EXPECT_NE(context->terminateSelfWithAnimationCallback_, nullptr); + + // Second call - callback should still be available + context->HandleTerminateWithAnimation(2); + EXPECT_EQ(callCount, 2); + EXPECT_NE(context->terminateSelfWithAnimationCallback_, nullptr); + + context->pendingTerminateRequests_.clear(); + context->eventHandler_ = nullptr; + context->terminateSelfWithAnimationCallback_ = nullptr; + TAG_LOGI(AAFwkTag::TEST, "HandleTerminateWithAnimation_0500 end"); +} + +// ==================== RegisterTerminateSelfWithAnimation additional edge case ==================== + +/** + * @tc.number: RegisterTerminateSelfWithAnimation_0600 + * @tc.name: RegisterTerminateSelfWithAnimation JUMP_SCREEN_MODE + * @tc.desc: RegisterTerminateSelfWithAnimation rejects JUMP_SCREEN_MODE. + */ +HWTEST_F(UIExtensionContextTest, RegisterTerminateSelfWithAnimation_0600, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "RegisterTerminateSelfWithAnimation_0600 start"); + auto context = std::make_shared(); + ASSERT_NE(context, nullptr); + context->screenMode_ = AAFwk::JUMP_SCREEN_MODE; + TerminateSelfWithAnimationCallback callback = [](int32_t) {}; + auto result = context->RegisterTerminateSelfWithAnimation(std::move(callback)); + EXPECT_EQ(result, ERR_INVALID_OPERATION); + + TAG_LOGI(AAFwkTag::TEST, "RegisterTerminateSelfWithAnimation_0600 end"); +} } // namespace AbilityRuntime } // namespace OHOS diff --git a/test/unittest/ui_extension_context_test/BUILD.gn b/test/unittest/ui_extension_context_test/BUILD.gn index 2e25b86cf9..4a4faa8ed3 100644 --- a/test/unittest/ui_extension_context_test/BUILD.gn +++ b/test/unittest/ui_extension_context_test/BUILD.gn @@ -27,6 +27,7 @@ ohos_unittest("ui_extension_context_test") { "${ability_runtime_test_path}/mock/frameworks_kits_ability_native_test/include", "${ability_runtime_test_path}/mock/services_appmgr_test/include", "${ability_runtime_path}/interfaces/kits/native/ability/native/ui_extension_ability", + "${ability_runtime_path}/interfaces/kits/native/ability/native/ui_extension_base", ] if (target_cpu == "arm") { @@ -58,6 +59,17 @@ ohos_unittest("ui_extension_context_test") { "ipc:ipc_napi", "napi:ace_napi", ] + + if (ability_runtime_graphics) { + external_deps += [ + "ace_engine:ace_uicontent", + "window_manager:libwm", + ] + defines = [ + "SUPPORT_GRAPHICS", + "SUPPORT_SCREEN", + ] + } } group("unittest") { diff --git a/test/unittest/ui_extension_context_test/ui_extension_context_test.cpp b/test/unittest/ui_extension_context_test/ui_extension_context_test.cpp index 2d0dd5605b..509649bb78 100644 --- a/test/unittest/ui_extension_context_test/ui_extension_context_test.cpp +++ b/test/unittest/ui_extension_context_test/ui_extension_context_test.cpp @@ -19,12 +19,14 @@ #define protected public #include "extension_base.h" #include "ui_extension_context.h" +#include "ui_extension_modal_callback.h" #undef private #undef protected #include "hilog_tag_wrapper.h" #include "want.h" #include "mock_window.h" +#include "int_wrapper.h" using namespace testing::ext; using namespace OHOS::Rosen; @@ -1538,5 +1540,415 @@ HWTEST_F(UIExtensionContextTest, SetAbilityConfiguration_0500, TestSize.Level1) TAG_LOGI(AAFwkTag::TEST, "SetAbilityConfiguration_0500 end"); } + +/** + * @tc.number: CreateModalUIExtensionWithApp_0100 + * @tc.name: CreateModalUIExtensionWithApp + * @tc.desc: Test CreateModalUIExtensionWithApp when uiContent is nullptr + */ +HWTEST_F(UIExtensionContextTest, CreateModalUIExtensionWithApp_0100, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "CreateModalUIExtensionWithApp_0100 start"); + auto context = std::make_shared(); + ASSERT_NE(context, nullptr); + AAFwk::Want want; + auto result = context->CreateModalUIExtensionWithApp(want); + EXPECT_EQ(result, ERR_INVALID_VALUE); + TAG_LOGI(AAFwkTag::TEST, "CreateModalUIExtensionWithApp_0100 end"); +} + +/** + * @tc.number: CreateModalUIExtensionWithApp_0200 + * @tc.name: CreateModalUIExtensionWithApp + * @tc.desc: Test CreateModalUIExtensionWithApp when UIExtension already exists + */ +HWTEST_F(UIExtensionContextTest, CreateModalUIExtensionWithApp_0200, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "CreateModalUIExtensionWithApp_0200 start"); + auto context = std::make_shared(); + ASSERT_NE(context, nullptr); + + // Setup window with UIContent + sptr window = sptr::MakeSptr(); + context->SetWindow(window); + + // Add an existing UIExtension to the map + AAFwk::Want existingWant; + ElementName element("", "com.example.test", "TestExtension"); + existingWant.SetElement(element); + context->uiExtensionMap_[1] = existingWant; + + // Try to create the same extension again + AAFwk::Want want; + want.SetElement(element); + auto result = context->CreateModalUIExtensionWithApp(want); + EXPECT_EQ(result, ERR_INVALID_VALUE); + + context->uiExtensionMap_.clear(); + TAG_LOGI(AAFwkTag::TEST, "CreateModalUIExtensionWithApp_0200 end"); +} + +/** + * @tc.number: CreateModalUIExtensionWithApp_0300 + * @tc.name: CreateModalUIExtensionWithApp + * @tc.desc: Test CreateModalUIExtensionWithApp when CreateModalUIExtension fails (returns sessionId=0) + */ +HWTEST_F(UIExtensionContextTest, CreateModalUIExtensionWithApp_0300, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "CreateModalUIExtensionWithApp_0300 start"); + auto context = std::make_shared(); + ASSERT_NE(context, nullptr); + + // Setup window with UIContent - MockWindow returns nullptr UIContent + sptr window = sptr::MakeSptr(); + context->SetWindow(window); + + AAFwk::Want want; + ElementName element("", "com.example.test", "TestExtension"); + want.SetElement(element); + + // When UIContent is not null but CreateModalUIExtension returns 0 + // The mock window will have nullptr UIContent, so it should return ERR_INVALID_VALUE + auto result = context->CreateModalUIExtensionWithApp(want); + EXPECT_EQ(result, ERR_INVALID_VALUE); + + TAG_LOGI(AAFwkTag::TEST, "CreateModalUIExtensionWithApp_0300 end"); +} + +/** + * @tc.number: EraseUIExtension_0100 + * @tc.name: EraseUIExtension + * @tc.desc: Test EraseUIExtension when sessionId exists + */ +HWTEST_F(UIExtensionContextTest, EraseUIExtension_0100, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "EraseUIExtension_0100 start"); + auto context = std::make_shared(); + ASSERT_NE(context, nullptr); + + // Add a UIExtension to the map + AAFwk::Want want; + ElementName element("", "com.example.test", "TestExtension"); + want.SetElement(element); + int32_t sessionId = 123; + context->uiExtensionMap_[sessionId] = want; + + EXPECT_EQ(context->uiExtensionMap_.size(), 1); + + // Erase the UIExtension + auto result = context->EraseUIExtension(sessionId); + EXPECT_EQ(result, ERR_OK); + EXPECT_EQ(context->uiExtensionMap_.size(), 0); + + TAG_LOGI(AAFwkTag::TEST, "EraseUIExtension_0100 end"); +} + +/** + * @tc.number: EraseUIExtension_0200 + * @tc.name: EraseUIExtension + * @tc.desc: Test EraseUIExtension when sessionId does not exist + */ +HWTEST_F(UIExtensionContextTest, EraseUIExtension_0200, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "EraseUIExtension_0200 start"); + auto context = std::make_shared(); + ASSERT_NE(context, nullptr); + + // Try to erase a non-existent sessionId + int32_t sessionId = 999; + auto result = context->EraseUIExtension(sessionId); + // EraseUIExtension always returns ERR_OK even if not found + EXPECT_EQ(result, ERR_OK); + EXPECT_EQ(context->uiExtensionMap_.size(), 0); + + TAG_LOGI(AAFwkTag::TEST, "EraseUIExtension_0200 end"); +} + +/** + * @tc.number: IsUIExtensionExist_0100 + * @tc.name: IsUIExtensionExist + * @tc.desc: Test IsUIExtensionExist when UIExtension exists + */ +HWTEST_F(UIExtensionContextTest, IsUIExtensionExist_0100, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "IsUIExtensionExist_0100 start"); + auto context = std::make_shared(); + ASSERT_NE(context, nullptr); + + // Add a UIExtension to the map + AAFwk::Want want; + ElementName element("", "com.example.test", "TestExtension"); + want.SetElement(element); + context->uiExtensionMap_[1] = want; + + // Check if exists + auto result = context->IsUIExtensionExist(want); + EXPECT_TRUE(result); + + context->uiExtensionMap_.clear(); + TAG_LOGI(AAFwkTag::TEST, "IsUIExtensionExist_0100 end"); +} + +/** + * @tc.number: IsUIExtensionExist_0200 + * @tc.name: IsUIExtensionExist + * @tc.desc: Test IsUIExtensionExist when UIExtension does not exist + */ +HWTEST_F(UIExtensionContextTest, IsUIExtensionExist_0200, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "IsUIExtensionExist_0200 start"); + auto context = std::make_shared(); + ASSERT_NE(context, nullptr); + + // Check non-existent extension + AAFwk::Want want; + ElementName element("", "com.example.test", "NonExistentExtension"); + want.SetElement(element); + + auto result = context->IsUIExtensionExist(want); + EXPECT_FALSE(result); + + TAG_LOGI(AAFwkTag::TEST, "IsUIExtensionExist_0200 end"); +} +/** + * @tc.number: CreateModalUIExtensionWithApp_0400 + * @tc.name: CreateModalUIExtensionWithApp + * @tc.desc: Test with USE_GLOBAL_UICONTENT=true bypasses IsUIExtensionExist check but uiContent=null. + */ +HWTEST_F(UIExtensionContextTest, CreateModalUIExtensionWithApp_0400, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "CreateModalUIExtensionWithApp_0400 start"); + auto context = std::make_shared(); + ASSERT_NE(context, nullptr); + + // Add existing extension + AAFwk::Want existingWant; + ElementName element("", "com.example.test", "TestExtension"); + existingWant.SetElement(element); + context->uiExtensionMap_[1] = existingWant; + + // Same element but with USE_GLOBAL_UICONTENT=true → bypasses the early return + AAFwk::Want want; + want.SetElement(element); + want.SetParam("ohos.uec.params.useGlobalUIContent", static_cast(1)); + + // Still fails because uiContent is null + auto result = context->CreateModalUIExtensionWithApp(want); + EXPECT_EQ(result, ERR_INVALID_VALUE); + + context->uiExtensionMap_.clear(); + TAG_LOGI(AAFwkTag::TEST, "CreateModalUIExtensionWithApp_0400 end"); +} + +/** + * @tc.number: CreateModalUIExtensionWithApp_0500 + * @tc.name: CreateModalUIExtensionWithApp + * @tc.desc: Test with DISPOSED_PROHIBIT_BACK=true, verifies config parameter is processed. + * uiContent is null so returns ERR_INVALID_VALUE before reaching CreateModalUIExtension. + */ +HWTEST_F(UIExtensionContextTest, CreateModalUIExtensionWithApp_0500, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "CreateModalUIExtensionWithApp_0500 start"); + auto context = std::make_shared(); + ASSERT_NE(context, nullptr); + + // Setup window (but UIContent will still be null from MockWindow) + sptr window = sptr::MakeSptr(); + context->SetWindow(window); + + AAFwk::Want want; + want.SetElement(ElementName("", "com.example.test", "TestExtension")); + want.SetParam("ohos.disposed.prohibitBack", static_cast(1)); + + auto result = context->CreateModalUIExtensionWithApp(want); + EXPECT_EQ(result, ERR_INVALID_VALUE); + + TAG_LOGI(AAFwkTag::TEST, "CreateModalUIExtensionWithApp_0500 end"); +} + +/** + * @tc.number: CreateModalUIExtensionWithApp_0600 + * @tc.name: CreateModalUIExtensionWithApp + * @tc.desc: Test with IS_WINDOWMODE_FOLLOWHOST parameter, verifies config processing. + * uiContent is null so returns ERR_INVALID_VALUE. + */ +HWTEST_F(UIExtensionContextTest, CreateModalUIExtensionWithApp_0600, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "CreateModalUIExtensionWithApp_0600 start"); + auto context = std::make_shared(); + ASSERT_NE(context, nullptr); + + sptr window = sptr::MakeSptr(); + context->SetWindow(window); + + AAFwk::Want want; + want.SetElement(ElementName("", "com.example.test", "TestExtension")); + want.SetParam("ohos.window.mode.followHost", static_cast(1)); + + auto result = context->CreateModalUIExtensionWithApp(want); + EXPECT_EQ(result, ERR_INVALID_VALUE); + + TAG_LOGI(AAFwkTag::TEST, "CreateModalUIExtensionWithApp_0600 end"); +} + +/** + * @tc.number: SetupModalCallbacks_0100 + * @tc.name: SetupModalCallbacks + * @tc.desc: Verify SetupModalCallbacks creates all 5 callbacks (onError, onRelease, onResult, onDestroy, onReceive). + */ +HWTEST_F(UIExtensionContextTest, SetupModalCallbacks_0100, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "SetupModalCallbacks_0100 start"); + auto context = std::make_shared(); + ASSERT_NE(context, nullptr); + + auto modalCallback = std::make_shared(); + modalCallback->SetSessionId(100); + + // Set context so callbacks don't crash + modalCallback->SetUIExtensionContext(context); + + auto callbacks = context->SetupModalCallbacks(modalCallback); + + // Verify all callbacks are non-null (callable) + EXPECT_TRUE(callbacks.onError != nullptr); + EXPECT_TRUE(callbacks.onRelease != nullptr); + EXPECT_TRUE(callbacks.onResult != nullptr); + EXPECT_TRUE(callbacks.onDestroy != nullptr); + EXPECT_TRUE(callbacks.onReceive != nullptr); + + TAG_LOGI(AAFwkTag::TEST, "SetupModalCallbacks_0100 end"); +} + +/** + * @tc.number: SetupModalCallbacks_0200 + * @tc.name: SetupModalCallbacks + * @tc.desc: Verify onError callback invokes UIExtensionModalCallback::OnError. + */ +HWTEST_F(UIExtensionContextTest, SetupModalCallbacks_0200, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "SetupModalCallbacks_0200 start"); + auto context = std::make_shared(); + ASSERT_NE(context, nullptr); + + auto modalCallback = std::make_shared(); + modalCallback->SetSessionId(200); + modalCallback->SetUIExtensionContext(context); + + auto callbacks = context->SetupModalCallbacks(modalCallback); + + // Invoke onError callback — should call modalCallback->OnError() without crash + ASSERT_TRUE(callbacks.onError != nullptr); + callbacks.onError(1, "test", "error message"); + + EXPECT_EQ(modalCallback->sessionId_, 200); + + TAG_LOGI(AAFwkTag::TEST, "SetupModalCallbacks_0200 end"); +} + +/** + * @tc.number: SetupModalCallbacks_0300 + * @tc.name: SetupModalCallbacks + * @tc.desc: Verify onRelease callback invokes UIExtensionModalCallback::OnRelease. + */ +HWTEST_F(UIExtensionContextTest, SetupModalCallbacks_0300, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "SetupModalCallbacks_0300 start"); + auto context = std::make_shared(); + ASSERT_NE(context, nullptr); + + auto modalCallback = std::make_shared(); + modalCallback->SetSessionId(300); + modalCallback->SetUIExtensionContext(context); + + auto callbacks = context->SetupModalCallbacks(modalCallback); + + // Invoke onRelease callback + ASSERT_TRUE(callbacks.onRelease != nullptr); + callbacks.onRelease(0); + + EXPECT_EQ(modalCallback->sessionId_, 300); + + TAG_LOGI(AAFwkTag::TEST, "SetupModalCallbacks_0300 end"); +} + +/** + * @tc.number: SetupModalCallbacks_0400 + * @tc.name: SetupModalCallbacks + * @tc.desc: Verify onResult callback can be invoked without crash. + */ +HWTEST_F(UIExtensionContextTest, SetupModalCallbacks_0400, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "SetupModalCallbacks_0400 start"); + auto context = std::make_shared(); + ASSERT_NE(context, nullptr); + + auto modalCallback = std::make_shared(); + modalCallback->SetSessionId(400); + modalCallback->SetUIExtensionContext(context); + + auto callbacks = context->SetupModalCallbacks(modalCallback); + + ASSERT_TRUE(callbacks.onResult != nullptr); + AAFwk::Want resultWant; + callbacks.onResult(0, resultWant); + + EXPECT_EQ(modalCallback->sessionId_, 400); + + TAG_LOGI(AAFwkTag::TEST, "SetupModalCallbacks_0400 end"); +} + +/** + * @tc.number: SetupModalCallbacks_0500 + * @tc.name: SetupModalCallbacks + * @tc.desc: Verify onDestroy callback invokes UIExtensionModalCallback::OnDestroy. + */ +HWTEST_F(UIExtensionContextTest, SetupModalCallbacks_0500, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "SetupModalCallbacks_0500 start"); + auto context = std::make_shared(); + ASSERT_NE(context, nullptr); + + auto modalCallback = std::make_shared(); + modalCallback->SetSessionId(500); + modalCallback->SetUIExtensionContext(context); + + auto callbacks = context->SetupModalCallbacks(modalCallback); + + ASSERT_TRUE(callbacks.onDestroy != nullptr); + callbacks.onDestroy(); + + EXPECT_EQ(modalCallback->sessionId_, 500); + + TAG_LOGI(AAFwkTag::TEST, "SetupModalCallbacks_0500 end"); +} + +/** + * @tc.number: SetupModalCallbacks_0600 + * @tc.name: SetupModalCallbacks + * @tc.desc: Verify onReceive callback invokes UIExtensionModalCallback::OnReceive with correct data. + */ +HWTEST_F(UIExtensionContextTest, SetupModalCallbacks_0600, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "SetupModalCallbacks_0600 start"); + auto context = std::make_shared(); + ASSERT_NE(context, nullptr); + + auto modalCallback = std::make_shared(); + modalCallback->SetSessionId(600); + modalCallback->SetUIExtensionContext(context); + + auto callbacks = context->SetupModalCallbacks(modalCallback); + + ASSERT_TRUE(callbacks.onReceive != nullptr); + AAFwk::WantParams data; + data.SetParam("testKey", AAFwk::Integer::Box(42)); + callbacks.onReceive(data); + + EXPECT_EQ(modalCallback->sessionId_, 600); + + TAG_LOGI(AAFwkTag::TEST, "SetupModalCallbacks_0600 end"); +} + } // namespace AbilityRuntime -} // namespace OHOS +} // namespace OHOS \ No newline at end of file diff --git a/test/unittest/ui_extension_modal_callback_test/BUILD.gn b/test/unittest/ui_extension_modal_callback_test/BUILD.gn new file mode 100644 index 0000000000..87aa94168c --- /dev/null +++ b/test/unittest/ui_extension_modal_callback_test/BUILD.gn @@ -0,0 +1,59 @@ +# 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_out_path = "ability_runtime/ability_runtime/ui_extension_modal_callback_test" + +ohos_unittest("ui_extension_modal_callback_test") { + module_out_path = module_out_path + + include_dirs = [ + "${ability_runtime_test_path}/mock/frameworks_kits_ability_native_test/include", + "${ability_runtime_path}/interfaces/kits/native/ability/native/ui_extension_base", + ] + + sources = [ + "ui_extension_modal_callback_test.cpp", + ] + + deps = [ + "${ability_runtime_innerkits_path}/ability_manager:ability_manager", + "${ability_runtime_native_path}/ability:ability_context_native", + "${ability_runtime_native_path}/ability/native:abilitykit_native", + "${ability_runtime_native_path}/ability/native:ui_extension", + "${ability_runtime_native_path}/appkit:app_context", + ] + + external_deps = [ + "ability_base:want", + "ace_engine:ace_uicontent", + "bundle_framework:appexecfwk_core", + "c_utils:utils", + "googletest:gmock_main", + "googletest:gtest_main", + "hilog:libhilog", + "ipc:ipc_core", + "napi:ace_napi", + ] +} + +group("unittest") { + testonly = true + deps = [] + + deps += [ + ":ui_extension_modal_callback_test", + ] +} diff --git a/test/unittest/ui_extension_modal_callback_test/ui_extension_modal_callback_test.cpp b/test/unittest/ui_extension_modal_callback_test/ui_extension_modal_callback_test.cpp new file mode 100644 index 0000000000..13037d38d3 --- /dev/null +++ b/test/unittest/ui_extension_modal_callback_test/ui_extension_modal_callback_test.cpp @@ -0,0 +1,327 @@ +/* + * 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 +#include + +#define private public +#define protected public +#include "ui_extension_modal_callback.h" +#include "ui_extension_context.h" +#undef private +#undef protected + +#include "hilog_tag_wrapper.h" +#include "mock_ui_content.h" +#include "want_params.h" +#include "int_wrapper.h" +#include "string_wrapper.h" + +using namespace testing::ext; +using namespace testing; +using namespace OHOS::AppExecFwk; +using namespace OHOS::AbilityRuntime; +using namespace OHOS::Ace; + +namespace OHOS { +namespace AAFwk { + +namespace { +constexpr const char* EXIT_EMBEDDABLE_PARAM = "ohos.param.exitEmbeddableUIExtension"; +} + +class UIExtensionModalCallbackTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp() override; + void TearDown() override; +}; + +void UIExtensionModalCallbackTest::SetUpTestCase(void) +{} + +void UIExtensionModalCallbackTest::TearDownTestCase(void) +{} + +void UIExtensionModalCallbackTest::SetUp() +{} + +void UIExtensionModalCallbackTest::TearDown() +{} + +// ===== SetSessionId tests ===== + +/** + * @tc.name: SetSessionId_0100 + * @tc.desc: SetSessionId stores the sessionId correctly. + */ +HWTEST_F(UIExtensionModalCallbackTest, SetSessionId_0100, TestSize.Level1) +{ + auto callback = std::make_shared(); + callback->SetSessionId(123); + EXPECT_EQ(callback->sessionId_, 123); +} + +// ===== OnRelease tests ===== + +/** + * @tc.name: OnRelease_0100 + * @tc.desc: OnRelease when context is null, early return without crash. + * Covers line 31-33: contextWeak_.lock() returns null. + */ +HWTEST_F(UIExtensionModalCallbackTest, OnRelease_0100, TestSize.Level1) +{ + auto callback = std::make_shared(); + callback->SetSessionId(123); + // contextWeak_ is default-constructed (empty weak_ptr) + callback->OnRelease(); + // Verify early return: no crash, sessionId unchanged + EXPECT_EQ(callback->sessionId_, 123); +} + +/** + * @tc.name: OnRelease_0200 + * @tc.desc: OnRelease with valid context and uiContent, verifies EraseUIExtension called. + * Covers line 36: context->EraseUIExtension and line 42: uiContent_->CloseModalUIExtension. + */ +HWTEST_F(UIExtensionModalCallbackTest, OnRelease_0200, TestSize.Level1) +{ + auto callback = std::make_shared(); + int32_t sessionId = 456; + callback->SetSessionId(sessionId); + + auto context = std::make_shared(); + callback->SetUIExtensionContext(context); + +#ifdef SUPPORT_SCREEN + auto* mockUIContent = new MockUIContent(); + EXPECT_CALL(*mockUIContent, CloseModalUIExtension(sessionId)).Times(1).WillOnce(Return()); + callback->SetUIContent(mockUIContent); +#endif + + callback->OnRelease(); + EXPECT_EQ(context->uiExtensionMap_.find(sessionId), context->uiExtensionMap_.end()); + +#ifdef SUPPORT_SCREEN + delete mockUIContent; +#endif +} + +/** + * @tc.name: OnRelease_0300 + * @tc.desc: OnRelease with valid context but uiContent is nullptr. + * Covers line 44-45: null uiContent_ branch (log only, no CloseModalUIExtension). + */ +HWTEST_F(UIExtensionModalCallbackTest, OnRelease_0300, TestSize.Level1) +{ + auto callback = std::make_shared(); + int32_t sessionId = 789; + callback->SetSessionId(sessionId); + + auto context = std::make_shared(); + callback->SetUIExtensionContext(context); + +#ifdef SUPPORT_SCREEN + callback->SetUIContent(nullptr); +#endif + + callback->OnRelease(); + // EraseUIExtension still called even when uiContent_ is null + EXPECT_EQ(context->uiExtensionMap_.find(sessionId), context->uiExtensionMap_.end()); +} + +// ===== OnError tests ===== + +/** + * @tc.name: OnError_0100 + * @tc.desc: OnError when context is null, early return without crash. + * Covers line 55-57: contextWeak_.lock() returns null. + */ +HWTEST_F(UIExtensionModalCallbackTest, OnError_0100, TestSize.Level1) +{ + auto callback = std::make_shared(); + callback->SetSessionId(321); + callback->OnError(); + EXPECT_EQ(callback->sessionId_, 321); +} + +/** + * @tc.name: OnError_0200 + * @tc.desc: OnError with valid context and uiContent. + * Covers line 61: EraseUIExtension and line 66: CloseModalUIExtension. + */ +HWTEST_F(UIExtensionModalCallbackTest, OnError_0200, TestSize.Level1) +{ + auto callback = std::make_shared(); + int32_t sessionId = 654; + callback->SetSessionId(sessionId); + + auto context = std::make_shared(); + callback->SetUIExtensionContext(context); + +#ifdef SUPPORT_SCREEN + auto* mockUIContent = new MockUIContent(); + EXPECT_CALL(*mockUIContent, CloseModalUIExtension(sessionId)).Times(1).WillOnce(Return()); + callback->SetUIContent(mockUIContent); +#endif + + callback->OnError(); + EXPECT_EQ(context->uiExtensionMap_.find(sessionId), context->uiExtensionMap_.end()); + +#ifdef SUPPORT_SCREEN + delete mockUIContent; +#endif +} + +/** + * @tc.name: OnError_0300 + * @tc.desc: OnError with valid context but uiContent is nullptr. + * Covers line 69: null uiContent_ branch. + */ +HWTEST_F(UIExtensionModalCallbackTest, OnError_0300, TestSize.Level1) +{ + auto callback = std::make_shared(); + int32_t sessionId = 555; + callback->SetSessionId(sessionId); + + auto context = std::make_shared(); + callback->SetUIExtensionContext(context); + +#ifdef SUPPORT_SCREEN + callback->SetUIContent(nullptr); +#endif + + callback->OnError(); + EXPECT_EQ(context->uiExtensionMap_.find(sessionId), context->uiExtensionMap_.end()); +} + +// ===== OnDestroy tests ===== + +/** + * @tc.name: OnDestroy_0100 + * @tc.desc: OnDestroy when context is null, early return without crash. + * Covers line 79-81: contextWeak_.lock() returns null. + */ +HWTEST_F(UIExtensionModalCallbackTest, OnDestroy_0100, TestSize.Level1) +{ + auto callback = std::make_shared(); + callback->SetSessionId(987); + callback->OnDestroy(); + EXPECT_EQ(callback->sessionId_, 987); +} + +/** + * @tc.name: OnDestroy_0200 + * @tc.desc: OnDestroy with valid context, verifies EraseUIExtension called. + * Covers line 86: context->EraseUIExtension. + */ +HWTEST_F(UIExtensionModalCallbackTest, OnDestroy_0200, TestSize.Level1) +{ + auto callback = std::make_shared(); + int32_t sessionId = 111; + callback->SetSessionId(sessionId); + + auto context = std::make_shared(); + callback->SetUIExtensionContext(context); + + callback->OnDestroy(); + EXPECT_EQ(context->uiExtensionMap_.find(sessionId), context->uiExtensionMap_.end()); +} + +// ===== OnReceive tests ===== + +/** + * @tc.name: OnReceive_0100 + * @tc.desc: OnReceive when context is null, early return without crash. + * Covers line 95-97: contextWeak_.lock() returns null. + */ +HWTEST_F(UIExtensionModalCallbackTest, OnReceive_0100, TestSize.Level1) +{ + auto callback = std::make_shared(); + callback->SetSessionId(222); + AAFwk::WantParams data; + callback->OnReceive(data); + EXPECT_EQ(callback->sessionId_, 222); +} + +/** + * @tc.name: OnReceive_0200 + * @tc.desc: OnReceive with exit param = 1, triggers TerminateSelfWithAnimation. + * Covers line 100-106: HasParam(true) + shouldExit==1 → TerminateSelfWithAnimation. + */ +HWTEST_F(UIExtensionModalCallbackTest, OnReceive_0200, TestSize.Level1) +{ + auto callback = std::make_shared(); + int32_t sessionId = 333; + callback->SetSessionId(sessionId); + + auto context = std::make_shared(); + callback->SetUIExtensionContext(context); + + AAFwk::WantParams data; + data.SetParam(EXIT_EMBEDDABLE_PARAM, AAFwk::Integer::Box(1)); + EXPECT_TRUE(data.HasParam(EXIT_EMBEDDABLE_PARAM)); + + callback->OnReceive(data); + EXPECT_NE(context, nullptr); +} + +/** + * @tc.name: OnReceive_0300 + * @tc.desc: OnReceive with exit param = 0 (has param but shouldExit != 1), no terminate. + * Covers line 100: HasParam(true) + line 102: shouldExit==0 → skip terminate. + */ +HWTEST_F(UIExtensionModalCallbackTest, OnReceive_0300, TestSize.Level1) +{ + auto callback = std::make_shared(); + int32_t sessionId = 444; + callback->SetSessionId(sessionId); + + auto context = std::make_shared(); + callback->SetUIExtensionContext(context); + + AAFwk::WantParams data; + data.SetParam(EXIT_EMBEDDABLE_PARAM, AAFwk::Integer::Box(0)); + EXPECT_TRUE(data.HasParam(EXIT_EMBEDDABLE_PARAM)); + + callback->OnReceive(data); + EXPECT_NE(context, nullptr); +} + +/** + * @tc.name: OnReceive_0400 + * @tc.desc: OnReceive with data that does not contain exit param, no terminate. + * Covers line 100: HasParam(false) → skip entire if block. + */ +HWTEST_F(UIExtensionModalCallbackTest, OnReceive_0400, TestSize.Level1) +{ + auto callback = std::make_shared(); + int32_t sessionId = 555; + callback->SetSessionId(sessionId); + + auto context = std::make_shared(); + callback->SetUIExtensionContext(context); + + AAFwk::WantParams data; + data.SetParam("someOtherParam", AAFwk::String::Box("testValue")); + EXPECT_FALSE(data.HasParam(EXIT_EMBEDDABLE_PARAM)); + + callback->OnReceive(data); + EXPECT_NE(context, nullptr); +} + +} // namespace AAFwk +} // namespace OHOS diff --git a/test/unittest/ui_extension_test/BUILD.gn b/test/unittest/ui_extension_test/BUILD.gn new file mode 100644 index 0000000000..439f27900b --- /dev/null +++ b/test/unittest/ui_extension_test/BUILD.gn @@ -0,0 +1,61 @@ +# 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("//build/test.gni") +import("//foundation/ability/ability_runtime/ability_runtime.gni") + +ohos_unittest("ui_extension_test") { + module_out_path = "ability_runtime/ability_runtime/extension" + + sources = [ "ui_extension_test.cpp" ] + + configs = [] + + include_dirs = [ + "./", + "${ability_runtime_test_path}/mock/frameworks_kits_ability_native_test/include", + "${ability_runtime_path}/interfaces/kits/native/ability/native/ui_extension_ability", + ] + + deps = [ + "${ability_runtime_innerkits_path}/ability_manager:ability_manager", + "${ability_runtime_innerkits_path}/runtime:runtime", + "${ability_runtime_native_path}/ability/native:abilitykit_native", + "${ability_runtime_native_path}/ability/native:abilitykit_utils", + "${ability_runtime_native_path}/ability/native:extensionkit_native", + "${ability_runtime_native_path}/ability/native:ui_extension", + "${ability_runtime_native_path}/appkit:app_context", + "${ability_runtime_native_path}/appkit:appkit_native", + ] + + external_deps = [ + "ability_base:base", + "ability_base:configuration", + "ability_base:want", + "bundle_framework:appexecfwk_base", + "c_utils:utils", + "eventhandler:libeventhandler", + "googletest:gmock_main", + "googletest:gtest_main", + "hilog:libhilog", + "ipc:ipc_core", + "ipc:ipc_napi", + "napi:ace_napi", + ] +} + +group("unittest") { + testonly = true + deps = [ ":ui_extension_test" ] +} diff --git a/test/unittest/ui_extension_test/ui_extension_test.cpp b/test/unittest/ui_extension_test/ui_extension_test.cpp new file mode 100644 index 0000000000..744d72b4da --- /dev/null +++ b/test/unittest/ui_extension_test/ui_extension_test.cpp @@ -0,0 +1,275 @@ +/* + * 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 + +#include "ui_extension_context.h" +#define private public +#define protected public +#include "ui_extension.h" +#undef private +#undef protected + +#include "ability_handler.h" +#include "ability_info.h" +#include "ability_local_record.h" +#include "event_runner.h" +#include "hilog_tag_wrapper.h" +#include "mock_ability_token.h" +#include "ohos_application.h" +#include "want.h" + +namespace OHOS { +namespace AbilityRuntime { +using namespace testing::ext; + +class UIExtensionTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp() override; + void TearDown() override; +}; + +void UIExtensionTest::SetUpTestCase(void) +{} + +void UIExtensionTest::TearDownTestCase(void) +{} + +void UIExtensionTest::SetUp() +{} + +void UIExtensionTest::TearDown() +{} + +/** + * @tc.number: UIExtension_CreateModalUIExtension_0100 + * @tc.name: UIExtension::CreateModalUIExtension + * @tc.desc: context is nullptr, return ERR_INVALID_VALUE. + */ +HWTEST_F(UIExtensionTest, UIExtension_CreateModalUIExtension_0100, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "UIExtension_CreateModalUIExtension_0100 start"); + auto uiExtension = std::make_shared(); + AAFwk::Want want; + int ret = uiExtension->CreateModalUIExtension(want); + EXPECT_EQ(ret, -1); + TAG_LOGI(AAFwkTag::TEST, "UIExtension_CreateModalUIExtension_0100 end"); +} + +/** + * @tc.number: UIExtension_CreateModalUIExtension_0200 + * @tc.name: UIExtension::CreateModalUIExtension + * @tc.desc: handler_ is nullptr, return ERR_INVALID_VALUE. + */ +HWTEST_F(UIExtensionTest, UIExtension_CreateModalUIExtension_0200, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "UIExtension_CreateModalUIExtension_0200 start"); + auto uiExtension = std::make_shared(); + + auto application = std::make_shared(); + std::shared_ptr info = std::make_shared(); + sptr token = new AppExecFwk::MockAbilityToken(); + auto record = std::make_shared(info, token, nullptr, 0); + + std::shared_ptr handler = nullptr; + uiExtension->Init(record, application, handler, token); + + AAFwk::Want want; + int ret = uiExtension->CreateModalUIExtension(want); + EXPECT_EQ(ret, -1); + TAG_LOGI(AAFwkTag::TEST, "UIExtension_CreateModalUIExtension_0200 end"); +} + +/** + * @tc.number: UIExtension_CreateModalUIExtension_0300 + * @tc.name: UIExtension::CreateModalUIExtension + * @tc.desc: Init'd but EventRunner is null, PostTask fails, return ERR_INVALID_VALUE. + */ +HWTEST_F(UIExtensionTest, UIExtension_CreateModalUIExtension_0300, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "UIExtension_CreateModalUIExtension_0300 start"); + auto uiExtension = std::make_shared(); + + auto application = std::make_shared(); + std::shared_ptr info = std::make_shared(); + sptr token = new AppExecFwk::MockAbilityToken(); + auto record = std::make_shared(info, token, nullptr, 0); + + std::shared_ptr runner; + auto handler = std::make_shared(runner); + uiExtension->Init(record, application, handler, token); + + AAFwk::Want want; + int ret = uiExtension->CreateModalUIExtension(want); + EXPECT_EQ(ret, -1); + TAG_LOGI(AAFwkTag::TEST, "UIExtension_CreateModalUIExtension_0300 end"); +} + +/** + * @tc.number: UIExtension_CreateModalUIExtension_0400 + * @tc.name: UIExtension::CreateModalUIExtension + * @tc.desc: Multiple calls with different Want parameters, EventRunner null, PostTask fails. + */ +HWTEST_F(UIExtensionTest, UIExtension_CreateModalUIExtension_0400, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "UIExtension_CreateModalUIExtension_0400 start"); + auto uiExtension = std::make_shared(); + + auto application = std::make_shared(); + std::shared_ptr info = std::make_shared(); + sptr token = new AppExecFwk::MockAbilityToken(); + auto record = std::make_shared(info, token, nullptr, 0); + + std::shared_ptr runner; + auto handler = std::make_shared(runner); + uiExtension->Init(record, application, handler, token); + + // First call with one Want + AAFwk::Want want1; + ElementName element1("device", "com.example.test", "Ability1"); + want1.SetElement(element1); + int ret1 = uiExtension->CreateModalUIExtension(want1); + EXPECT_EQ(ret1, -1); + + // Second call with different Want + AAFwk::Want want2; + ElementName element2("device", "com.example.test", "Ability2"); + want2.SetElement(element2); + int ret2 = uiExtension->CreateModalUIExtension(want2); + EXPECT_EQ(ret2, -1); + + TAG_LOGI(AAFwkTag::TEST, "UIExtension_CreateModalUIExtension_0400 end"); +} + +/** + * @tc.number: UIExtension_CreateModalUIExtension_0500 + * @tc.name: UIExtension::CreateModalUIExtension + * @tc.desc: CreateModalUIExtension with empty Want parameter. + */ +HWTEST_F(UIExtensionTest, UIExtension_CreateModalUIExtension_0500, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "UIExtension_CreateModalUIExtension_0500 start"); + auto uiExtension = std::make_shared(); + + auto application = std::make_shared(); + std::shared_ptr info = std::make_shared(); + sptr token = new AppExecFwk::MockAbilityToken(); + auto record = std::make_shared(info, token, nullptr, 0); + + std::shared_ptr runner; + auto handler = std::make_shared(runner); + uiExtension->Init(record, application, handler, token); + + AAFwk::Want want; // Empty Want + int ret = uiExtension->CreateModalUIExtension(want); + EXPECT_EQ(ret, -1); + + TAG_LOGI(AAFwkTag::TEST, "UIExtension_CreateModalUIExtension_0500 end"); +} + +/** + * @tc.number: UIExtension_CreateModalUIExtension_0600 + * @tc.name: UIExtension::CreateModalUIExtension + * @tc.desc: CreateModalUIExtension with Want containing parameters. + */ +HWTEST_F(UIExtensionTest, UIExtension_CreateModalUIExtension_0600, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "UIExtension_CreateModalUIExtension_0600 start"); + auto uiExtension = std::make_shared(); + + auto application = std::make_shared(); + std::shared_ptr info = std::make_shared(); + sptr token = new AppExecFwk::MockAbilityToken(); + auto record = std::make_shared(info, token, nullptr, 0); + + std::shared_ptr runner; + auto handler = std::make_shared(runner); + uiExtension->Init(record, application, handler, token); + + AAFwk::Want want; + ElementName element("device", "com.example.modal", "ModalExtension"); + want.SetElement(element); + want.SetAction("action.modal.test"); + want.SetFlags(AAFwk::Want::FLAG_ABILITY_CONTINUATION); + + int ret = uiExtension->CreateModalUIExtension(want); + EXPECT_EQ(ret, -1); + + TAG_LOGI(AAFwkTag::TEST, "UIExtension_CreateModalUIExtension_0600 end"); +} + +/** + * @tc.number: UIExtension_CreateModalUIExtension_0700 + * @tc.name: UIExtension::CreateModalUIExtension + * @tc.desc: Verify handler_ is used correctly for task posting. + */ +HWTEST_F(UIExtensionTest, UIExtension_CreateModalUIExtension_0700, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "UIExtension_CreateModalUIExtension_0700 start"); + auto uiExtension = std::make_shared(); + + auto application = std::make_shared(); + std::shared_ptr info = std::make_shared(); + sptr token = new AppExecFwk::MockAbilityToken(); + auto record = std::make_shared(info, token, nullptr, 0); + + std::shared_ptr runner; + auto handler = std::make_shared(runner); + uiExtension->Init(record, application, handler, token); + + // Verify handler_ is set correctly + EXPECT_TRUE(uiExtension->handler_ != nullptr); + + AAFwk::Want want; + int ret = uiExtension->CreateModalUIExtension(want); + EXPECT_EQ(ret, -1); + + TAG_LOGI(AAFwkTag::TEST, "UIExtension_CreateModalUIExtension_0700 end"); +} + +/** + * @tc.number: UIExtension_CreateModalUIExtension_0800 + * @tc.name: UIExtension::CreateModalUIExtension + * @tc.desc: CreateModalUIExtension with different device IDs in Want. + */ +HWTEST_F(UIExtensionTest, UIExtension_CreateModalUIExtension_0800, TestSize.Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "UIExtension_CreateModalUIExtension_0800 start"); + auto uiExtension = std::make_shared(); + + auto application = std::make_shared(); + std::shared_ptr info = std::make_shared(); + sptr token = new AppExecFwk::MockAbilityToken(); + auto record = std::make_shared(info, token, nullptr, 0); + + std::shared_ptr runner; + auto handler = std::make_shared(runner); + uiExtension->Init(record, application, handler, token); + + AAFwk::Want want; + ElementName element("tablet", "com.example.test", "ModalExtension"); + want.SetElement(element); + want.SetDeviceId("tablet_device_001"); + + int ret = uiExtension->CreateModalUIExtension(want); + EXPECT_EQ(ret, -1); + + TAG_LOGI(AAFwkTag::TEST, "UIExtension_CreateModalUIExtension_0800 end"); +} + +} // namespace AbilityRuntime +} // namespace OHOS diff --git a/test/unittest/update_caller_info_util_test/update_caller_info_util_test.cpp b/test/unittest/update_caller_info_util_test/update_caller_info_util_test.cpp index d4c2b104ae..90ff7d0860 100644 --- a/test/unittest/update_caller_info_util_test/update_caller_info_util_test.cpp +++ b/test/unittest/update_caller_info_util_test/update_caller_info_util_test.cpp @@ -37,6 +37,7 @@ namespace AAFwk { constexpr const char *CALLER_REQUEST_CODE = "ohos.extra.param.key.callerRequestCode"; constexpr const char *DMS_CALLER_BUNDLE_NAME = "ohos.dms.param.sourceCallerBundleName"; +const std::string HIDE_SENSITIVE_TYPE = "ohos.media.params.hideSensitiveType"; class UpdateCallerInfoUtilTest : public testing::Test { public: @@ -665,5 +666,43 @@ HWTEST_F(UpdateCallerInfoUtilTest, UpdateCallerAppCloneIndex_0002, TestSize.Leve updateCallerUtil->UpdateCallerAppCloneIndex(want, zeroIndex); EXPECT_EQ(want.GetIntParam(Want::PARAM_RESV_CALLER_APP_CLONE_INDEX, -1), zeroIndex); } + +/** + * @tc.name: UpdateCallerInfoUtilTest_ClearProtectedWantParam_HideSensitiveType_001 + * @tc.desc: Test ClearProtectedWantParam with HIDE_SENSITIVE_TYPE parameter + * @tc.type: FUNC + */ +HWTEST_F(UpdateCallerInfoUtilTest, ClearProtectedWantParam_HideSensitiveType_001, TestSize.Level1) +{ + auto updateCallerUtil = std::make_shared(); + Want want; + want.SetParam(HIDE_SENSITIVE_TYPE, 521); + want.SetElementName("com.test.demo", "entry", "TestAbility"); + + // ClearProtectedWantParam should trigger event for HIDE_SENSITIVE_TYPE + updateCallerUtil->ClearProtectedWantParam(want); + + // Verify the parameter is processed (no exception thrown) + EXPECT_EQ(want.GetIntParam(HIDE_SENSITIVE_TYPE, -1), 521); +} + +/** + * @tc.name: UpdateCallerInfoUtilTest_ClearProtectedWantParam_HideSensitiveType_002 + * @tc.desc: Test ClearProtectedWantParam with HIDE_SENSITIVE_TYPE = 0 + * @tc.type: FUNC + */ +HWTEST_F(UpdateCallerInfoUtilTest, ClearProtectedWantParam_HideSensitiveType_002, TestSize.Level1) +{ + auto updateCallerUtil = std::make_shared(); + Want want; + want.SetParam(HIDE_SENSITIVE_TYPE, 0); + want.SetElementName("com.test.demo", "entry", "TestAbility"); + + // ClearProtectedWantParam should still process the parameter + updateCallerUtil->ClearProtectedWantParam(want); + + // Verify the parameter value + EXPECT_EQ(want.GetIntParam(HIDE_SENSITIVE_TYPE, -1), 0); +} } // namespace AAFwk } // namespace OHOS diff --git a/test/unittest/uri_permission_impl_test/mock/include/mock_storage_manager_service.h b/test/unittest/uri_permission_impl_test/mock/include/mock_storage_manager_service.h index 0ebb70e211..c4e1efee29 100755 --- a/test/unittest/uri_permission_impl_test/mock/include/mock_storage_manager_service.h +++ b/test/unittest/uri_permission_impl_test/mock/include/mock_storage_manager_service.h @@ -480,11 +480,6 @@ public: return E_OK; } - virtual int32_t ClearSecondMountPoint(uint32_t userId, const std::string &bundleName) override - { - return E_OK; - } - virtual int32_t Encrypt(const std::string &volumeId, const std::string &password) override { return E_OK; @@ -560,6 +555,36 @@ public: { return E_OK; } + + virtual int32_t GetPartitionTable(const std::string &diskId, PartitionTableInfo &partitionTableInfo) + { + return E_OK; + } + + int32_t CreatePartition(const std::string &diskId, const PartitionParams &partitionParams) + { + return E_OK; + } + + int32_t DeletePartition(const std::string &diskId, uint32_t partitionNum) + { + return E_OK; + } + + int32_t FormatPartition(const std::string &diskId, uint32_t partitionNum, const FormatParams &formatParams) + { + return E_OK; + } + + virtual int32_t Burn(const std::string &volumeId, const BurnParams ¶ms) override + { + return E_OK; + } + + virtual int32_t VerifyBurnData(const std::string &volumeId, uint32_t verType) override + { + return E_OK; + } }; bool StorageManagerServiceMock::isZero = true; diff --git a/tools/BUILD.gn b/tools/BUILD.gn index 72d6bd5fa9..bce71edd59 100644 --- a/tools/BUILD.gn +++ b/tools/BUILD.gn @@ -15,10 +15,14 @@ import("//build/ohos.gni") import("//foundation/ability/ability_runtime/ability_runtime.gni") group("tools_target") { + deps = [ "aa:tools_aa", + "cc:tools_cc", "ohos-example:ohos-example", "ohos-simple:ohos-simple", "ohos-timer:ohos-timer", + "ohos-aa:tools_ohos_aa", + "ohos-arktsScript:ohos-arkTSScript" ] } diff --git a/tools/aa/include/shell_command.h b/tools/aa/include/shell_command.h index bcf7ed7c72..fc51cc7c28 100644 --- a/tools/aa/include/shell_command.h +++ b/tools/aa/include/shell_command.h @@ -84,6 +84,15 @@ struct AaToolErrorInfo { } return oss.str(); } + + std::string SolutionsToString() const + { + std::ostringstream oss; + for (const auto& solution : solutions) { + oss << solution << "\n"; + } + return oss.str(); + } }; } // namespace AAFwk } // namespace OHOS diff --git a/tools/cc/BUILD.gn b/tools/cc/BUILD.gn new file mode 100644 index 0000000000..04300a9e5f --- /dev/null +++ b/tools/cc/BUILD.gn @@ -0,0 +1,121 @@ +# Copyright (c) 2021-2025 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("//build/ohos/cli/ohos_cli_executable.gni") +import("//foundation/ability/ability_runtime/ability_runtime.gni") + +config("cc_command_config") { + include_dirs = [ + "include", + "${ability_runtime_innerkits_path}/ability_manager/include", + "${ability_runtime_innerkits_path}/app_manager/include/appmgr", + "${ability_runtime_services_path}/abilitymgr/include", + ] +} + +config("cc_command_exception_config") { + cflags_cc = [ "-fexceptions" ] +} + +ohos_static_library("tools_cc_source_set") { + sources = [ + "src/cc_command.cpp", + "src/cc_param_parser.cpp", + "${ability_runtime_path}/tools/aa/src/shell_command.cpp", + ] + + sanitize = { + integer_overflow = true + ubsan = true + boundary_sanitize = true + cfi = true + cfi_cross_dso = true + debug = false + } + + public_configs = [ + ":cc_command_config", + ":cc_command_exception_config", + "${ability_runtime_services_path}/abilitymgr:abilityms_config", + "${ability_runtime_services_path}/common:common_config", + ] + + cflags = [] + if (target_cpu == "arm") { + cflags += [ "-DBINDER_IPC_32BIT" ] + } + + deps = [ + "${ability_runtime_innerkits_path}/ability_manager:ability_manager", + "${ability_runtime_abilitymgr_path}/:abilityms", + ] + + external_deps = [ + "ability_base:base", + "ability_base:want", + "c_utils:utils", + "hilog:libhilog", + "ipc:ipc_core", + "json:nlohmann_json_static", + "samgr:samgr_proxy", + ] + + subsystem_name = "ability" + part_name = "ability_runtime" +} + +ohos_cli_executable("ohos-claw-cc") { + sources = [ "src/main.cpp" ] + + sanitize = { + integer_overflow = true + ubsan = true + boundary_sanitize = true + cfi = true + cfi_cross_dso = true + debug = false + } + + cflags = [] + if (target_cpu == "arm") { + cflags += [ "-DBINDER_IPC_32BIT" ] + } + + deps = [ + ":tools_cc_source_set", + "${ability_runtime_innerkits_path}/ability_manager:ability_manager", + ] + + external_deps = [ + "ability_base:base", + "c_utils:utils", + "hilog:libhilog", + "ipc:ipc_core", + ] + + defines = [] + + install_enable = true + install_images = [ "system" ] + cli_config_file = "ohos-claw-cc.json" + + subsystem_name = "ability" + part_name = "ability_runtime" +} + +group("tools_cc") { + deps = [ + ":ohos-claw-cc", + ] +} diff --git a/tools/cc/include/cc_command.h b/tools/cc/include/cc_command.h new file mode 100644 index 0000000000..a636712957 --- /dev/null +++ b/tools/cc/include/cc_command.h @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2021-2025 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_CC_COMMAND_H +#define OHOS_ABILITY_RUNTIME_CC_COMMAND_H + +#include + +#include "ability_manager_interface.h" +#include "cc_param_parser.h" +#include "insight_intent/insight_intent_info_for_query.h" +#include "shell_command.h" + +namespace OHOS { +namespace AAFwk { +namespace { +const std::string TOOL_NAME = "ohos-claw-cc"; + +const std::string HELP_MSG = + "ohos-claw-cc - InsightIntent framework CLI tool for executing and querying insight intents\n" + "\n" + "Usage:\n" + " ohos-claw-cc [options]\n" + "\n" + "Parameters:\n" + " --help Display this help message\n" + "\n" + "SubCommands:\n" + " execute-intent Execute an insight intent and return the result\n" + " get-intent Query insight intent registration information\n" + "\n" + "Examples:\n" + " ohos-claw-cc execute-intent --bundleName com.example --moduleName entry --intentName MyIntent\n" + " ohos-claw-cc get-intent --executeMode 1 --flag 1\n"; + +const std::string HELP_MSG_EXECUTE_INTENT = + "ohos-claw-cc execute-intent - Execute an insight intent synchronously and return the result\n" + "\n" + "Usage:\n" + " ohos-claw-cc execute-intent [options]\n" + "\n" + "Parameters:\n" + " --bundleName Target application bundle name (required)\n" + " --moduleName Module name within the bundle (required)\n" + " --intentName Insight intent name to execute (required)\n" + " --abilityName Ability name (required, pass empty string '' if not needed)\n" + " --executeMode Execute mode (required, range: 0-3)\n" + " 0=UI_ABILITY_FOREGROUND, 1=UI_ABILITY_BACKGROUND,\n" + " 2=UI_EXTENSION_ABILITY, 3=SERVICE_EXTENSION_ABILITY\n" + " --param Intent parameters as JSON string (required, pass '{}' if not needed)\n" + " For link-type intents, include \"uri\" field in JSON, e.g.\n" + " '{\"uri\":\"https://example.com/page\",\"key\":\"value\"}'\n" + " --help Display this help message\n" + "\n" + "Examples:\n" + " ohos-claw-cc execute-intent --bundleName com.example --moduleName entry --intentName MyIntent\n" + "\n" + " ohos-claw-cc execute-intent --bundleName com.example --moduleName entry\n" + " --intentName MyIntent --executeMode 0 --param '{\"key1\":\"value1\"}'\n"; + +const std::string HELP_MSG_GET_INTENT = + "ohos-claw-cc get-intent - Query insight intent registration information\n" + "\n" + "Usage:\n" + " ohos-claw-cc get-intent [options]\n" + "\n" + "Parameters:\n" + " --executeMode Query mode (required, values: 1-3)\n" + " 1=query all, 2=query by bundle, 3=query by intent name\n" + " --flag Query flag (required, values: 1, 2, 5, 6)\n" + " 1=GET_FULL_INSIGHT_INTENT, 2=GET_SUMMARY_INSIGHT_INTENT,\n" + " 5=FULL+ENTITY_INFO, 6=SUMMARY+ENTITY_INFO\n" + " --bundleName Bundle name (required when executeMode is 2 or 3)\n" + " --moduleName Module name (required when executeMode is 3)\n" + " --intentName Intent name (required when executeMode is 3)\n" + " --help Display this help message\n" + "\n" + "Examples:\n" + " ohos-claw-cc get-intent --executeMode 1 --flag 1\n" + "\n" + " ohos-claw-cc get-intent --executeMode 2 --flag 1 --bundleName com.example.bundle\n" + "\n" + " ohos-claw-cc get-intent --executeMode 3 --flag 1\n" + " --bundleName com.example.bundle --moduleName entry --intentName MyIntent\n"; +} // namespace + +class InsightIntentShellCommand : public ShellCommand { +public: + InsightIntentShellCommand(int argc, char* argv[]); + ~InsightIntentShellCommand() override = default; + + ErrCode CreateMessageMap() override; + +private: + ErrCode CreateCommandMap() override; + ErrCode init() override; + + ErrCode RunAsHelpCommand(); + ErrCode RunAsExecuteIntentCommand(); + ErrCode RunAsGetIntentCommand(); + + ErrCode ParseExecuteIntentOptions(std::string &bundleName, std::string &moduleName, + std::string &abilityName, std::string &insightIntentName, + std::string &intentParamJson, int32_t &executeMode); + ErrCode HandleExecuteIntentOption(int option, std::string &bundleName, + std::string &moduleName, std::string &abilityName, + std::string &insightIntentName, std::string &intentParamJson, + int32_t &executeMode); + ErrCode CheckRequiredExecuteParams(const std::string &bundleName, + const std::string &moduleName, + const std::string &insightIntentName); + ErrCode CheckAllExecuteParams(const std::string &bundleName, + const std::string &moduleName, + const std::string &insightIntentName, + const std::string &abilityName, int32_t executeMode, + const std::string &intentParamJson); + ErrCode ValidateGetIntentParams(int32_t flag, int32_t executeMode); + ErrCode ValidateIntentFromDatabase(const std::string &bundleName, + const std::string &moduleName, + const std::string &insightIntentName, + AbilityRuntime::InsightIntentInfoForQuery &queryInfo); + int8_t ConvertIntentTypeToDecoratorType(const std::string &intentType); + void BuildExecuteParam(InsightIntentExecuteParam ¶m, + const std::string &bundleName, const std::string &moduleName, + const std::string &abilityName, const std::string &insightIntentName, + int32_t executeMode, const std::string &intentType, + const std::string &intentParamJson); + ErrCode ExecuteIntentWithParam(const InsightIntentExecuteParam ¶m); + void AppendExecuteResult(const InsightIntentExecuteResult &executeResult); + + ErrCode ParseGetIntentOptions(int32_t &flag, int32_t &executeMode, + std::string &bundleName, + std::string &moduleName, std::string &intentName); + ErrCode HandleGetIntentOption(int option, int32_t &flag, + int32_t &executeMode, std::string &bundleName, + std::string &moduleName, std::string &intentName); + ErrCode DispatchGetIntentMode(int32_t executeMode, int32_t flag, + const std::string &bundleName, + const std::string &moduleName, const std::string &intentName); + ErrCode RunGetIntentModeAll(int32_t flag); + ErrCode RunGetIntentModeByBundle(int32_t flag, + const std::string &bundleName); + ErrCode RunGetIntentModeByIntent(int32_t flag, + const std::string &bundleName, const std::string &moduleName, + const std::string &intentName); + + ErrCode ParseIntOption(const char *arg, int32_t &value); + bool ShouldAppendEntityInfo(int32_t flag) const; + bool IsFullInfo(int32_t flag) const; + ErrCode ValidateExecuteMode(int32_t executeMode); + + void AppendResultEvent(const nlohmann::json &data); + void AppendErrorEvent(const std::string &errCode, + const std::string &errMsg, const std::string &suggestion); + nlohmann::json BuildIntentInfoListJson( + const std::vector &infos, + int32_t flag); + nlohmann::json BuildIntentInfoJson( + const AbilityRuntime::InsightIntentInfoForQuery &info, int32_t flag); + nlohmann::json BuildEntityJson( + const AbilityRuntime::EntityInfoForQuery &entity); + nlohmann::json CollectSupportedModesJson( + const AbilityRuntime::InsightIntentInfoForQuery &queryInfo); +}; +} // namespace AAFwk +} // namespace OHOS + +#endif // OHOS_ABILITY_RUNTIME_CC_COMMAND_H diff --git a/tools/cc/include/cc_param_parser.h b/tools/cc/include/cc_param_parser.h new file mode 100644 index 0000000000..ec94fb07a2 --- /dev/null +++ b/tools/cc/include/cc_param_parser.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2021-2025 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_CC_PARAM_PARSER_H +#define OHOS_ABILITY_RUNTIME_CC_PARAM_PARSER_H + +#include +#include "want_params.h" +#include +#include "array_wrapper.h" + +namespace OHOS { +namespace AAFwk { + +class CcParamParser { +public: + CcParamParser() = default; + ~CcParamParser() = default; + + static WantParams BuildWantParamsFromJson(const std::string &jsonStr); + +private: + static WantParams ParseJsonObjectToWantParams( + const nlohmann::json &jsonObj); + static sptr ParseJsonArrayToIArray( + const nlohmann::json &jsonArr); + static InterfaceID DetectArrayInterfaceType( + const sptr &firstItem); +}; + +} // namespace AAFwk +} // namespace OHOS + +#endif // OHOS_ABILITY_RUNTIME_CC_PARAM_PARSER_H diff --git a/tools/cc/ohos-claw-cc.json b/tools/cc/ohos-claw-cc.json new file mode 100644 index 0000000000..216f69ce02 --- /dev/null +++ b/tools/cc/ohos-claw-cc.json @@ -0,0 +1,203 @@ +{ + "name": "ohos-claw-cc", + "version": "1.0.0", + "description": "ohos-claw-cc - InsightIntent framework CLI tool for executing and querying InsightIntents.", + "executablePath": "/system/bin/cli_tool/executable/ohos-claw-cc", + "requirePermissions": [], + "inputSchema": { + "type": "object", + "description": "Sub-options of the ohos-claw-cc command.", + "properties": { + "help": { + "type": "boolean", + "default": false + } + } + }, + "outputSchema": { + "type": "object", + "description": "Execution result of the ohos-claw-cc command.", + "properties": { + "type": { + "type": "string", + "enum": ["result"] + }, + "status": { + "type": "string", + "enum": ["success", "failed"] + }, + "data": { + "type": "object", + "properties": { + "message": { + "type": "string" + } + } + }, + "errCode": { + "type": "string" + }, + "errMsg": { + "type": "string" + }, + "suggestion": { + "type": "string" + } + } + }, + "hasSubCommand": true, + "subcommands": { + "execute-intent": { + "description": "ohos-claw-cc execute-intent - Execute an InsightIntent synchronously and return the execution result.", + "requirePermissions": [], + "inputSchema": { + "type": "object", + "description": "Sub-options of the ohos-claw-cc execute-intent command.", + "properties": { + "help": { + "type": "boolean", + "default": false + }, + "bundleName": { + "type": "string", + "description": "Target application bundle name" + }, + "moduleName": { + "type": "string", + "description": "Module name within the bundle" + }, + "intentName": { + "type": "string", + "description": "InsightIntent name to execute" + }, + "abilityName": { + "type": "string", + "description": "Ability name" + }, + "executeMode": { + "type": "integer", + "description": "Execute mode: 0=UI_ABILITY_FOREGROUND, 1=UI_ABILITY_BACKGROUND, 2=UI_EXTENSION_ABILITY, 3=SERVICE_EXTENSION_ABILITY", + "minimum": 0, + "maximum": 3 + }, + "param": { + "type": "string", + "description": "InsightIntent parameters as a JSON object string. Pass '{}' if not needed." + } + }, + "required": [ + "bundleName", + "moduleName", + "intentName", + "abilityName", + "executeMode", + "param" + ] + }, + "outputSchema": { + "type": "object", + "description": "Execution result of the ohos-claw-cc execute-intent command.", + "properties": { + "type": { + "type": "string", + "enum": ["result"] + }, + "status": { + "type": "string", + "enum": ["success", "failed"] + }, + "data": { + "type": "object", + "properties": { + "message": { + "type": "string" + } + } + }, + "errCode": { + "type": "string" + }, + "errMsg": { + "type": "string" + }, + "suggestion": { + "type": "string" + } + } + } + }, + "get-intent": { + "description": "ohos-claw-cc get-intent - Query InsightIntent registration information with three query modes.", + "requirePermissions": [], + "inputSchema": { + "type": "object", + "description": "Sub-options of the ohos-claw-cc get-intent command.", + "properties": { + "help": { + "type": "boolean", + "default": false + }, + "executeMode": { + "type": "integer", + "description": "Query mode: 1=query all, 2=query by bundle, 3=query by InsightIntent name", + "minimum": 1, + "maximum": 3 + }, + "flag": { + "type": "integer", + "description": "Query flag (required, values: 1=GET_FULL_INSIGHT_INTENT, 2=GET_SUMMARY_INSIGHT_INTENT, 5=FULL+ENTITY_INFO, 6=SUMMARY+ENTITY_INFO)", + "enum": [1, 2, 5, 6] + }, + "bundleName": { + "type": "string", + "description": "Bundle name (required when executeMode is 2 or 3)" + }, + "moduleName": { + "type": "string", + "description": "Module name (required when executeMode is 3)" + }, + "intentName": { + "type": "string", + "description": "InsightIntent name (required when executeMode is 3)" + } + }, + "required": [ + "executeMode", + "flag" + ] + }, + "outputSchema": { + "type": "object", + "description": "Execution result of the ohos-claw-cc get-intent command.", + "properties": { + "type": { + "type": "string", + "enum": ["result"] + }, + "status": { + "type": "string", + "enum": ["success", "failed"] + }, + "data": { + "type": "object", + "properties": { + "message": { + "type": "string" + } + } + }, + "errCode": { + "type": "string" + }, + "errMsg": { + "type": "string" + }, + "suggestion": { + "type": "string" + } + } + } + } + }, + "eventSchemas": {} +} diff --git a/tools/cc/src/cc_command.cpp b/tools/cc/src/cc_command.cpp new file mode 100644 index 0000000000..6041571423 --- /dev/null +++ b/tools/cc/src/cc_command.cpp @@ -0,0 +1,861 @@ +/* + * Copyright (c) 2021-2025 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 "cc_command.h" + +#include +#include +#include +#include +#include + +#include + +#include "ability_manager_client.h" +#include "hilog_tag_wrapper.h" +#include "insight_intent/insight_intent_constant.h" + +namespace OHOS { +namespace AAFwk { +namespace { +constexpr size_t QUOTATION_MARK_PAIR_LEN = 2; +constexpr int32_t MAX_PARSE_COUNT = 256; +constexpr int32_t DEFAULT_USER_ID = 100; +constexpr int32_t QUERY_MODE_ALL = 1; +constexpr int32_t QUERY_MODE_BY_BUNDLE = 2; +constexpr int32_t QUERY_MODE_BY_INTENT = 3; + +const std::string SHORT_OPTIONS_EXECUTE_INTENT = "hb:m:a:i:e:p:"; +constexpr struct option LONG_OPTIONS_EXECUTE_INTENT[] = { + {"help", no_argument, nullptr, 'h'}, + {"bundle", required_argument, nullptr, 'b'}, + {"bundleName", required_argument, nullptr, 'b'}, + {"module", required_argument, nullptr, 'm'}, + {"moduleName", required_argument, nullptr, 'm'}, + {"ability", required_argument, nullptr, 'a'}, + {"abilityName", required_argument, nullptr, 'a'}, + {"intent", required_argument, nullptr, 'i'}, + {"intentName", required_argument, nullptr, 'i'}, + {"execute-mode", required_argument, nullptr, 'e'}, + {"executeMode", required_argument, nullptr, 'e'}, + {"param", required_argument, nullptr, 'p'}, + {nullptr, 0, nullptr, 0}, +}; + +const std::string SHORT_OPTIONS_GET_INTENT = "he:f:b:m:i:"; +constexpr struct option LONG_OPTIONS_GET_INTENT[] = { + {"help", no_argument, nullptr, 'h'}, + {"execute-mode", required_argument, nullptr, 'e'}, + {"executeMode", required_argument, nullptr, 'e'}, + {"flag", required_argument, nullptr, 'f'}, + {"bundle", required_argument, nullptr, 'b'}, + {"bundleName", required_argument, nullptr, 'b'}, + {"module", required_argument, nullptr, 'm'}, + {"moduleName", required_argument, nullptr, 'm'}, + {"intent", required_argument, nullptr, 'i'}, + {"intentName", required_argument, nullptr, 'i'}, + {nullptr, 0, nullptr, 0}, +}; +void StripQuotationMarks(std::string &str) +{ + if (str.size() >= QUOTATION_MARK_PAIR_LEN && + str.front() == '\'' && str.back() == '\'') { + str = str.substr(1, str.size() - QUOTATION_MARK_PAIR_LEN); + } +} +} // namespace + +using ErrCode = OHOS::ErrCode; + +InsightIntentShellCommand::InsightIntentShellCommand(int argc, char* argv[]) + : ShellCommand(argc, argv, TOOL_NAME) +{ + for (int i = 0; i < argc_; i++) { + TAG_LOGI(AAFwkTag::CC_TOOL, "argv_[%{public}d]: %{public}s", i, argv_[i]); + } +} + +ErrCode InsightIntentShellCommand::CreateCommandMap() +{ + commandMap_ = { + {"help", [this]() { return this->RunAsHelpCommand(); }}, + {"-h", [this]() { return this->RunAsHelpCommand(); }}, + {"--help", [this]() { return this->RunAsHelpCommand(); }}, + {"execute-intent", + [this]() { return this->RunAsExecuteIntentCommand(); }}, + {"get-intent", [this]() { return this->RunAsGetIntentCommand(); }}, + }; + + return OHOS::ERR_OK; +} + +ErrCode InsightIntentShellCommand::CreateMessageMap() +{ + return OHOS::ERR_OK; +} + +ErrCode InsightIntentShellCommand::init() +{ + return AbilityManagerClient::GetInstance()->Connect(); +} + +ErrCode InsightIntentShellCommand::RunAsHelpCommand() +{ + resultReceiver_.append(HELP_MSG); + return OHOS::ERR_OK; +} + +ErrCode InsightIntentShellCommand::RunAsExecuteIntentCommand() +{ + TAG_LOGI(AAFwkTag::CC_TOOL, "execute-intent command called"); + + std::string bundleName; + std::string moduleName; + std::string abilityName; + std::string insightIntentName; + std::string intentParamJson; + int32_t executeMode = -1; + + auto result = ParseExecuteIntentOptions(bundleName, moduleName, abilityName, + insightIntentName, intentParamJson, executeMode); + if (result != OHOS::ERR_OK) { + return result; + } + + result = ValidateExecuteMode(executeMode); + if (result != OHOS::ERR_OK) { + return result; + } + + AbilityRuntime::InsightIntentInfoForQuery queryInfo; + result = ValidateIntentFromDatabase(bundleName, moduleName, + insightIntentName, queryInfo); + if (result != OHOS::ERR_OK) { + return result; + } + + InsightIntentExecuteParam param; + BuildExecuteParam(param, bundleName, moduleName, abilityName, + insightIntentName, executeMode, queryInfo.intentType, intentParamJson); + return ExecuteIntentWithParam(param); +} + +void InsightIntentShellCommand::BuildExecuteParam( + InsightIntentExecuteParam ¶m, + const std::string &bundleName, const std::string &moduleName, + const std::string &abilityName, const std::string &insightIntentName, + int32_t executeMode, const std::string &intentType, + const std::string &intentParamJson) +{ + param.bundleName_ = bundleName; + param.moduleName_ = moduleName; + param.abilityName_ = abilityName; + param.insightIntentName_ = insightIntentName; + param.executeMode_ = executeMode; + param.userId_ = DEFAULT_USER_ID; + param.decoratorType_ = ConvertIntentTypeToDecoratorType(intentType); + if (intentParamJson.empty()) { + param.insightIntentParam_ = std::make_shared(); + return; + } + try { + auto jsonObj = nlohmann::json::parse(intentParamJson); + if (jsonObj.contains("uri") && jsonObj["uri"].is_string()) { + param.uris_.push_back(jsonObj["uri"].get()); + jsonObj.erase("uri"); + } + param.insightIntentParam_ = std::make_shared( + CcParamParser::BuildWantParamsFromJson(jsonObj.dump())); + } catch (const nlohmann::json::exception &e) { + TAG_LOGE(AAFwkTag::CC_TOOL, "parse param json failed: %{public}s", e.what()); + param.insightIntentParam_ = std::make_shared( + CcParamParser::BuildWantParamsFromJson(intentParamJson)); + } +} + +ErrCode InsightIntentShellCommand::ParseExecuteIntentOptions( + std::string &bundleName, std::string &moduleName, + std::string &abilityName, std::string &insightIntentName, + std::string &intentParamJson, int32_t &executeMode) +{ + int option = -1; + int counter = 0; + + while (counter < MAX_PARSE_COUNT) { + counter++; + option = getopt_long(argc_, argv_, SHORT_OPTIONS_EXECUTE_INTENT.c_str(), + LONG_OPTIONS_EXECUTE_INTENT, nullptr); + + TAG_LOGI(AAFwkTag::CC_TOOL, + "option: %{public}d, optopt: %{public}d, optind: %{public}d", + option, optopt, optind); + + if (optind < 0 || optind > argc_) { + AppendErrorEvent("ERR_ARG_MISSING", + "Missing required parameters for execute-intent.", + "Please provide --bundleName, --moduleName and --intentName."); + return OHOS::ERR_INVALID_VALUE; + } + + if (option == -1) { + if (counter == 1 && optind < argc_ && + strcmp(argv_[optind], cmd_.c_str()) == 0) { + AppendErrorEvent("ERR_ARG_MISSING", + "No options specified for execute-intent.", + "Run 'ohos-claw-cc execute-intent --help' for usage."); + } + break; + } + + if (option == '?') { + TAG_LOGI(AAFwkTag::CC_TOOL, "'ohos-claw-cc execute-intent' option unknown"); + AppendErrorEvent("ERR_ARG_INVALID", + "Unknown option for execute-intent.", + "Run 'ohos-claw-cc execute-intent --help' for valid options."); + return OHOS::ERR_INVALID_VALUE; + } + + if (HandleExecuteIntentOption(option, bundleName, moduleName, abilityName, + insightIntentName, intentParamJson, executeMode) != OHOS::ERR_OK) { + return OHOS::ERR_INVALID_VALUE; + } + } + + return CheckAllExecuteParams(bundleName, moduleName, + insightIntentName, abilityName, executeMode, intentParamJson); +} + +ErrCode InsightIntentShellCommand::CheckRequiredExecuteParams( + const std::string &bundleName, const std::string &moduleName, + const std::string &insightIntentName) +{ + if (bundleName.empty() || moduleName.empty() || + insightIntentName.empty()) { + TAG_LOGE(AAFwkTag::CC_TOOL, "missing required parameters"); + AppendErrorEvent("ERR_ARG_MISSING", + "Missing required parameters for execute-intent.", + "Please provide --bundleName, --moduleName and --intentName."); + return OHOS::ERR_INVALID_VALUE; + } + return OHOS::ERR_OK; +} + +ErrCode InsightIntentShellCommand::CheckAllExecuteParams( + const std::string &bundleName, const std::string &moduleName, + const std::string &insightIntentName, const std::string &abilityName, + int32_t executeMode, const std::string &intentParamJson) +{ + if (bundleName.empty() || moduleName.empty() || + insightIntentName.empty() || executeMode < 0 || + intentParamJson.empty()) { + TAG_LOGE(AAFwkTag::CC_TOOL, "missing required parameters"); + AppendErrorEvent("ERR_ARG_MISSING", + "Missing required parameters for execute-intent.", + "Please provide --bundleName, --moduleName, --intentName, " + "and --param. " + "Pass empty string '' for --abilityName or '{}' for --param " + "if not needed."); + return OHOS::ERR_INVALID_VALUE; + } + return OHOS::ERR_OK; +} + +ErrCode InsightIntentShellCommand::HandleExecuteIntentOption( + int option, std::string &bundleName, std::string &moduleName, + std::string &abilityName, std::string &insightIntentName, + std::string &intentParamJson, int32_t &executeMode) +{ + switch (option) { + case 'h': + resultReceiver_.append(HELP_MSG_EXECUTE_INTENT + "\n"); + return OHOS::ERR_INVALID_VALUE; + case 'b': + bundleName = optarg; + TAG_LOGI(AAFwkTag::CC_TOOL, "bundleName: %{public}s", bundleName.c_str()); + break; + case 'm': + moduleName = optarg; + TAG_LOGI(AAFwkTag::CC_TOOL, "moduleName: %{public}s", moduleName.c_str()); + break; + case 'a': + abilityName = optarg; + TAG_LOGI(AAFwkTag::CC_TOOL, "abilityName: %{public}s", abilityName.c_str()); + break; + case 'i': + insightIntentName = optarg; + TAG_LOGI(AAFwkTag::CC_TOOL, + "insightIntentName: %{public}s", insightIntentName.c_str()); + break; + case 'e': + if (ParseIntOption(optarg, executeMode) != OHOS::ERR_OK) { + AppendErrorEvent("ERR_ARG_INVALID", + "Invalid executeMode value for execute-intent.", + "ExecuteMode must be an integer 0-3."); + return OHOS::ERR_INVALID_VALUE; + } + break; + case 'p': + intentParamJson = optarg; + StripQuotationMarks(intentParamJson); + TAG_LOGI(AAFwkTag::CC_TOOL, + "intentParamJson: %{public}s", intentParamJson.c_str()); + break; + default: + break; + } + return OHOS::ERR_OK; +} + +ErrCode InsightIntentShellCommand::ValidateIntentFromDatabase( + const std::string &bundleName, const std::string &moduleName, + const std::string &insightIntentName, + AbilityRuntime::InsightIntentInfoForQuery &queryInfo) +{ + auto err = AbilityManagerClient::GetInstance()->GetInsightIntentInfoByIntentName( + AbilityRuntime::GetInsightIntentFlag::GET_FULL_INSIGHT_INTENT, + bundleName, moduleName, insightIntentName, queryInfo, DEFAULT_USER_ID); + if (err != OHOS::ERR_OK || queryInfo.intentName.empty()) { + TAG_LOGE(AAFwkTag::CC_TOOL, + "intent not found: %{public}s, err: %{public}d", + insightIntentName.c_str(), err); + AppendErrorEvent("ERR_RESOURCE_NOT_FOUND", + "Intent not found in database: " + insightIntentName, + "Check --bundleName, --moduleName and --intentName."); + return OHOS::ERR_INVALID_VALUE; + } + + TAG_LOGI(AAFwkTag::CC_TOOL, + "intent validated: %{public}s, intentType: %{public}s", + insightIntentName.c_str(), queryInfo.intentType.c_str()); + return OHOS::ERR_OK; +} + +int8_t InsightIntentShellCommand::ConvertIntentTypeToDecoratorType( + const std::string &intentType) +{ + static const std::unordered_map mapping = { + {"@InsightIntentLink", 1}, + {"@InsightIntentPage", 2}, + {"@InsightIntentFunctionMethod", 3}, + {"@InsightIntentForm", 4}, + {"@InsightIntentEntry", 5}, + }; + auto it = mapping.find(intentType); + if (it != mapping.end()) { + return it->second; + } + return 0; +} + +ErrCode InsightIntentShellCommand::ExecuteIntentWithParam( + const InsightIntentExecuteParam ¶m) +{ + TAG_LOGI(AAFwkTag::CC_TOOL, + "Executing insight intent: bundle=%{public}s, module=%{public}s, " + "ability=%{public}s, intent=%{public}s, mode=%{public}d", + param.bundleName_.c_str(), param.moduleName_.c_str(), + param.abilityName_.c_str(), param.insightIntentName_.c_str(), + param.executeMode_); + + InsightIntentExecuteResult executeResult; + auto err = AbilityManagerClient::GetInstance()->ExecuteIntentWithResult( + const_cast(param), executeResult, 30000); + if (err == OHOS::ERR_OK) { + TAG_LOGI(AAFwkTag::CC_TOOL, + "execute intent successfully, innerErr=%{public}d, code=%{public}d", + executeResult.innerErr, executeResult.code); + AppendExecuteResult(executeResult); + } else { + TAG_LOGE(AAFwkTag::CC_TOOL, "execute intent failed: %{public}d", err); + AppendErrorEvent("ERR_INTERNAL_ERROR", + "Failed to execute intent: " + GetMessageFromCode(err), + "Check if the target application is installed and the intent is registered."); + } + + return err; +} + +void InsightIntentShellCommand::AppendExecuteResult( + const InsightIntentExecuteResult &executeResult) +{ + nlohmann::json data; + data["innerErr"] = executeResult.innerErr; + data["code"] = executeResult.code; + data["flags"] = executeResult.flags; + + if (executeResult.result != nullptr) { + data["result"] = executeResult.result->ToString(); + } + if (!executeResult.uris.empty()) { + data["uris"] = executeResult.uris; + } + + AppendResultEvent(data); +} + +ErrCode InsightIntentShellCommand::RunAsGetIntentCommand() +{ + TAG_LOGI(AAFwkTag::CC_TOOL, "get-intent command called"); + + int32_t flag = -1; + int32_t executeMode = -1; + std::string bundleName; + std::string moduleName; + std::string intentName; + + auto result = ParseGetIntentOptions(flag, executeMode, + bundleName, moduleName, intentName); + if (result != OHOS::ERR_OK) { + return result; + } + + return DispatchGetIntentMode(executeMode, flag, + bundleName, moduleName, intentName); +} + +ErrCode InsightIntentShellCommand::ParseGetIntentOptions( + int32_t &flag, int32_t &executeMode, + std::string &bundleName, std::string &moduleName, + std::string &intentName) +{ + int option = -1; + int counter = 0; + + while (counter < MAX_PARSE_COUNT) { + counter++; + option = getopt_long(argc_, argv_, SHORT_OPTIONS_GET_INTENT.c_str(), + LONG_OPTIONS_GET_INTENT, nullptr); + + TAG_LOGI(AAFwkTag::CC_TOOL, + "option: %{public}d, optopt: %{public}d, optind: %{public}d", + option, optopt, optind); + + if (optind < 0 || optind > argc_) { + AppendErrorEvent("ERR_ARG_MISSING", + "Missing required parameters for get-intent.", + "Please provide --executeMode and --flag."); + return OHOS::ERR_INVALID_VALUE; + } + + if (option == -1) { + if (counter == 1 && optind < argc_ && + strcmp(argv_[optind], cmd_.c_str()) == 0) { + AppendErrorEvent("ERR_ARG_MISSING", + "No options specified for get-intent.", + "Run 'ohos-claw-cc get-intent --help' for usage."); + } + break; + } + + if (option == '?') { + TAG_LOGI(AAFwkTag::CC_TOOL, "'ohos-claw-cc get-intent' option unknown"); + AppendErrorEvent("ERR_ARG_INVALID", + "Unknown option for get-intent.", + "Run 'ohos-claw-cc get-intent --help' for valid options."); + return OHOS::ERR_INVALID_VALUE; + } + + if (HandleGetIntentOption(option, flag, executeMode, + bundleName, moduleName, intentName) != OHOS::ERR_OK) { + return OHOS::ERR_INVALID_VALUE; + } + } + + return ValidateGetIntentParams(flag, executeMode); +} + +ErrCode InsightIntentShellCommand::ValidateGetIntentParams( + int32_t flag, int32_t executeMode) +{ + if (executeMode < 0 || flag < 0) { + TAG_LOGE(AAFwkTag::CC_TOOL, "missing required parameters -e or -f"); + AppendErrorEvent("ERR_ARG_MISSING", + "Missing required parameters --executeMode and --flag.", + "Please provide --executeMode (1-3) and --flag (1, 2, 5 or 6)."); + return OHOS::ERR_INVALID_VALUE; + } + + if (flag != AbilityRuntime::GetInsightIntentFlag::GET_FULL_INSIGHT_INTENT && + flag != AbilityRuntime::GetInsightIntentFlag::GET_SUMMARY_INSIGHT_INTENT && + flag != AbilityRuntime::GetInsightIntentFlag::GET_FULL_INSIGHT_INTENT_ENTITY && + flag != AbilityRuntime::GetInsightIntentFlag::GET_SUMMARY_INSIGHT_INTENT_ENTITY) { + TAG_LOGE(AAFwkTag::CC_TOOL, "invalid flag: %{public}d", flag); + AppendErrorEvent("ERR_ARG_INVALID", + "Invalid flag value: " + std::to_string(flag), + "Flag must be one of: 1 (GET_FULL_INSIGHT_INTENT), " + "2 (GET_SUMMARY_INSIGHT_INTENT), " + "5 (FULL + ENTITY_INFO), 6 (SUMMARY + ENTITY_INFO)."); + return OHOS::ERR_INVALID_VALUE; + } + + return OHOS::ERR_OK; +} + +ErrCode InsightIntentShellCommand::HandleGetIntentOption( + int option, int32_t &flag, int32_t &executeMode, + std::string &bundleName, std::string &moduleName, + std::string &intentName) +{ + switch (option) { + case 'h': + resultReceiver_.append(HELP_MSG_GET_INTENT + "\n"); + return OHOS::ERR_INVALID_VALUE; + case 'e': + if (ParseIntOption(optarg, executeMode) != OHOS::ERR_OK) { + AppendErrorEvent("ERR_ARG_INVALID", + "Invalid executeMode value for get-intent.", + "ExecuteMode must be an integer 1-3."); + return OHOS::ERR_INVALID_VALUE; + } + TAG_LOGI(AAFwkTag::CC_TOOL, "executeMode: %{public}d", executeMode); + break; + case 'f': + if (ParseIntOption(optarg, flag) != OHOS::ERR_OK) { + AppendErrorEvent("ERR_ARG_INVALID", + "Invalid flag value for get-intent.", + "Flag must be an integer 1-7."); + return OHOS::ERR_INVALID_VALUE; + } + TAG_LOGI(AAFwkTag::CC_TOOL, "flag: %{public}d", flag); + break; + case 'b': + bundleName = optarg; + TAG_LOGI(AAFwkTag::CC_TOOL, + "bundleName: %{public}s", bundleName.c_str()); + break; + case 'm': + moduleName = optarg; + TAG_LOGI(AAFwkTag::CC_TOOL, + "moduleName: %{public}s", moduleName.c_str()); + break; + case 'i': + intentName = optarg; + TAG_LOGI(AAFwkTag::CC_TOOL, + "intentName: %{public}s", intentName.c_str()); + break; + default: + break; + } + return OHOS::ERR_OK; +} + +ErrCode InsightIntentShellCommand::DispatchGetIntentMode( + int32_t executeMode, int32_t flag, + const std::string &bundleName, const std::string &moduleName, + const std::string &intentName) +{ + if (executeMode == QUERY_MODE_ALL) { + return RunGetIntentModeAll(flag); + } else if (executeMode == QUERY_MODE_BY_BUNDLE) { + if (bundleName.empty()) { + TAG_LOGE(AAFwkTag::CC_TOOL, "mode 2 requires -b bundleName"); + AppendErrorEvent("ERR_ARG_MISSING", + "executeMode 2 requires --bundleName.", + "Please provide --bundleName for query by bundle."); + return OHOS::ERR_INVALID_VALUE; + } + return RunGetIntentModeByBundle(flag, bundleName); + } else if (executeMode == QUERY_MODE_BY_INTENT) { + if (bundleName.empty() || moduleName.empty() || intentName.empty()) { + TAG_LOGE(AAFwkTag::CC_TOOL, "mode 3 requires -b, -m, -i"); + AppendErrorEvent("ERR_ARG_MISSING", + "executeMode 3 requires --bundleName, --moduleName and --intentName.", + "Please provide all three parameters for query by intent."); + return OHOS::ERR_INVALID_VALUE; + } + return RunGetIntentModeByIntent(flag, + bundleName, moduleName, intentName); + } + + TAG_LOGE(AAFwkTag::CC_TOOL, + "invalid execute mode: %{public}d", executeMode); + AppendErrorEvent("ERR_ARG_INVALID", + "Invalid executeMode value: " + std::to_string(executeMode), + "executeMode must be 1 (all), 2 (by bundle), or 3 (by intent)."); + return OHOS::ERR_INVALID_VALUE; +} + +ErrCode InsightIntentShellCommand::ParseIntOption( + const char *arg, int32_t &value) +{ + if (arg == nullptr || *arg == '\0') { + TAG_LOGE(AAFwkTag::CC_TOOL, "invalid integer value: empty"); + return OHOS::ERR_INVALID_VALUE; + } + + char *end = nullptr; + errno = 0; + long result = strtol(arg, &end, 10); + if (errno != 0 || end == arg || *end != '\0' || + result < INT32_MIN || result > INT32_MAX) { + TAG_LOGE(AAFwkTag::CC_TOOL, + "invalid integer value: %{public}s", arg); + return OHOS::ERR_INVALID_VALUE; + } + value = static_cast(result); + return OHOS::ERR_OK; +} + +ErrCode InsightIntentShellCommand::ValidateExecuteMode(int32_t executeMode) +{ + if (executeMode < 0) { + return OHOS::ERR_OK; + } + + constexpr int32_t MAX_EXECUTE_MODE = + static_cast(AppExecFwk::ExecuteMode::SERVICE_EXTENSION_ABILITY); + if (executeMode > MAX_EXECUTE_MODE) { + TAG_LOGE(AAFwkTag::CC_TOOL, + "invalid execute mode: %{public}d", executeMode); + AppendErrorEvent("ERR_ARG_OUT_OF_RANGE", + "executeMode out of range: " + std::to_string(executeMode), + "executeMode must be 0-3."); + return OHOS::ERR_INVALID_VALUE; + } + return OHOS::ERR_OK; +} + +ErrCode InsightIntentShellCommand::RunGetIntentModeAll(int32_t flag) +{ + std::vector infos; + TAG_LOGI(AAFwkTag::CC_TOOL, + "GetAllInsightIntentInfo with flag: %{public}d", flag); + + auto err = AbilityManagerClient::GetInstance()->GetAllInsightIntentInfo( + static_cast(flag), infos, + DEFAULT_USER_ID); + if (err == OHOS::ERR_OK) { + TAG_LOGI(AAFwkTag::CC_TOOL, + "get all insight intent info successfully, count: %{public}zu", + infos.size()); + nlohmann::json data; + data["mode"] = "all"; + data["total"] = infos.size(); + data["intents"] = BuildIntentInfoListJson(infos, flag); + AppendResultEvent(data); + } else { + TAG_LOGE(AAFwkTag::CC_TOOL, + "get all insight intent info failed: %{public}d", err); + AppendErrorEvent("ERR_INTERNAL_ERROR", + "Failed to get all insight intent info: " + GetMessageFromCode(err), + "Check if AbilityManagerService is running."); + } + + return err; +} + +ErrCode InsightIntentShellCommand::RunGetIntentModeByBundle( + int32_t flag, const std::string &bundleName) +{ + std::vector infos; + TAG_LOGI(AAFwkTag::CC_TOOL, + "GetInsightIntentInfoByBundleName with flag: %{public}d, " + "bundle: %{public}s", + flag, bundleName.c_str()); + + auto err = AbilityManagerClient::GetInstance()->GetInsightIntentInfoByBundleName( + static_cast(flag), + bundleName, infos, DEFAULT_USER_ID); + if (err == OHOS::ERR_OK) { + TAG_LOGI(AAFwkTag::CC_TOOL, + "get insight intent info by bundle successfully, count: %{public}zu", + infos.size()); + nlohmann::json data; + data["mode"] = "by bundle"; + data["bundleName"] = bundleName; + data["total"] = infos.size(); + data["intents"] = BuildIntentInfoListJson(infos, flag); + AppendResultEvent(data); + } else { + TAG_LOGE(AAFwkTag::CC_TOOL, + "get insight intent info by bundle failed: %{public}d", err); + AppendErrorEvent("ERR_INTERNAL_ERROR", + "Failed to get insight intent info by bundle: " + GetMessageFromCode(err), + "Check if --bundleName is correct and AbilityManagerService is running."); + } + + return err; +} + +ErrCode InsightIntentShellCommand::RunGetIntentModeByIntent( + int32_t flag, const std::string &bundleName, + const std::string &moduleName, const std::string &intentName) +{ + AbilityRuntime::InsightIntentInfoForQuery info; + TAG_LOGI(AAFwkTag::CC_TOOL, + "GetInsightIntentInfoByIntentName with flag: %{public}d, " + "bundle: %{public}s, module: %{public}s, intent: %{public}s", + flag, bundleName.c_str(), moduleName.c_str(), + intentName.c_str()); + + auto err = AbilityManagerClient::GetInstance()->GetInsightIntentInfoByIntentName( + static_cast(flag), + bundleName, moduleName, intentName, info, DEFAULT_USER_ID); + if (err == OHOS::ERR_OK && info.intentName.empty()) { + TAG_LOGE(AAFwkTag::CC_TOOL, "intent not found: %{public}s", + intentName.c_str()); + AppendErrorEvent("ERR_RESOURCE_NOT_FOUND", + "Intent not found: " + intentName, + "Check --bundleName, --moduleName and --intentName are correct."); + return OHOS::ERR_INVALID_VALUE; + } + + if (err == OHOS::ERR_OK) { + TAG_LOGI(AAFwkTag::CC_TOOL, + "get insight intent info by intent name successfully"); + nlohmann::json data; + data["mode"] = "by intent"; + data["intent"] = BuildIntentInfoJson(info, flag); + AppendResultEvent(data); + } else { + TAG_LOGE(AAFwkTag::CC_TOOL, + "get insight intent info by intent name failed: %{public}d", err); + AppendErrorEvent("ERR_INTERNAL_ERROR", + "Failed to get insight intent info by intent name: " + + GetMessageFromCode(err), + "Check if AbilityManagerService is running."); + } + + return err; +} + +void InsightIntentShellCommand::AppendResultEvent(const nlohmann::json &data) +{ + nlohmann::json event; + event["type"] = "result"; + event["status"] = "success"; + event["data"] = data; + resultReceiver_.append(event.dump()); +} + +void InsightIntentShellCommand::AppendErrorEvent(const std::string &errCode, + const std::string &errMsg, const std::string &suggestion) +{ + nlohmann::json event; + event["type"] = "result"; + event["status"] = "failed"; + event["errCode"] = errCode; + event["errMsg"] = errMsg; + event["suggestion"] = suggestion; + resultReceiver_.append(event.dump()); + (void)fprintf(stderr, "%s\n", event.dump().c_str()); +} + +bool InsightIntentShellCommand::ShouldAppendEntityInfo(int32_t flag) const +{ + return (flag & AbilityRuntime::GetInsightIntentFlag::GET_ENTITY_INFO) != 0; +} + +bool InsightIntentShellCommand::IsFullInfo(int32_t flag) const +{ + return (flag & AbilityRuntime::GetInsightIntentFlag::GET_FULL_INSIGHT_INTENT) != 0; +} + +nlohmann::json InsightIntentShellCommand::BuildIntentInfoListJson( + const std::vector &infos, + int32_t flag) +{ + nlohmann::json arr = nlohmann::json::array(); + for (const auto &info : infos) { + arr.emplace_back(BuildIntentInfoJson(info, flag)); + } + return arr; +} + +nlohmann::json InsightIntentShellCommand::BuildIntentInfoJson( + const AbilityRuntime::InsightIntentInfoForQuery &info, int32_t flag) +{ + nlohmann::json j; + j["bundleName"] = info.bundleName; + j["moduleName"] = info.moduleName; + j["intentName"] = info.intentName; + j["displayName"] = info.displayName; + j["intentType"] = info.intentType; + j["developType"] = info.develoType; + j["parameters"] = info.parameters; + + if (IsFullInfo(flag)) { + j["domain"] = info.domain; + j["intentVersion"] = info.intentVersion; + j["displayDescription"] = info.displayDescription; + j["schema"] = info.schema; + j["icon"] = info.icon; + j["llmDescription"] = info.llmDescription; + if (!info.keywords.empty()) { + j["keywords"] = info.keywords; + } + } + + if (!info.linkInfo.uri.empty()) { + j["linkInfo"]["uri"] = info.linkInfo.uri; + } + + const auto &page = info.pageInfo; + if (!page.uiAbility.empty() || !page.pagePath.empty()) { + j["pageInfo"]["uiAbility"] = page.uiAbility; + j["pageInfo"]["pagePath"] = page.pagePath; + j["pageInfo"]["navigationId"] = page.navigationId; + j["pageInfo"]["navDestinationName"] = page.navDestinationName; + } + + const auto &entry = info.entryInfo; + if (!entry.abilityName.empty()) { + j["entryInfo"]["abilityName"] = entry.abilityName; + j["entryInfo"]["executeMode"] = CollectSupportedModesJson(info); + } + + const auto &form = info.formInfo; + if (!form.abilityName.empty()) { + j["formInfo"]["abilityName"] = form.abilityName; + j["formInfo"]["formName"] = form.formName; + } + + if (ShouldAppendEntityInfo(flag) && !info.entities.empty()) { + nlohmann::json entities = nlohmann::json::array(); + for (const auto &entity : info.entities) { + entities.emplace_back(BuildEntityJson(entity)); + } + j["entities"] = entities; + } + + return j; +} + +nlohmann::json InsightIntentShellCommand::BuildEntityJson( + const AbilityRuntime::EntityInfoForQuery &entity) +{ + nlohmann::json j; + j["className"] = entity.className; + j["entityId"] = entity.entityId; + j["entityCategory"] = entity.entityCategory; + j["parameters"] = entity.parameters; + j["parentClassName"] = entity.parentClassName; + return j; +} + +nlohmann::json InsightIntentShellCommand::CollectSupportedModesJson( + const AbilityRuntime::InsightIntentInfoForQuery &queryInfo) +{ + nlohmann::json arr = nlohmann::json::array(); + for (auto mode : queryInfo.entryInfo.executeMode) { + arr.emplace_back(static_cast(mode)); + } + for (auto mode : queryInfo.uiAbilityIntentInfo.supportExecuteMode) { + arr.emplace_back(static_cast(mode)); + } + return arr; +} +} // namespace AAFwk +} // namespace OHOS diff --git a/tools/cc/src/cc_param_parser.cpp b/tools/cc/src/cc_param_parser.cpp new file mode 100644 index 0000000000..7abf5dae35 --- /dev/null +++ b/tools/cc/src/cc_param_parser.cpp @@ -0,0 +1,186 @@ +/* + * Copyright (c) 2021-2025 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 "cc_param_parser.h" + +#include "want_params_wrapper.h" +#include "string_wrapper.h" +#include "int_wrapper.h" +#include "bool_wrapper.h" +#include "long_wrapper.h" +#include "float_wrapper.h" +#include "double_wrapper.h" +#include "array_wrapper.h" + +#include "hilog_tag_wrapper.h" + +using namespace OHOS::AAFwk; + +namespace OHOS { +namespace AAFwk { + +WantParams CcParamParser::BuildWantParamsFromJson(const std::string &jsonStr) +{ + WantParams wantParams; + + if (jsonStr.empty()) { + TAG_LOGW(AAFwkTag::CC_TOOL, "json string is empty"); + return wantParams; + } + + try { + nlohmann::json jsonObj = nlohmann::json::parse(jsonStr); + if (!jsonObj.is_object()) { + TAG_LOGE(AAFwkTag::CC_TOOL, "json is not an object"); + return wantParams; + } + + wantParams = ParseJsonObjectToWantParams(jsonObj); + + TAG_LOGI(AAFwkTag::CC_TOOL, + "BuildWantParamsFromJson success, count: %{public}d", + wantParams.Size()); + } catch (const nlohmann::json::exception &e) { + TAG_LOGE(AAFwkTag::CC_TOOL, + "json parse error: %{public}s", e.what()); + } + + return wantParams; +} + +WantParams CcParamParser::ParseJsonObjectToWantParams( + const nlohmann::json &jsonObj) +{ + WantParams wantParams; + + if (!jsonObj.is_object()) { + return wantParams; + } + + for (auto it = jsonObj.begin(); it != jsonObj.end(); ++it) { + const std::string &key = it.key(); + const nlohmann::json &value = it.value(); + + if (value.is_string()) { + wantParams.SetParam(key, + String::Box(value.get())); + } else if (value.is_number_integer()) { + int64_t longValue = value.get(); + if (longValue > INT32_MAX || longValue < INT32_MIN) { + wantParams.SetParam(key, Long::Box(longValue)); + } else { + wantParams.SetParam(key, + Integer::Box(static_cast(longValue))); + } + } else if (value.is_number_float()) { + wantParams.SetParam(key, + String::Box(std::to_string(value.get()))); + } else if (value.is_boolean()) { + wantParams.SetParam(key, Boolean::Box(value.get())); + } else if (value.is_null()) { + wantParams.SetParam(key, String::Box("")); + } else if (value.is_object()) { + WantParams nested = ParseJsonObjectToWantParams(value); + wantParams.SetParam(key, WantParamWrapper::Box(nested)); + } else if (value.is_array()) { + sptr arr = ParseJsonArrayToIArray(value); + if (arr != nullptr) { + wantParams.SetParam(key, arr); + } + } else { + TAG_LOGW(AAFwkTag::CC_TOOL, + "unsupported type for key: %{public}s", key.c_str()); + } + } + + return wantParams; +} + +sptr CcParamParser::ParseJsonArrayToIArray( + const nlohmann::json &jsonArr) +{ + if (!jsonArr.is_array()) { + return nullptr; + } + + std::vector> items; + for (const auto &item : jsonArr) { + if (item.is_object()) { + WantParams p = ParseJsonObjectToWantParams(item); + items.push_back(WantParamWrapper::Box(p)); + } else if (item.is_string()) { + items.push_back(String::Box(item.get())); + } else if (item.is_number_integer()) { + int64_t lv = item.get(); + if (lv > INT32_MAX || lv < INT32_MIN) { + items.push_back(Long::Box(lv)); + } else { + items.push_back( + Integer::Box(static_cast(lv))); + } + } else if (item.is_number_float()) { + items.push_back( + String::Box(std::to_string(item.get()))); + } else if (item.is_boolean()) { + items.push_back(Boolean::Box(item.get())); + } else { + TAG_LOGW(AAFwkTag::CC_TOOL, + "unsupported array item type"); + } + } + + if (items.empty()) { + return nullptr; + } + + InterfaceID type = DetectArrayInterfaceType(items[0]); + TAG_LOGI(AAFwkTag::CC_TOOL, + "array type detected, size: %{public}zu", items.size()); + + sptr arrayObj = new (std::nothrow) Array(items.size(), type); + if (arrayObj != nullptr) { + for (size_t i = 0; i < items.size(); i++) { + arrayObj->Set(i, items[i]); + } + } + + return arrayObj; +} + +InterfaceID CcParamParser::DetectArrayInterfaceType( + const sptr &firstItem) +{ + if (IString::Query(firstItem) != nullptr) { + return g_IID_IString; + } + if (IInteger::Query(firstItem) != nullptr) { + return g_IID_IInteger; + } + if (ILong::Query(firstItem) != nullptr) { + return g_IID_ILong; + } + if (IBoolean::Query(firstItem) != nullptr) { + return g_IID_IBoolean; + } + if (IFloat::Query(firstItem) != nullptr) { + return g_IID_IFloat; + } + if (IDouble::Query(firstItem) != nullptr) { + return g_IID_IDouble; + } + return g_IID_IWantParams; +} +} // namespace AAFwk +} // namespace OHOS diff --git a/tools/cc/src/main.cpp b/tools/cc/src/main.cpp new file mode 100644 index 0000000000..ee242446b1 --- /dev/null +++ b/tools/cc/src/main.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021-2025 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 "cc_command.h" +#include "hilog_tag_wrapper.h" + +using namespace OHOS; + +int main(int argc, char* argv[]) +{ + AAFwk::InsightIntentShellCommand cmd(argc, argv); + auto result = cmd.ExecCommand(); + (void)fprintf(stdout, "%s\n", result.c_str()); + return 0; +} diff --git a/tools/ohos-aa/BUILD.gn b/tools/ohos-aa/BUILD.gn new file mode 100644 index 0000000000..162e03668a --- /dev/null +++ b/tools/ohos-aa/BUILD.gn @@ -0,0 +1,142 @@ +# 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("//build/ohos/cli/ohos_cli_executable.gni") +import("//foundation/ability/ability_runtime/ability_runtime.gni") + +config("ohos_aa_command_config") { + include_dirs = [ + "include", + "${ability_runtime_path}/tools/aa/include", + "${ability_runtime_innerkits_path}/app_manager/include/appmgr", + "${ability_runtime_services_path}/abilitymgr/include/utils", + "${ability_runtime_services_path}/abilitymgr/include", + ] +} + +config("ohos_aa_command_exception_config") { + cflags_cc = [ "-fexceptions" ] +} + +ohos_static_library("ohos_aa_cli_source_set") { + sources = [ + "src/ohos_aa_command.cpp", + "${ability_runtime_path}/tools/aa/src/ability_start_with_observer.cpp", + "${ability_runtime_path}/tools/aa/src/shell_command.cpp", + "${ability_runtime_path}/tools/aa/src/shell_command_config_loader.cpp", + "${ability_runtime_path}/tools/aa/src/shell_command_executor.cpp", + "${ability_runtime_path}/tools/aa/src/shell_command_result.cpp", + ] + + sanitize = { + integer_overflow = true + ubsan = true + boundary_sanitize = true + cfi = true + cfi_cross_dso = true + debug = false + } + + public_configs = [ + ":ohos_aa_command_config", + ":ohos_aa_command_exception_config", + "${ability_runtime_services_path}/abilitymgr:abilityms_config", + "${ability_runtime_services_path}/common:common_config", + ] + + cflags = [] + if (target_cpu == "arm") { + cflags += [ "-DBINDER_IPC_32BIT" ] + } + + deps = [ + "${ability_runtime_innerkits_path}/ability_manager:ability_manager", + "${ability_runtime_innerkits_path}/ability_manager:ability_start_options", + "${ability_runtime_innerkits_path}/ability_manager:ability_start_setting", + "${ability_runtime_innerkits_path}/ability_manager:mission_info", + "${ability_runtime_innerkits_path}/app_manager:app_manager", + ] + + external_deps = [ + "ability_base:base", + "ability_base:want", + "bundle_framework:appexecfwk_core", + "c_utils:utils", + "eventhandler:libeventhandler", + "hicollie:libhicollie", + "hilog:libhilog", + "init:libbegetutil", + "ipc:ipc_core", + ] + external_deps += [ + "ability_base:session_info", + "bundle_framework:appexecfwk_base", + "graphic_2d:color_manager", + "image_framework:image_native", + "init:libbegetutil", + "relational_store:native_dataability", + "relational_store:native_rdb", + "samgr:samgr_proxy", + ] + + subsystem_name = "ability" + part_name = "ability_runtime" +} + +ohos_cli_executable("ohos-aa") { + sources = [ "src/main.cpp" ] + + sanitize = { + integer_overflow = true + ubsan = true + boundary_sanitize = true + cfi = true + cfi_cross_dso = true + debug = false + } + + cflags = [] + if (target_cpu == "arm") { + cflags += [ "-DBINDER_IPC_32BIT" ] + } + + deps = [ ":ohos_aa_cli_source_set" ] + + external_deps = [ + "ability_base:base", + "ability_base:configuration", + "ability_base:session_info", + "c_utils:utils", + "hicollie:libhicollie", + "hilog:libhilog", + "ipc:ipc_core", + "ability_base:want", + "ability_base:session_info", + "bundle_framework:appexecfwk_base", + ] + + defines = [] + + install_enable = true + install_images = ["system"] + cli_config_file = "ohos-aa.json" + subsystem_name = "ability" + part_name = "ability_runtime" +} + +group("tools_ohos_aa") { + deps = [ + ":ohos-aa", + ] +} \ No newline at end of file diff --git a/tools/ohos-aa/README.md b/tools/ohos-aa/README.md new file mode 100644 index 0000000000..8f00cd0c2a --- /dev/null +++ b/tools/ohos-aa/README.md @@ -0,0 +1,201 @@ +# ohos-aa + +## 概述 + +ohos-aa 是 OpenHarmony 提供的 Ability 管理命令行工具,用于在系统上启动指定的Ability组件或强制终止应用程序。该工具遵循 Claw 规范,以 JSON 格式输出执行结果,并提供详细的错误码、错误原因和解决建议,ohos-aa的安装路径为 `/system/bin/cli_tool/executable/ohos-aa`。 + +### 目录结构 + +``` +tools/ohos-aa/ +├── BUILD.gn # GN 构建配置 +├── ohos-aa.json # Claw CLI 命令规范定义文件 +├── include/ +│ └── ohos_aa_command.h # 主头文件,定义命令选项、帮助信息和错误码 +├── src/ +│ ├── main.cpp # 入口函数,包含命令超时管理 +│ └── ohos_aa_command.cpp # 核心命令实现(start、force-stop、help) +└── tests/ + ├── BUILD.gn # 测试构建配置 + ├── ohos_aa_command_start_test.cpp # start 子命令单元测试 + ├── ohos_aa_command_force_stop_test.cpp # force-stop 子命令单元测试 + └── ohos_aa_command_util_test.cpp # 工具函数单元测试 +``` + +## CLI 子命令表 + +| 子命令 | 作用 | 可选参数 | 所需权限 | +|--------|------|----------|----------| +| `start` | 启动一个 Ability 组件 | `--abilityname`、`--bundlename`、`--modulename`、`--uri`、`--action`、`--entity`、`--type`、`--time`、`--pi`、`--ps`、`--pb`、`--psn`、`--help` | `ohos.permission.cli.START_ABILITY` | +| `force-stop` | 强制停止指定应用及其进程 | `--bundlename`、`--help` | `ohos.permission.cli.KILL_APP_PROCESSES` | +| `--help` / `help` | 显示帮助信息 | 无 | 无 | + +### start 子命令参数说明 + +| 参数 | 类型 | 说明 | +|------|------|------| +| `--abilityname ` | string | 待启动的 Ability 名称 | +| `--bundlename ` | string | 待启动应用所在的 Bundle 名称 | +| `--modulename ` | string | 待启动的模块名称(多 HAP 应用中使用) | +| `--uri ` | string | 用于隐式启动的 URI | +| `--action ` | string | 用于隐式启动的 Action | +| `--entity ` | string | 用于隐式启动的 Entity | +| `--type ` | string | 用于隐式启动的 MIME 类型 | +| `--time` | flag | 等待 Ability 启动完成并测量启动耗时 | +| `--pi ` | string | 整型参数键值对,JSON 格式,如 `'{"key1":100,"key2":101}'` | +| `--ps ` | string | 字符串参数键值对,JSON 格式,如 `'{"key1":"value1","key2":"value2"}'` | +| `--pb ` | string | 布尔参数键值对,JSON 格式,如 `'{"key1":true,"key2":false}'` | +| `--psn ` | string | 空键对应的字符串类型值 | +| `--help` | flag | 显示 start 子命令帮助信息 | + +> **注意**:显式启动时 `--abilityname` 和 `--bundlename` 必须同时提供。仅提供 `--abilityname` 而未提供 `--bundlename` 将导致错误。 + +### force-stop 子命令参数说明 + +| 参数 | 类型 | 说明 | +|------|------|------| +| `--bundlename ` | string | 待停止应用的 Bundle 名称 | +| `--help` | flag | 显示 force-stop 子命令帮助信息 | + +## Claw 规范遵循情况 + +### 命令命名规范 + +- 工具名称采用 `ohos-` 格式:`ohos-aa` +- 子命令使用小写英文,多词子命令以连字符分隔:`start`、`force-stop` +- 参数采用双连字符前缀的驼峰命名:`--abilityname`、`--bundlename` +- 命令规范元数据通过 JSON 配置文件 `ohos-aa.json` 定义 + +### 输入格式规范 + +- 命令行参数使用 `getopt_long` 进行解析,支持长选项格式 +- 复杂参数(`--pi`、`--ps`、`--pb`)使用 JSON 字符串格式传入,需用单引号包裹以避免 Shell 转义 +- 输入参数定义在 `ohos-aa.json` 的 `inputSchema` 字段中,采用 JSON Schema 规范 + +### 输出格式规范 + +所有命令执行结果均以 JSON 格式输出到标准输出,符合 `ohos-aa.json` 中 `outputSchema` 的定义。 + +**成功响应:** + +```json +{ + "type": "result", + "status": "success", + "data": { + "message": "start ability successfully." + } +} +``` + +**失败响应:** + +```json +{ + "type": "result", + "status": "failed", + "errCode": "ERR_ABILITY_NOT_FOUND", + "errMsg": "The specified ability does not exist. The specified Ability is not installed.", + "suggestion": "1. Check if the parameter abilityName of ohos-aa -a and the parameter bundleName of -b are correct\n2. Check if the application corresponding to the specified bundleName is installed\n3. For multi-HAP applications, it is necessary to confirm whether the HAP to which the ability belongs has been installed" +} +``` + +**带计时信息的成功响应(使用 `--time`):** + +```json +{ + "type": "result", + "status": "success", + "data": { + "message": "StartMode: Cold\nBundleName: com.example.app\nAbilityName: EntryAbility\nTotalTime: 1200\nWaitTime: 1500" + } +} +``` + +### 错误码 + +ohos-aa 定义了以下错误码,在命令执行失败时通过 JSON 输出返回: + +| 错误码 | 说明 | +|--------|------| +| `ERR_INVALID_COMMAND` | 无效命令 | +| `ERR_INVALID_INPUT` | 无效的输入参数 | +| `ERR_ABILITY_VISIBLE_FALSE_DENY_REQUEST` | 目标 Ability 可见性校验失败 | +| `ERR_ABILITY_NOT_FOUND` | 指定的 Ability 不存在 | +| `ERR_ABILITY_SERVICE_NOT_CONNECTED` | Ability 服务连接失败 | +| `ERR_GET_ABILITY_SERVICE_FAILED` | 获取 Ability 服务失败 | +| `ERR_APP_RESOLVE_APP_ERR` | BMS 返回的应用信息异常 | +| `ERR_ABILITY_NO_FOUND_ABILITY_BY_CALLER` | 不支持通过 ohos-aa 启动 UIExtensionAbility | +| `ERR_ABILITY_IMPLICIT_START_ABILITY_FAIL` | 隐式启动未找到匹配应用 | +| `ERR_APP_CLONE_INDEX_INVALID` | appCloneIndex 参数无效 | +| `ERR_ABILITY_START_ABILITY_WAITING` | 有其他 Ability 正在启动中 | +| `ERR_UNLOCK_SCREEN_FAILED_IN_DEVELOPER_MODE` | 开发者模式下解锁屏幕失败 | +| `ERR_CROWDTEST_EXPIRED` | 众测应用已过期 | +| `ERR_APP_CONTROLLED` | 目标应用被管控 | +| `ERR_EDM_APP_CONTROLLED` | 目标应用被企业设备管理管控 | +| `ERR_NOT_SUPPORTED_PRODUCT_TYPE` | 当前设备不支持窗口选项 | +| `ERR_STATIC_CFG_PERMISSION` | 指定进程权限校验失败 | +| `ERR_INNER_ERR_START` | 内部错误(内存不足、超时等) | +| `ERR_GET_BUNDLE_INFO_FAILED` | 获取包信息失败 | +| `ERR_KILL_PROCESS_FAILED` | 杀进程失败 | +| `ERR_KILL_PROCESS_KEEP_ALIVE` | 常驻进程无法终止 | + +## 使用示例 + +### 查看帮助信息 + +```bash +# 查看 ohos-aa 总体帮助 +ohos-aa --help + +# 查看 start 子命令帮助 +ohos-aa start --help + +# 查看 force-stop 子命令帮助 +ohos-aa force-stop --help +``` + +### 启动 Ability(显式启动) + +```bash +# 基本显式启动 +ohos-aa start --abilityname EntryAbility --bundlename com.example.app +``` + +### 启动 Ability(带模块名) + +```bash +# 指定模块名启动(适用于多 HAP 应用) +ohos-aa start --abilityname EntryAbility --bundlename com.example.app --modulename entry +``` + +### 启动 Ability(隐式启动) + +```bash +# 通过 Action 和 Type 隐式启动 +ohos-aa start --action ohos.want.action.view --type text/plain --uri "https://www.example.com/page" +``` + +### 启动 Ability(带参数传递) + +```bash +# 传递整型、字符串、布尔参数 +ohos-aa start --abilityname EntryAbility --bundlename com.example.app \ + --pi '{"pageId":1,"count":100}' \ + --ps '{"theme":"dark","language":"zh"}' \ + --pb '{"debug":true,"fullscreen":false}' +``` + +### 启动 Ability(带启动耗时测量) + +```bash +# 使用 --time 选项测量启动耗时 +ohos-aa start --abilityname EntryAbility --bundlename com.example.app --time +``` + +### 强制停止应用 + +```bash +# 强制停止指定应用 +ohos-aa force-stop --bundlename com.example.app +``` diff --git a/tools/ohos-aa/include/ohos_aa_command.h b/tools/ohos-aa/include/ohos_aa_command.h new file mode 100644 index 0000000000..9d5cf628b6 --- /dev/null +++ b/tools/ohos-aa/include/ohos_aa_command.h @@ -0,0 +1,186 @@ +/* + * 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_CLAW_AA_COMMAND_H +#define OHOS_ABILITY_RUNTIME_CLAW_AA_COMMAND_H + +#include +#include +#include +#include +#include + +#include "ability_manager_interface.h" +#include "ability_start_setting.h" +#include "shell_command.h" + +namespace OHOS { +namespace AAFwk { +namespace { +using ParametersInteger = std::map; +using ParametersString = std::map; +using ParametersBool = std::map; + +const std::string TOOL_NAME = "ohos-aa"; + +const std::string HELP_MSG = + "ohos-aa - Package management utility for starting an ability or stopping an application on the system\n\n" + "Usage:\n" + " ohos-aa [command] [options]\n\n" + "Parameters:\n" + " --help Display this help message\n\n" + "SubCommands:\n" + " start start an ability\n" + " force-stop stop an application\n\n" + "Examples:\n" + " # Start an ability\n" + " ohos-aa start --abilityname EntryAbility --bundlename com.acts.example\n\n" + " # Stop an applcation\n" + " ohos-aa force-stop --bundlename com.acts.example\n"; + +const std::string VERSION_MSG = "1.0.0\n"; + +const std::string HELP_MSG_START = "ohos-aa start - Start an ability on the system\n\n" + "Usage:\n" + " ohos-aa start [options]\n\n" + "Parameters:\n" + " --help Display this help message\n" + " --abilityname Ability name to be started\n" + " --bundlename bundle name to be started\n" + " --modulename module name to be started\n" + " --uri URI for implicit startup\n" + " --action action for implicit startup\n" + " --entity entity for implicit startup\n" + " --type type for implicit startup\n" + " --pi <'{\"key1\":100,\"key2\":101,\"key3\":102}'> integer-type key-value pair\n" + " --pb <'{\"key1\":true,\"key2\":false,\"key3\":true}'> bool-type key-value pair\n" + " --ps <'{\"key1\":\"str1\",\"key2\":\"str2\",\"key3\":\"str3\"}'> string-type key-value pair\n" + " --psn type for implicit startup\n" + " --time flag for launch-to-foreground time\n\n" + "Examples:\n" + " # Start an ability\n" + " ohos-aa start --abilityname EntryAbility --bundlename com.acts.example\n\n"; + +const std::string HELP_MSG_FORCE_STOP = "ohos-aa force-stop - Stop an application on the system\n\n" + "Usage:\n" + " ohos-aa force-stop [options]\n\n" + "Parameters:\n" + " --help Display this help message\n" + " --bundlename bundle name to be stopped\n" + "Examples:\n" + " # Stop an applcation\n" + " ohos-aa force-stop --bundlename com.acts.example\n"; + +const std::string HELP_MSG_NO_BUNDLE_NAME_OPTION = "error: -b is expected."; + +const std::string STRING_START_ABILITY_OK = "start ability successfully."; +const std::string STRING_START_ABILITY_NG = "error: failed to start ability."; + +const std::string STRING_FORCE_STOP_OK = "force stop process successfully."; +const std::string STRING_FORCE_STOP_NG = "error: failed to force stop process."; + +const int NUMBER_TWO = 2; + +const std::string STRING_TEST_REGEX_INTEGER_NUMBERS = "^(0|[1-9][0-9]*|-[1-9][0-9]*)$"; +const std::string STRING_REGEX_ALL_NUMBERS = "^(-)?([0-9]|[1-9][0-9]+)([\\.][0-9]+)?$"; +const std::string STRING_IMPLICT_START_WITH_WAIT_NG = "The wait option does not support starting implict"; +const std::string STRING_NON_UIABILITY_START_WITH_WAIT_NG = "The wait option does not support starting non-uiability"; + +constexpr int EXTRA_ARGUMENTS_FOR_KEY_VALUE_PAIR = 1; +constexpr int EXTRA_ARGUMENTS_FOR_NULL_STRING = 0; +constexpr int PARAM_LENGTH = 20; +constexpr int INDEX_OFFSET = 3; + +enum OptionType { + OPTION_PARAMETER_INTEGER = 1000, + OPTION_PARAMETER_STRING, + OPTION_PARAMETER_BOOL, + OPTION_PARAMETER_NULL_STRING, + OPTION_ABILITY_NAME, + OPTION_BUNDLE_NAME, + OPTION_MODULE_NAME, + OPTION_URI, + OPTION_ACTION, + OPTION_ENTITY, + OPTION_HELP, + OPTION_TYPE, + OPTION_TIME +}; + +const std::string SHORT_OPTIONS = ""; + +struct option LONG_OPTIONS[] = { + {"help", no_argument, 0, OPTION_HELP}, + {"abilityname", required_argument, 0, OPTION_ABILITY_NAME}, + {"bundlename", required_argument, 0, OPTION_BUNDLE_NAME}, + {"modulename", required_argument, 0, OPTION_MODULE_NAME}, + {"uri", required_argument, 0, OPTION_URI}, + {"action", required_argument, 0, OPTION_ACTION}, + {"entity", required_argument, 0, OPTION_ENTITY}, + {"type", required_argument, 0, OPTION_TYPE}, + {"time", no_argument, 0, OPTION_TIME}, //对应aa start命令的 -W 选项 + {"pi", required_argument, 0, OPTION_PARAMETER_INTEGER}, + {"ps", required_argument, 0, OPTION_PARAMETER_STRING}, + {"pb", required_argument, 0, OPTION_PARAMETER_BOOL}, + {"psn", required_argument, 0, OPTION_PARAMETER_NULL_STRING}, + {0, 0, 0, 0} +}; +} + +class ClawAaShellCommand : public ShellCommand { +public: + ClawAaShellCommand(int argc, char* argv[]); + ~ClawAaShellCommand() override + {} + + ErrCode CreateMessageMap() override; + void CheckStartAbilityResult(ErrCode& result); + ErrCode CreateErrorInfoMap(); + AaToolErrorInfo GetErrorInfoFromCode(const int32_t code); + +private: + ErrCode init() override; + ErrCode CreateCommandMap() override; + + ErrCode RunAsHelpCommand(); + ErrCode RunAsStartAbility(); + ErrCode RunAsForceStop(); + + bool IsLongStartOption(const std::string &argv); + bool IsShortStartOption(const std::string &argv); + bool IsStartOption(const std::string &argv); + bool CheckParameters(int target); + ErrCode ParseParamInteger(ParametersInteger& pi); + ErrCode ParseParamBool(ParametersBool& pb); + ErrCode ParseParamString(ParametersString& ps); + void SetParams(const ParametersInteger& pi, Want& want); + void SetParams(const ParametersString& ps, Want& want); + void SetParams(const ParametersBool& pb, Want& want); + pid_t ConvertPid(std::string& inputPid); + + ErrCode MakeWantFromCmd(Want& want, int32_t& userId); + ErrCode StartAbilityWithWait(Want& want, int32_t userId = DEFAULT_INVAL_VALUE); + bool IsImplicitStartAction(const Want &want); + bool MatchOrderString(const std::regex ®exScript, const std::string &orderCmd); + bool CheckPerfCmdString(const char* optarg, const size_t paramLength, std::string &perfCmd); + void FormatOutputForWithWait(const Want &want, const AbilityStartWithWaitObserverData& data); + + bool startAbilityWithWaitFlag_ = false; + std::map errorInfoMap_; +}; +} // namespace AAFwk +} // namespace OHOS + +#endif // OHOS_ABILITY_RUNTIME_CLAW_AA_COMMAND_H \ No newline at end of file diff --git a/tools/ohos-aa/ohos-aa.json b/tools/ohos-aa/ohos-aa.json new file mode 100644 index 0000000000..878aa166d2 --- /dev/null +++ b/tools/ohos-aa/ohos-aa.json @@ -0,0 +1,203 @@ +{ + "name": "ohos-aa", + "version": "1.0.0", + "description": "ohos-aa - Ability management utility for starting an ability or stopping an application on the system.", + "executablePath": "/system/bin/cli_tool/executable/ohos-aa", + "requirePermissions": [], + "inputSchema": { + "type": "object", + "description": "Sub-options of the ohos-aa command.", + "properties": { + "help": { + "type": "boolean", + "default": false + } + } + }, + "outputSchema": { + "type": "object", + "description": "Execution result of the ohos-aa command.", + "properties": { + "type": { + "type": "string", + "enum": ["result"] + }, + "status": { + "type": "string", + "enum": ["success", "failed"] + }, + "data": { + "type": "object", + "properties": { + "message": { + "type": "string" + } + } + }, + "errCode": { + "type": "string" + }, + "errMsg": { + "type": "string" + }, + "suggestion": { + "type": "string" + } + } + }, + "hasSubCommand": true, + "subcommands": { + "start": { + "description": "ohos-aa start - Start an ability on the system", + "requirePermissions": [ + "ohos.permission.cli.START_ABILITY" + ], + "inputSchema": { + "type": "object", + "description": "Sub-options of the ohos-aa start command.", + "properties": { + "help": { + "type": "boolean", + "default": false + }, + "abilityname": { + "type": "string", + "description": "abilityName" + }, + "bundlename": { + "type": "string", + "description": "bundleName" + }, + "deviceId": { + "type": "string", + "description": "deviceId" + }, + "modulename": { + "type": "string", + "description": "moduleName" + }, + "uri": { + "type": "string", + "description": "URI" + }, + "action": { + "type": "string", + "description": "action" + }, + "entity": { + "type": "string", + "description": "entity" + }, + "type": { + "type": "string", + "description": "type" + }, + "time": { + "type": "boolean", + "description": "time", + "default": false + }, + "pi": { + "type": "string", + "description": "Parameter integer key-value pair map as JSON string. Example: '{\"key1\":100,\"key2\":101}'" + }, + "pb": { + "type": "string", + "description": "Parameter boolean key-value pair map as JSON string. Example: '{\"key1\":true,\"key2\":false}'" + }, + "ps": { + "type": "string", + "description": "Parameter string key-value pair map as JSON string. Example: '{\"key1\":\"value1\",\"key2\":\"value2\"}'" + }, + "psn": { + "type": "string", + "description": "For the string-type value corresponding to an empty key." + } + } + }, + "outputSchema": { + "type": "object", + "description": "Execution result of the ohos-aa start command.", + "properties": { + "type": { + "type": "string", + "enum": ["result"] + }, + "status": { + "type": "string", + "enum": ["success", "failed"] + }, + "data": { + "type": "object", + "properties": { + "message": { + "type": "string" + } + } + }, + "errCode": { + "type": "string" + }, + "errMsg": { + "type": "string" + }, + "suggestion": { + "type": "string" + } + } + } + }, + "force-stop": { + "description": "ohos-aa force-stop - Stop an application on the system.", + "requirePermissions": [ + "ohos.permission.cli.KILL_APP_PROCESSES" + ], + "inputSchema": { + "type": "object", + "description": "Sub-options of the ohos-aa force-stop command.", + "properties": { + "help": { + "type": "boolean", + "default": false + }, + "bundlename": { + "type": "string", + "description": "bundleName" + } + } + }, + "outputSchema": { + "type": "object", + "description": "Execution result of the ohos-aa force-stop command.", + "properties": { + "type": { + "type": "string", + "enum": ["result"] + }, + "status": { + "type": "string", + "enum": ["success", "failed"] + }, + "data": { + "type": "object", + "properties": { + "message": { + "type": "string" + } + } + }, + "errCode": { + "type": "string" + }, + "errMsg": { + "type": "string" + }, + "suggestion": { + "type": "string" + } + } + } + } + }, + "eventSchemas": {} +} \ No newline at end of file diff --git a/tools/ohos-aa/src/main.cpp b/tools/ohos-aa/src/main.cpp new file mode 100644 index 0000000000..b1af3a2a36 --- /dev/null +++ b/tools/ohos-aa/src/main.cpp @@ -0,0 +1,74 @@ +/* + * 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 +#include + +#include "ohos_aa_command.h" +#include "xcollie/xcollie.h" +#include "xcollie/xcollie_define.h" +#include + +using namespace OHOS; +using json = nlohmann::json; +constexpr uint32_t COMMAND_TIME_OUT = 60; +const std::string EXECUTE_COMMAND_FAIL = "error: failed to execute your command.\n"; + +class CommandTimer { +public: + CommandTimer(const std::string &timerName, uint32_t timeout, const std::string &operation) + { + if (operation != "test") { + setTimer_ = true; + timerId_ = HiviewDFX::XCollie::GetInstance().SetTimer("ability::claw_aa_cli_command", timeout, + nullptr, nullptr, HiviewDFX::XCOLLIE_FLAG_LOG | HiviewDFX::XCOLLIE_FLAG_RECOVERY); + } + } + ~CommandTimer() + { + if (setTimer_) { + HiviewDFX::XCollie::GetInstance().CancelTimer(timerId_); + } + } +private: + bool setTimer_ = false; + int32_t timerId_ = 0; +}; + +int main(int argc, char* argv[]) +{ + std::string operation; + if (argc > 1) { + operation = argv[1]; + } + + if (strstr(argv[0], "ohos-aa") != nullptr) { + CommandTimer commandTimer("ability::claw_aa_cli_command", COMMAND_TIME_OUT, operation); + OHOS::AAFwk::ClawAaShellCommand cmd(argc, argv); + cmd.CreateErrorInfoMap(); + if (cmd.ExecCommand() == EXECUTE_COMMAND_FAIL) { + json response; + std::string msg = EXECUTE_COMMAND_FAIL.substr(0, EXECUTE_COMMAND_FAIL.find_last_not_of("\n") + 1); + response["type"] = "result"; + response["status"] = "failed"; + response["errCode"] = msg; + response["errMsg"] = msg; + response["suggestion"] = msg; + std::cout << response.dump() << std::endl; + } + } + fflush(stdout); + _exit(0); +} \ No newline at end of file diff --git a/tools/ohos-aa/src/ohos_aa_command.cpp b/tools/ohos-aa/src/ohos_aa_command.cpp new file mode 100644 index 0000000000..d37c539cdd --- /dev/null +++ b/tools/ohos-aa/src/ohos_aa_command.cpp @@ -0,0 +1,1121 @@ +/* + * 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 "ohos_aa_command.h" + +#include +#include +#include +#include +#include +#include +#include "ability_manager_client.h" +#include "ability_start_with_wait_observer.h" +#include "ability_start_with_wait_observer_utils.h" +#include "hilog_tag_wrapper.h" +#include "iservice_registry.h" +#include "system_ability_definition.h" + +using namespace OHOS::AppExecFwk; +using json = nlohmann::json; + +namespace OHOS { +namespace AAFwk { +using TerminateReason = AbilityStartWithWaitObserverUtil::TerminateReason; +namespace { +constexpr int INNER_ERR_START = 10108101; +const std::string ERR_INVALID_COMMAND = "ERR_INVALID_COMMAND"; +const std::string ERR_INVALID_INPUT = "ERR_INVALID_INPUT"; +constexpr int START_HELP_CODE = 10108104; +const std::string DEVELOPERMODE_STATE = "const.security.developermode.state"; +const std::string SHORT_OPTION_CHARS = "chdabetpsmuAUCDESNR"; +constexpr int SHORT_OPTION_INDEX = 1; + +constexpr int64_t WAIT_INTERVAL = 10 * 1000; // us +constexpr int64_t MAX_WAIT_TIME = 15 * 1000 * 1000; // us + +// Error solution strings +const std::string RESOLVE_ABILITY_ERR_SOLUTION_ONE = + "Check if the parameter abilityName of ohos-aa -a and the parameter bundleName of -b are correct"; +const std::string RESOLVE_ABILITY_ERR_SOLUTION_TWO = + "Check if the application corresponding to the specified bundleName is installed"; +const std::string RESOLVE_ABILITY_ERR_SOLUTION_THREE = + "For multi-HAP applications, " + "it is necessary to confirm whether the HAP to which the ability belongs has been installed"; + +const std::string GET_ABILITY_SERVICE_FAILED_SOLUTION_ONE = + "Check if the application corresponding to the specified bundleName is installed"; + +const std::string ABILITY_SERVICE_NOT_CONNECTED_SOLUTION_ONE = + "Try restarting the device and executing again"; + +const std::string RESOLVE_APP_ERR_SOLUTION_ONE = + "The app information retrieved from BMS is missing the application name or package name"; + +const std::string START_ABILITY_WAITING_SOLUTION_ONE = "No need to process, just wait for the startup"; + +const std::string INNER_ERR_START_SOLUTION_ONE = + "Confirm whether the system memory is sufficient and " + "if there are any issues with the system version used by the device"; +const std::string INNER_ERR_START_SOLUTION_TWO = "Check if too many abilities have been launched"; +const std::string INNER_ERR_START_SOLUTION_THREE = "Try restarting the device"; + +const std::string KILL_PROCESS_FAILED_SOLUTION_ONE = "Confirm whether the target application exists"; +const std::string KILL_PROCESS_FAILED_SOLUTION_TWO = "Confirm the permissions of the target process"; + +const std::string NO_FOUND_ABILITY_BY_CALLER_SOLUTION_ONE = "Normal specifications, no action needed"; + +const std::string ABILITY_VISIBLE_FALSE_DENY_REQUEST_SOLUTION_ONE = + "Check if the exported configuration of the Ability field in the module.json5 of " + "the pulled application is set to true. " + "If not, set it to true"; + +const std::string GET_BUNDLE_INFO_FAILED_SOLUTION_ONE = "Check if the bundleName is correct"; +const std::string GET_BUNDLE_INFO_FAILED_SOLUTION_TWO = + "Check whether the application corresponding " + "to the specified bundleName is installed"; + +const std::string KILL_PROCESS_KEEP_ALIVE_SOLUTION_ONE = "Normal specifications, no action needed"; + +const std::string ERR_UNLOCK_SCREEN_FAILED_IN_DEVELOPER_MODE_SOLUTION_ONE = + "Check in the settings whether the current device is in developer mode, and turn off developer mode"; + +const std::string ERR_NOT_SUPPORTED_PRODUCT_TYPE_SOLUTION_ONE = "Normal specifications, no action needed"; + +const std::string ERR_NOT_IN_APP_PROVISION_MODE_SOLUTION_ONE = + "The same application can be compiled with the Debug mode process " + "to produce an application that supports Debug mode"; + +const std::string ERR_APP_CLONE_INDEX_INVALID_SOLUTION_ONE = "Confirm whether the appCloneIndex is valid"; + +const std::string ERR_STATIC_CFG_PERMISSION_SOLUTION_ONE = + "Confirm whether the permissions of the specified process are correct"; + +const std::string ERR_CROWDTEST_EXPIRED_SOLUTION_ONE = + "Please check whether the application has expired for beta testing; " + "applications that have passed their validity period cannot be launched"; + +const std::string ERR_APP_CONTROLLED_SOLUTION_ONE = "It is recommended to uninstall the application"; + +const std::string ERR_EDM_APP_CONTROLLED_SOLUTION_ONE = + "Please contact the personnel related to enterprise device management"; + +const std::string ERR_IMPLICIT_START_ABILITY_FAIL_SOLUTION_ONE = + "Make sure the parameter configuration of implicit startup is correct"; + +const std::string ERR_IMPLICIT_START_ABILITY_FAIL_SOLUTION_TWO = + "Make sure the corresponding HAP package is installed"; + +const std::string BLACK_ACTION_SELECT_DATA = "ohos.want.action.select"; + +void AddEntities(const std::vector& entities, Want& want) +{ + for (auto entity : entities) { + want.AddEntity(entity); + } +} +} // namespace + +ClawAaShellCommand::ClawAaShellCommand(int argc, char* argv[]) : ShellCommand(argc, argv, TOOL_NAME) +{ + for (int i = 0; i < argc_; i++) { + if (i > 1) { + TAG_LOGI(AAFwkTag::AA_TOOL, "argc greater than 2, ignoring the rest"); + return; + } + TAG_LOGI(AAFwkTag::AA_TOOL, "argv_[%{public}d]: %{public}s", i, argv_[i]); + } +} + +void PrintSuccess(const std::string& message) +{ + json response; + response["type"] = "result"; + response["status"] = "success"; + response["data"]["message"] = message; + std::cout << response.dump() << std::endl; +} + +void PrintError(const AaToolErrorInfo& errorInfo) +{ + json response; + response["type"] = "result"; + response["status"] = "failed"; + response["errCode"] = errorInfo.code; + std::string errMessage = errorInfo.message; + if (errMessage != errorInfo.cause) { + errMessage = errMessage + " " + errorInfo.cause; + } + response["errMsg"] = errMessage; + response["suggestion"] = errorInfo.SolutionsToString(); + std::cout << response.dump() << std::endl; +} + +ErrCode ClawAaShellCommand::CreateCommandMap() +{ + commandMap_ = { + {"--help", [this]() { return this->RunAsHelpCommand(); }}, + {"help", [this]() { return this->RunAsHelpCommand(); }}, + {"start", [this]() { return this->RunAsStartAbility(); }}, + {"force-stop", [this]() { return this->RunAsForceStop(); }}, + }; + return ERR_OK; +} + +ErrCode ClawAaShellCommand::CreateErrorInfoMap() +{ + // Add error code mappings with detailed information (using enum constants as keys) + errorInfoMap_[ABILITY_VISIBLE_FALSE_DENY_REQUEST] = {"ERR_ABILITY_VISIBLE_FALSE_DENY_REQUEST", + "Failed to verify the visibility of the target ability.", + "Application visibility check failed.", + {ABILITY_VISIBLE_FALSE_DENY_REQUEST_SOLUTION_ONE}}; + + errorInfoMap_[RESOLVE_ABILITY_ERR] = {"ERR_ABILITY_NOT_FOUND", "The specified ability does not exist.", + "The specified Ability is not installed.", + {RESOLVE_ABILITY_ERR_SOLUTION_ONE, RESOLVE_ABILITY_ERR_SOLUTION_TWO, RESOLVE_ABILITY_ERR_SOLUTION_THREE}}; + + errorInfoMap_[ABILITY_SERVICE_NOT_CONNECTED] = {"ERR_ABILITY_SERVICE_NOT_CONNECTED", + "Ability service connection failed.", + "Failed to obtain the ability remote service.", + {ABILITY_SERVICE_NOT_CONNECTED_SOLUTION_ONE}}; + + errorInfoMap_[GET_ABILITY_SERVICE_FAILED] = {"ERR_GET_ABILITY_SERVICE_FAILED", "Failed to get the ability service.", + "The abilityInfo is empty when generating the Ability request through BMS.", + {GET_ABILITY_SERVICE_FAILED_SOLUTION_ONE}}; + + errorInfoMap_[RESOLVE_APP_ERR] = {"ERR_APP_RESOLVE_APP_ERR", + "An error of the Want could not be resolved to app info from BMS.", + "Abnormal app information retrieved from BMS.", + {RESOLVE_APP_ERR_SOLUTION_ONE}}; + + errorInfoMap_[NO_FOUND_ABILITY_BY_CALLER] = {"ERR_ABILITY_NO_FOUND_ABILITY_BY_CALLER", + "The oho-aa start command cannot be used to launch a UIExtensionAbility.", + "ohos-aa start does not meet the restrictions imposed by UIExtensionAbility on the initiating party.", + {NO_FOUND_ABILITY_BY_CALLER_SOLUTION_ONE}}; + + errorInfoMap_[ERR_IMPLICIT_START_ABILITY_FAIL] = {"ERR_ABILITY_IMPLICIT_START_ABILITY_FAIL", + "Failed to find a matching application for implicit launch.", + "The parameter configuration of implicit startup is incorrect, or the specified HAP package is not installed.", + {ERR_IMPLICIT_START_ABILITY_FAIL_SOLUTION_ONE, ERR_IMPLICIT_START_ABILITY_FAIL_SOLUTION_TWO}}; + + errorInfoMap_[ERR_APP_CLONE_INDEX_INVALID] = {"ERR_APP_CLONE_INDEX_INVALID", "The passed appCloneIndex is invalid.", + "If the appCloneIndex carried in the parameters of the command is an invalid value, return that error code.", + {ERR_APP_CLONE_INDEX_INVALID_SOLUTION_ONE}}; + + errorInfoMap_[START_ABILITY_WAITING] = {"ERR_ABILITY_START_ABILITY_WAITING", + "Another ability is being started. Wait until it finishes starting.", + "High system concurrency.", + {START_ABILITY_WAITING_SOLUTION_ONE}}; + + errorInfoMap_[ERR_UNLOCK_SCREEN_FAILED_IN_DEVELOPER_MODE] = {"ERR_UNLOCK_SCREEN_FAILED_IN_DEVELOPER_MODE", + "The device screen is locked during the application launch, unlock screen failed.", + "The current mode is developer mode, and the screen cannot be unlocked automatically.", + {ERR_UNLOCK_SCREEN_FAILED_IN_DEVELOPER_MODE_SOLUTION_ONE}}; + + errorInfoMap_[ERR_CROWDTEST_EXPIRED] = {"ERR_CROWDTEST_EXPIRED", + "Failed to unlock the screen in developer mode.", + "The current mode is developer mode, and the screen cannot be unlocked automatically.", + {ERR_CROWDTEST_EXPIRED_SOLUTION_ONE}}; + + errorInfoMap_[ERR_APP_CONTROLLED] = {"ERR_APP_CONTROLLED", + "The target application is under control.", + "The application is suspected of malicious behavior and is restricted from launching by the appStore.", + {ERR_APP_CONTROLLED_SOLUTION_ONE}}; + + errorInfoMap_[ERR_EDM_APP_CONTROLLED] = {"ERR_EDM_APP_CONTROLLED", + "The target application is managed by EDM.", + "The application is under the control of enterprise device management.", + {ERR_EDM_APP_CONTROLLED_SOLUTION_ONE}}; + + errorInfoMap_[ERR_NOT_SUPPORTED_PRODUCT_TYPE] = {"ERR_NOT_SUPPORTED_PRODUCT_TYPE", + "The current device does not support using window options.", + "The user specified windowOptions, but the device does not support it.", + {ERR_NOT_SUPPORTED_PRODUCT_TYPE_SOLUTION_ONE}}; + + errorInfoMap_[ERR_STATIC_CFG_PERMISSION] = {"ERR_STATIC_CFG_PERMISSION", + "The specified process does not have the permission.", + "The specified process permission check failed.", + {ERR_STATIC_CFG_PERMISSION_SOLUTION_ONE}}; + + errorInfoMap_[INNER_ERR_START] = {"ERR_INNER_ERR_START", + "An internal error occurs while attempting to launch the ability.", + "Kernel common errors such as memory allocation and multithreading processing. " + "Specific reasons may include: internal object being null, processing timeout, " + "failure to obtain application information from package management, failure to obtain system service, " + "the number of launched ability instances has reached the limit, etc", + {INNER_ERR_START_SOLUTION_ONE, INNER_ERR_START_SOLUTION_TWO, INNER_ERR_START_SOLUTION_THREE}}; + + errorInfoMap_[GET_BUNDLE_INFO_FAILED] = {"ERR_GET_BUNDLE_INFO_FAILED", + "Failed to retrieve specified package information.", + "The application corresponding to the specified package name is not installed.", + {GET_BUNDLE_INFO_FAILED_SOLUTION_ONE, GET_BUNDLE_INFO_FAILED_SOLUTION_TWO}}; + + errorInfoMap_[KILL_PROCESS_FAILED] = {"ERR_KILL_PROCESS_FAILED", "kill process failed.", + "The specified application's process ID does not exist, " + "there is no permission to kill the target process, or the connection to appManagerService was not successful.", + {KILL_PROCESS_FAILED_SOLUTION_ONE, KILL_PROCESS_FAILED_SOLUTION_TWO}}; + + errorInfoMap_[KILL_PROCESS_KEEP_ALIVE] = {"ERR_KILL_PROCESS_KEEP_ALIVE", + "Persistent processes cannot be terminated.", + "Designate the process as a persistent process and ensure that the device has sufficient memory.", + {KILL_PROCESS_KEEP_ALIVE_SOLUTION_ONE}}; + + return ERR_OK; +} + +ErrCode ClawAaShellCommand::init() +{ + startTime_ = std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()).count(); + return AbilityManagerClient::GetInstance()->Connect(); +} + +ErrCode ClawAaShellCommand::RunAsHelpCommand() +{ + if (cmd_ != "--help") { + std::string message = "Invalid command for ohos-aa."; + AaToolErrorInfo errorInfo = { + ERR_INVALID_COMMAND, + message, + message, + {HELP_MSG}, + }; + PrintError(errorInfo); + } else { + std::cout << HELP_MSG << std::endl; + } + return ERR_OK; +} + +ErrCode ClawAaShellCommand::RunAsStartAbility() +{ + Want want; + int32_t userId = DEFAULT_INVAL_VALUE; + ErrCode result = MakeWantFromCmd(want, userId); + if (result == OHOS::ERR_OK) { + if (startAbilityWithWaitFlag_) { + result = StartAbilityWithWait(want); + } else { + result = AbilityManagerClient::GetInstance()->StartAbility(want); + } + if (result == OHOS::ERR_OK) { + TAG_LOGI(AAFwkTag::AA_TOOL, "%{public}s", STRING_START_ABILITY_OK.c_str()); + resultReceiver_.append(STRING_START_ABILITY_OK); + PrintSuccess(resultReceiver_); + } else { + TAG_LOGI(AAFwkTag::AA_TOOL, "%{public}s result: %{public}d", STRING_START_ABILITY_NG.c_str(), result); + CheckStartAbilityResult(result); + if (result == INNER_ERR) { + result = INNER_ERR_START; + } + AaToolErrorInfo errorInfo = GetErrorInfoFromCode(result); + PrintError(errorInfo); + } + } else if (result == START_HELP_CODE) { + std::cout << HELP_MSG_START << std::endl; + } else { + std::string message = "Invalid options or parameters for start command."; + if (resultReceiver_ == "") { + resultReceiver_ = message; + } + AaToolErrorInfo errorInfo = { + ERR_INVALID_INPUT, + message, + resultReceiver_, + {HELP_MSG_START}, + }; + PrintError(errorInfo); + result = OHOS::ERR_INVALID_VALUE; + } + + return result; +} + +void ClawAaShellCommand::CheckStartAbilityResult(ErrCode& result) +{ + auto it = errorInfoMap_.find(result); + if (it == errorInfoMap_.end()) { + result = INNER_ERR; + } +} + +ErrCode ClawAaShellCommand::RunAsForceStop() +{ + TAG_LOGI(AAFwkTag::AA_TOOL, "enter"); + if (argList_.size() == NUMBER_TWO && argList_[0] == "--bundlename") { + std::string bundleName = argList_[1]; + std::string inputReason = "ohos-aa force-stop"; + TAG_LOGI(AAFwkTag::AA_TOOL, "Bundle name %{public}s", bundleName.c_str()); + ErrCode result = AbilityManagerClient::GetInstance()->KillProcess(bundleName, false, 0, inputReason); + if (result == OHOS::ERR_OK) { + TAG_LOGI(AAFwkTag::AA_TOOL, "%{public}s", STRING_FORCE_STOP_OK.c_str()); + PrintSuccess(STRING_FORCE_STOP_OK); + } else { + TAG_LOGI(AAFwkTag::AA_TOOL, "%{public}s result: %{public}d", STRING_FORCE_STOP_NG.c_str(), result); + AaToolErrorInfo errorInfo = GetErrorInfoFromCode(result); + resultReceiver_ = STRING_FORCE_STOP_NG; + PrintError(errorInfo); + } + return result; + } else if (argList_.size() == 1 && argList_[0] == "--help") { + std::cout << HELP_MSG_FORCE_STOP << std::endl; + return OHOS::ERR_OK; + } + + AaToolErrorInfo errorInfo = { + ERR_INVALID_INPUT, + "Invalid options or parameters for force-stop command.", + "Wrong options or Missing parameters or too many parameters.", + {HELP_MSG_FORCE_STOP}, + }; + resultReceiver_ = errorInfo.message; + PrintError(errorInfo); + return OHOS::ERR_INVALID_VALUE; +} + +pid_t ClawAaShellCommand::ConvertPid(std::string& inputPid) +{ + pid_t pid = 0; + auto res = std::from_chars(inputPid.c_str(), inputPid.c_str() + inputPid.size(), pid); + if (res.ec != std::errc()) { + TAG_LOGE(AAFwkTag::AA_TOOL, "pid stoi(%{public}s) failed", inputPid.c_str()); + } + return pid; +} + +bool ClawAaShellCommand::IsLongStartOption(const std::string &argv) +{ + if (argv.find("--") != 0) { + return false; + } + static std::vector longOptions; + if (longOptions.empty()) { + for (const auto &longOpt : LONG_OPTIONS) { + if (longOpt.name == nullptr) { + continue; + } + longOptions.emplace_back("--" + std::string(longOpt.name)); + } + } + return std::find(longOptions.begin(), longOptions.end(), argv) != longOptions.end(); +} + +bool ClawAaShellCommand::IsShortStartOption(const std::string &argv) +{ + std::string shortOption = "-c"; + for (char c : SHORT_OPTION_CHARS) { + shortOption[SHORT_OPTION_INDEX] = c; + if (argv.find(shortOption) == 0) { + return true; + } + } + return false; +} + +bool ClawAaShellCommand::IsStartOption(const std::string &argv) +{ + if (argv.empty() || (argv.find("-") != 0 && argv.find("--") != 0)) { + return false; + } + if (IsLongStartOption(argv)) { + return true; + } + return IsShortStartOption(argv); +} + +bool ClawAaShellCommand::CheckParameters(int extraArguments) +{ + if (optind + extraArguments >= argc_) return false; + int index = optind + 1; // optind is the index of 'start' which is right behind optarg + int count = 0; + while (index < argc_ && !IsStartOption(argv_[index])) { + count++; + index++; + } + return count == extraArguments; +} + +// parse integer parameters +ErrCode ClawAaShellCommand::ParseParamInteger(ParametersInteger& pi) +{ + std::string sarg(optarg); + if (!sarg.empty() && sarg.front() == '\'') { + sarg.erase(0, 1); + } + if (!sarg.empty() && sarg.back() == '\'') { + sarg.pop_back(); + } + try { + auto paramObj = nlohmann::json::parse(sarg.c_str()); + for (auto& [key, value] : paramObj.items()) { + pi[key] = value.get(); + } + } catch(const std::exception& e) { + resultReceiver_.append("invalid parameter for '--pi' option."); + return OHOS::ERR_INVALID_VALUE; + } + return OHOS::ERR_OK; +} + +// parse bool parameters +ErrCode ClawAaShellCommand::ParseParamBool(ParametersBool& pb) +{ + std::string sarg(optarg); + if (!sarg.empty() && sarg.front() == '\'') { + sarg.erase(0, 1); + } + if (!sarg.empty() && sarg.back() == '\'') { + sarg.pop_back(); + } + try { + auto paramObj = nlohmann::json::parse(sarg.c_str()); + for (auto& [key, value] : paramObj.items()) { + pb[key] = value.get(); + } + } catch(const std::exception& e) { + resultReceiver_.append("invalid parameter for '--pb' option."); + return OHOS::ERR_INVALID_VALUE; + } + return OHOS::ERR_OK; +} + +// parse string parameters +ErrCode ClawAaShellCommand::ParseParamString(ParametersString& ps) +{ + std::string sarg(optarg); + if (!sarg.empty() && sarg.front() == '\'') { + sarg.erase(0, 1); + } + if (!sarg.empty() && sarg.back() == '\'') { + sarg.pop_back(); + } + try { + auto paramObj = nlohmann::json::parse(sarg.c_str()); + for (auto& [key, value] : paramObj.items()) { + ps[key] = value.get(); + } + } catch(const std::exception& e) { + resultReceiver_.append("invalid parameter for '--ps' option."); + return OHOS::ERR_INVALID_VALUE; + } + return OHOS::ERR_OK; +} + +void ClawAaShellCommand::SetParams(const ParametersInteger& pi, Want& want) +{ + for (auto it = pi.begin(); it != pi.end(); it++) { + want.SetParam(it->first, it->second); + } +} + +void ClawAaShellCommand::SetParams(const ParametersString& ps, Want& want) +{ + for (auto it = ps.begin(); it != ps.end(); it++) { + want.SetParam(it->first, it->second); + } +} + +void ClawAaShellCommand::SetParams(const ParametersBool& pb, Want& want) +{ + for (auto it = pb.begin(); it != pb.end(); it++) { + want.SetParam(it->first, it->second); + } +} + +bool ClawAaShellCommand::MatchOrderString(const std::regex ®exScript, const std::string &orderCmd) +{ + TAG_LOGD(AAFwkTag::AA_TOOL, "orderCmd: %{public}s", orderCmd.c_str()); + if (orderCmd.empty()) { + TAG_LOGE(AAFwkTag::AA_TOOL, "empty orderCmd"); + return false; + } + + std::match_results matchResults; + try { + if (!std::regex_match(orderCmd, matchResults, regexScript)) { + TAG_LOGE(AAFwkTag::AA_TOOL, "order mismatch"); + return false; + } + } catch (...) { + TAG_LOGE(AAFwkTag::AA_TOOL, "regex failed"); + return false; + } + return true; +} + +bool ClawAaShellCommand::CheckPerfCmdString(const char* optarg, const size_t paramLength, std::string &perfCmd) +{ + if (optarg == nullptr) { + TAG_LOGE(AAFwkTag::AA_TOOL, "null optarg"); + return false; + } + + if (strlen(optarg) >= paramLength) { + TAG_LOGE(AAFwkTag::AA_TOOL, "ohos-aa start -p param length must < 1024"); + return false; + } + + perfCmd = optarg; + const std::regex regexDumpHeapType(R"(^\s*(dumpheap)\s*$)"); + const std::regex regexSleepType(R"(^\s*(sleep)((\s+\d*)|)\s*$)"); + const std::regex regexBaseLineProfileType(R"(^\s*(baseLineProfile)(\s+.*|$))"); + if (MatchOrderString(regexDumpHeapType, perfCmd) || MatchOrderString(regexSleepType, perfCmd) || + MatchOrderString(regexBaseLineProfileType, perfCmd)) { + return true; + } + + TAG_LOGD(AAFwkTag::AA_TOOL, "command mismatch"); + const std::regex regexProfileType(R"(^\s*(profile)\s+(nativeperf|jsperf)(\s+.*|$))"); + if (!MatchOrderString(regexProfileType, perfCmd)) { + TAG_LOGE(AAFwkTag::AA_TOOL, "invalid command"); + return false; + } + + auto findPos = perfCmd.find("jsperf"); + if (findPos != std::string::npos) { + const std::regex regexCmd(R"(^jsperf($|\s+($|((5000|([1-9]|[1-4]\d)\d\d)|)\s*($|nativeperf.*))))"); + if (!MatchOrderString(regexCmd, perfCmd.substr(findPos, perfCmd.length() - findPos))) { + TAG_LOGE(AAFwkTag::AA_TOOL, "invalid order"); + return false; + } + } + return true; +} + +ErrCode ClawAaShellCommand::MakeWantFromCmd(Want& want, int32_t& userId) +{ + int result = OHOS::ERR_OK; + + int option = -1; + int counter = 0; + + std::string deviceId = ""; + std::string bundleName = ""; + std::string abilityName = ""; + std::string moduleName; + std::string perfCmd; + ParametersInteger parametersInteger; + ParametersString parametersString; + ParametersBool parametersBool; + std::string uri; + std::string action; + std::vector entities; + std::string typeVal; + bool isColdStart = false; + bool isDebugApp = false; + bool isErrorInfoEnhance = false; + bool isContinuation = false; + bool isSandboxApp = false; + bool isNativeDebug = false; + bool isMultiThread = false; + int windowLeft = 0; + bool hasWindowLeft = false; + int windowTop = 0; + bool hasWindowTop = false; + int windowHeight = 0; + bool hasWindowHeight = false; + int windowWidth = 0; + bool hasWindowWidth = false; + + while (true) { + counter++; + + option = getopt_long(argc_, argv_, SHORT_OPTIONS.c_str(), LONG_OPTIONS, nullptr); + + TAG_LOGI( + AAFwkTag::AA_TOOL, "option: %{public}d, optopt: %{public}d, optind: %{public}d", option, optopt, optind); + + if (optind < 0 || optind > argc_) { + return OHOS::ERR_INVALID_VALUE; + } + + if (option == -1) { + // When scanning the first argument + if (counter == 1 && strcmp(argv_[optind], cmd_.c_str()) == 0) { + // 'aa start' with no option: aa start + // 'aa start' with a wrong argument: aa start xxx + // 'aa stop-service' with no option: aa stop-service + // 'aa stop-service' with a wrong argument: aa stop-service xxx + TAG_LOGI(AAFwkTag::AA_TOOL, "'aa %{public}s' %{public}s", HELP_MSG_NO_OPTION.c_str(), cmd_.c_str()); + + resultReceiver_.append(HELP_MSG_NO_OPTION + "\n"); + result = OHOS::ERR_INVALID_VALUE; + } + break; + } + + if (option == '?') { + switch (optopt) { + case OPTION_HELP: { + // 'aa start -h' + // 'aa stop-service -h' + TAG_LOGI(AAFwkTag::AA_TOOL, "'ohos-aa %{public}s --help' wrong arg", cmd_.c_str()); + result = OHOS::ERR_INVALID_VALUE; + break; + } + case OPTION_ABILITY_NAME: { + // 'aa start -a' with no argument + // 'aa stop-service -a' with no argument + TAG_LOGI(AAFwkTag::AA_TOOL, "'ohos-aa %{public}s --abilityname' no arg", cmd_.c_str()); + + resultReceiver_.append("error: option "); + resultReceiver_.append("requires a value.\n"); + + result = OHOS::ERR_INVALID_VALUE; + break; + } + case OPTION_BUNDLE_NAME: { + // 'aa start -b' with no argument + // 'aa stop-service -b' with no argument + TAG_LOGI(AAFwkTag::AA_TOOL, "'ohos-aa %{public}s --bundlename' no arg", cmd_.c_str()); + + resultReceiver_.append("error: option "); + resultReceiver_.append("requires a value.\n"); + + result = OHOS::ERR_INVALID_VALUE; + break; + } + case OPTION_ENTITY: { + // 'aa start -e' with no argument + TAG_LOGI(AAFwkTag::AA_TOOL, "'ohos-aa %{public}s --entity no arg", cmd_.c_str()); + + resultReceiver_.append("error: option "); + resultReceiver_.append("requires a value.\n"); + + result = OHOS::ERR_INVALID_VALUE; + break; + } + case OPTION_TYPE: { + // 'aa start -t' with no argument + TAG_LOGI(AAFwkTag::AA_TOOL, "'ohos-aa %{public}s --time no arg", cmd_.c_str()); + + resultReceiver_.append("error: option "); + resultReceiver_.append("requires a value.\n"); + + result = OHOS::ERR_INVALID_VALUE; + break; + } + case OPTION_MODULE_NAME: { + // 'aa start -m' with no argument + // 'aa stop-service -m' with no argument + TAG_LOGI(AAFwkTag::AA_TOOL, "'ohos-aa %{public}s --modulename' no arg", cmd_.c_str()); + + resultReceiver_.append("error: option "); + resultReceiver_.append("requires a value.\n"); + + result = OHOS::ERR_INVALID_VALUE; + break; + } + case OPTION_PARAMETER_INTEGER: { + // 'aa start --pi' with no argument + TAG_LOGI(AAFwkTag::AA_TOOL, "'aa %{public}s --pi' no arg", cmd_.c_str()); + + resultReceiver_.append("error: option "); + resultReceiver_.append("requires a value.\n"); + + result = OHOS::ERR_INVALID_VALUE; + + break; + } + case OPTION_PARAMETER_STRING: { + // 'aa start --ps' with no argument + TAG_LOGI(AAFwkTag::AA_TOOL, "'aa %{public}s --ps' no arg", cmd_.c_str()); + + resultReceiver_.append("error: option "); + resultReceiver_.append("requires a value.\n"); + + result = OHOS::ERR_INVALID_VALUE; + + break; + } + case OPTION_PARAMETER_BOOL: { + // 'aa start --pb' with no argument + TAG_LOGI(AAFwkTag::AA_TOOL, "'aa %{public}s -pb' no arg", cmd_.c_str()); + + resultReceiver_.append("error: option "); + resultReceiver_.append("requires a value.\n"); + + result = OHOS::ERR_INVALID_VALUE; + + break; + } + case OPTION_PARAMETER_NULL_STRING: { + // 'aa start --psn' with no argument + TAG_LOGI(AAFwkTag::AA_TOOL, "'aa %{public}s --psn' no arg", cmd_.c_str()); + + resultReceiver_.append("error: option "); + resultReceiver_.append("requires a value.\n"); + + result = OHOS::ERR_INVALID_VALUE; + + break; + } + case OPTION_ACTION: { + // 'aa start -A' with no argument + TAG_LOGI(AAFwkTag::AA_TOOL, "'ohos-aa %{public}s --action' no arg", cmd_.c_str()); + + resultReceiver_.append("error: option "); + resultReceiver_.append("requires a value.\n"); + + result = OHOS::ERR_INVALID_VALUE; + + break; + } + case OPTION_URI: { + // 'aa start -U' with no argument + TAG_LOGI(AAFwkTag::AA_TOOL, "'ohos-aa %{public}s --uri' no arg", cmd_.c_str()); + + resultReceiver_.append("error: option "); + resultReceiver_.append("requires a value.\n"); + + result = OHOS::ERR_INVALID_VALUE; + + break; + } + case OPTION_TIME: { + // 'aa start -W' with no argument + startAbilityWithWaitFlag_ = true; + break; + } + case 0: { + // 'aa start' with an unknown option: aa start --x + // 'aa start' with an unknown option: aa start --xxx + // 'aa stop-service' with an unknown option: aa stop-service --x + // 'aa stop-service' with an unknown option: aa stop-service --xxx + std::string unknownOption = ""; + std::string unknownOptionMsg = GetUnknownOptionMsg(unknownOption); + + TAG_LOGI(AAFwkTag::AA_TOOL, "'aa %{public}s' opt unknown", cmd_.c_str()); + + resultReceiver_.append(unknownOptionMsg); + result = OHOS::ERR_INVALID_VALUE; + break; + } + default: { + // 'aa start' with an unknown option: aa start -x + // 'aa start' with an unknown option: aa start -xxx + // 'aa stop-service' with an unknown option: aa stop-service -x + // 'aa stop-service' with an unknown option: aa stop-service -xxx + std::string unknownOption = ""; + std::string unknownOptionMsg = GetUnknownOptionMsg(unknownOption); + + TAG_LOGI(AAFwkTag::AA_TOOL, "'aa %{public}s' opt unknown", cmd_.c_str()); + + resultReceiver_.append(unknownOptionMsg); + result = OHOS::ERR_INVALID_VALUE; + break; + } + } + break; + } + + switch (option) { + case OPTION_HELP: { + // 'aa start -h' + // 'aa start --help' + // 'aa stop-service -h' + // 'aa stop-service --help' + result = START_HELP_CODE; + break; + } + case OPTION_ABILITY_NAME: { + // 'aa start -a xxx' + // 'aa stop-service -a xxx' + + // save ability name + abilityName = optarg; + break; + } + case OPTION_BUNDLE_NAME: { + // 'aa start -b xxx' + // 'aa stop-service -b xxx' + + // save bundle name + bundleName = optarg; + break; + } + case OPTION_ENTITY: { + // 'aa start -e xxx' + + // save entity + entities.push_back(optarg); + break; + } + case OPTION_TYPE: { + // 'aa start -t xxx' + + // save type + typeVal = optarg; + break; + } + case OPTION_MODULE_NAME: { + // 'aa start -m xxx' + // 'aa stop-service -m xxx' + + // save module name + moduleName = optarg; + break; + } + case OPTION_TIME: { + // 'aa start -W' with no argument + startAbilityWithWaitFlag_ = true; + break; + } + case OPTION_PARAMETER_INTEGER: { + // 'ohos-aa start --pi xxx' + // parse option arguments into a key-value map + result = ParseParamInteger(parametersInteger); + break; + } + case OPTION_PARAMETER_STRING: { + // 'aa start --ps xxx' + // parse option arguments into a key-value map + result = ParseParamString(parametersString); + + break; + } + case OPTION_PARAMETER_BOOL: { + // 'aa start --pb xxx' + // parse option arguments into a key-value map + result = ParseParamBool(parametersBool); + + break; + } + case OPTION_PARAMETER_NULL_STRING: { + // 'aa start --psn xxx' + if (!CheckParameters(EXTRA_ARGUMENTS_FOR_NULL_STRING)) { + resultReceiver_.append("invalid number of parameters for option --psn\n"); + result = OHOS::ERR_INVALID_VALUE; + break; + } + + // parse option arguments into a key-value map + parametersString[optarg] = ""; + result = OHOS::ERR_OK; + + break; + } + case OPTION_URI: { + // 'aa start -U xxx' + + // save URI + uri = optarg; + break; + } + case OPTION_ACTION: { + // 'aa start -A xxx' + + // save action + action = optarg; + break; + } + case 0: { + // 'aa start' with an unknown option: aa start -x + // 'aa start' with an unknown option: aa start -xxx + break; + } + default: { + break; + } + } + if (result != OHOS::ERR_OK) { + break; + } + } + + if (result == OHOS::ERR_OK) { + if (!abilityName.empty() && bundleName.empty()) { + // explicitly start ability must have both ability and bundle names + + // 'aa start [-d ] -a -b [-D]' + // 'aa stop-service [-d ] -a -b ' + TAG_LOGI(AAFwkTag::AA_TOOL, "'aa %{public}s' without enough options", cmd_.c_str()); + + resultReceiver_.append(HELP_MSG_NO_BUNDLE_NAME_OPTION + "\n"); + result = OHOS::ERR_INVALID_VALUE; + } else { + ElementName element(deviceId, bundleName, abilityName, moduleName); + want.SetElement(element); + + if (isColdStart) { + want.SetParam("coldStart", isColdStart); + } + if (isDebugApp) { + want.SetParam("debugApp", isDebugApp); + } + if (isContinuation) { + want.AddFlags(Want::FLAG_ABILITY_CONTINUATION); + } + if (!perfCmd.empty()) { + want.SetParam("perfCmd", perfCmd); + } + if (isSandboxApp) { + want.SetParam("sandboxApp", isSandboxApp); + } + if (isNativeDebug) { + want.SetParam("nativeDebug", isNativeDebug); + } + if (!parametersInteger.empty()) { + SetParams(parametersInteger, want); + } + if (!parametersBool.empty()) { + SetParams(parametersBool, want); + } + if (!parametersString.empty()) { + SetParams(parametersString, want); + } + if (!action.empty()) { + want.SetAction(action); + } + if (!uri.empty()) { + want.SetUri(uri); + } + if (!entities.empty()) { + AddEntities(entities, want); + } + if (!typeVal.empty()) { + want.SetType(typeVal); + } + if (isErrorInfoEnhance) { + want.SetParam("errorInfoEnhance", isErrorInfoEnhance); + } + if (isMultiThread) { + want.SetParam("multiThread", isMultiThread); + } + if (hasWindowLeft) { + want.SetParam(Want::PARAM_RESV_WINDOW_LEFT, windowLeft); + } + if (hasWindowTop) { + want.SetParam(Want::PARAM_RESV_WINDOW_TOP, windowTop); + } + if (hasWindowHeight) { + want.SetParam(Want::PARAM_RESV_WINDOW_HEIGHT, windowHeight); + } + if (hasWindowWidth) { + want.SetParam(Want::PARAM_RESV_WINDOW_WIDTH, windowWidth); + } + } + } + + return result; +} + +ErrCode ClawAaShellCommand::StartAbilityWithWait(Want& want, int32_t userId) +{ + if (IsImplicitStartAction(want)) { + auto ret = AbilityManagerClient::GetInstance()->StartAbility(want, DEFAULT_INVAL_VALUE, userId); + if (ret != ERR_OK) { + return ret; + } + resultReceiver_.append(STRING_IMPLICT_START_WITH_WAIT_NG + "\n"); + return ret; + } + if (userId != DEFAULT_INVAL_VALUE) { + TAG_LOGW(AAFwkTag::AA_TOOL, "userId %{public}d is ignored when using -W option", userId); + } + auto observer = sptr::MakeSptr(); + if (!observer) { + TAG_LOGE(AAFwkTag::AA_TOOL, "inner error, alloc memory failed."); + return INNER_ERR; + } + auto ret = AbilityManagerClient::GetInstance()->StartAbilityWithWait(want, observer); + if (ret != ERR_OK) { + return ret; + } + auto maxWaitTime = MAX_WAIT_TIME; + AbilityStartWithWaitObserverData data; + while (true) { + bool isAlwaysWaiting = true; + observer->GetData(isAlwaysWaiting, data); + if (!isAlwaysWaiting) { + FormatOutputForWithWait(want, data); + break; + } + usleep(WAIT_INTERVAL); + maxWaitTime -= WAIT_INTERVAL; + if (maxWaitTime <= 0) { + TAG_LOGE(AAFwkTag::AA_TOOL, "start ability with wait timeout."); + break; + } + } + return ret; +} + +void ClawAaShellCommand::FormatOutputForWithWait(const Want &want, const AbilityStartWithWaitObserverData& data) +{ + switch (static_cast(data.reason)) { + case TerminateReason::TERMINATE_FOR_NONE: { + auto totalTime = data.foregroundTime - data.startTime; + auto now = std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()).count(); + auto waitTime = now - startTime_; + resultReceiver_.append("StartMode: ").append(data.coldStart ? "Cold" : "Hot").append("\n") + .append("BundleName: " + data.bundleName + "\n").append("AbilityName: " + data.abilityName + "\n"); + if (!want.GetModuleName().empty()) { + resultReceiver_.append("ModuleName: " + want.GetModuleName() + "\n"); + } + resultReceiver_.append("TotalTime: " + std::to_string(totalTime) + "\n") + .append("WaitTime: " + std::to_string(waitTime) + "\n"); + break; + } + case TerminateReason::TERMINATE_FOR_NON_UI_ABILITY: { + resultReceiver_.append(STRING_NON_UIABILITY_START_WITH_WAIT_NG + "\n"); + break; + } + default: + // do nothing + break; + } +} + +bool ClawAaShellCommand::IsImplicitStartAction(const Want &want) +{ + auto element = want.GetElement(); + if (!element.GetAbilityName().empty()) { + return false; + } + + if (want.GetIntParam(AAFwk::SCREEN_MODE_KEY, ScreenMode::IDLE_SCREEN_MODE) != ScreenMode::IDLE_SCREEN_MODE) { + TAG_LOGI(AAFwkTag::AA_TOOL, "not use implicit startup process"); + return false; + } + + if (want.GetAction() != BLACK_ACTION_SELECT_DATA) { + TAG_LOGI(AAFwkTag::AA_TOOL, "implicit start, action:%{public}s", want.GetAction().data()); + return true; + } + + return false; +} + +AaToolErrorInfo ClawAaShellCommand::GetErrorInfoFromCode(const int32_t code) +{ + TAG_LOGI(AAFwkTag::AA_TOOL, "code = %{public}d", code); + + AaToolErrorInfo result; + if (errorInfoMap_.find(code) != errorInfoMap_.end()) { + result = errorInfoMap_.at(code); + } + + TAG_LOGI(AAFwkTag::AA_TOOL, "result: %{public}s", result.ToString().c_str()); + + return result; +} + +ErrCode ClawAaShellCommand::CreateMessageMap() +{ + return OHOS::ERR_OK; +} +} // namespace AAFwk +} // namespace OHOS \ No newline at end of file diff --git a/tools/ohos-aa/tests/BUILD.gn b/tools/ohos-aa/tests/BUILD.gn new file mode 100644 index 0000000000..3a3bfe4a24 --- /dev/null +++ b/tools/ohos-aa/tests/BUILD.gn @@ -0,0 +1,122 @@ +# 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/tools" + +config("tools_ohos_aa_config_mock") { + include_dirs = [ "${ability_runtime_path}/tools/test/mock" ] +} + +tools_ohos_aa_mock_sources = + [ "${ability_runtime_path}/tools/test/mock/mock_ability_manager_stub.cpp" ] + +ohos_unittest("ohos_aa_command_start_test") { + module_out_path = module_output_path + + sources = [ "ohos_aa_command_start_test.cpp" ] + sources += tools_ohos_aa_mock_sources + + configs = [ ":tools_ohos_aa_config_mock" ] + + cflags = [] + if (target_cpu == "arm") { + cflags += [ "-DBINDER_IPC_32BIT" ] + } + + deps = [ + "${ability_runtime_path}/tools/ohos-aa:ohos_aa_cli_source_set", + "${ability_runtime_services_path}/abilitymgr:abilityms", + ] + + external_deps = [ + "ability_base:session_info", + "ability_base:configuration", + "bundle_framework:appexecfwk_base", + "googletest:gmock_main", + "googletest:gtest_main", + "hilog:libhilog", + "ipc:ipc_core", + ] +} + +ohos_unittest("ohos_aa_command_force_stop_test") { + module_out_path = module_output_path + + sources = [ "ohos_aa_command_force_stop_test.cpp" ] + sources += tools_ohos_aa_mock_sources + + configs = [ ":tools_ohos_aa_config_mock" ] + + cflags = [] + if (target_cpu == "arm") { + cflags += [ "-DBINDER_IPC_32BIT" ] + } + + deps = [ + "${ability_runtime_path}/tools/ohos-aa:ohos_aa_cli_source_set", + "${ability_runtime_services_path}/abilitymgr:abilityms", + ] + + external_deps = [ + "ability_base:session_info", + "ability_base:configuration", + "bundle_framework:appexecfwk_base", + "googletest:gmock_main", + "googletest:gtest_main", + "hilog:libhilog", + "ipc:ipc_core", + ] +} + +ohos_unittest("ohos_aa_command_util_test") { + module_out_path = module_output_path + + sources = [ "ohos_aa_command_util_test.cpp" ] + + sources += tools_ohos_aa_mock_sources + + configs = [ ":tools_ohos_aa_config_mock" ] + + cflags = [] + if (target_cpu == "arm") { + cflags += [ "-DBINDER_IPC_32BIT" ] + } + + deps = [ + "${ability_runtime_path}/tools/ohos-aa:ohos_aa_cli_source_set", + "${ability_runtime_services_path}/abilitymgr:abilityms", + ] + + external_deps = [ + "ability_base:session_info", + "ability_base:configuration", + "bundle_framework:appexecfwk_base", + "googletest:gmock_main", + "googletest:gtest_main", + "hilog:libhilog", + "ipc:ipc_core", + ] +} + +group("unittest") { + testonly = true + + deps = [ + ":ohos_aa_command_start_test", + ":ohos_aa_command_force_stop_test", + ":ohos_aa_command_util_test", + ] +} diff --git a/tools/ohos-aa/tests/ohos_aa_command_force_stop_test.cpp b/tools/ohos-aa/tests/ohos_aa_command_force_stop_test.cpp new file mode 100644 index 0000000000..6b96ad1790 --- /dev/null +++ b/tools/ohos-aa/tests/ohos_aa_command_force_stop_test.cpp @@ -0,0 +1,160 @@ +/* + * 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 +#include + +#define private public +#define protected public +#include "ohos_aa_command.h" +#undef protected +#undef private +#include "mock_ability_manager_stub.h" +#define private public +#include "ability_manager_client.h" +#undef private +#include "ability_manager_interface.h" +#include "hilog_tag_wrapper.h" + +using namespace testing::ext; +using namespace OHOS; +using namespace OHOS::AAFwk; +using testing::_; +using testing::Return; + +class OhosAaCommandForceStopTest : public ::testing::Test { +public: + static void SetUpTestCase(); + static void TearDownTestCase(); + void SetUp() override; + void TearDown() override; + std::string cmd_ = "force-stop"; +}; + +void OhosAaCommandForceStopTest::SetUpTestCase() +{ + // mock a stub + auto managerStubPtr = sptr(new MockAbilityManagerStub()); + + // set the mock stub + auto managerClientPtr = AbilityManagerClient::GetInstance(); + managerClientPtr->proxy_ = managerStubPtr; +} + +void OhosAaCommandForceStopTest::TearDownTestCase() +{} + +void OhosAaCommandForceStopTest::SetUp() +{ + // reset optind to 0 + optind = 0; +} + +void OhosAaCommandForceStopTest::TearDown() +{} + +/** + * @tc.number: Ohos_Aa_Command_Force_Stop_0100 + * @tc.name: ExecCommand + * @tc.desc: Verify the "ohos-aa force-stop" command with no option. + */ +HWTEST_F(OhosAaCommandForceStopTest, Ohos_Aa_Command_Force_Stop_0100, Function | MediumTest | Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "Ohos_Aa_Command_Force_Stop_0100"); + + char* argv[] = { + (char*)TOOL_NAME.c_str(), + (char*)cmd_.c_str(), + (char*)"", + }; + int argc = sizeof(argv) / sizeof(argv[0]) - 1; + + ClawAaShellCommand cmd(argc, argv); + cmd.CreateErrorInfoMap(); + std::string result = cmd.ExecCommand(); + EXPECT_NE(result.find("Invalid options"), std::string::npos); +} + +/** + * @tc.number: Ohos_Aa_Command_Force_Stop_0500 + * @tc.name: ExecCommand + * @tc.desc: Verify the "ohos-aa force-stop xxx" command with wrong option. + */ +HWTEST_F(OhosAaCommandForceStopTest, Ohos_Aa_Command_Force_Stop_0500, Function | MediumTest | Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "Ohos_Aa_Command_Force_Stop_0500"); + + char* argv[] = { + (char*)TOOL_NAME.c_str(), + (char*)cmd_.c_str(), + (char*)"xxx", + (char*)"", + }; + int argc = sizeof(argv) / sizeof(argv[0]) - 1; + + ClawAaShellCommand cmd(argc, argv); + cmd.CreateErrorInfoMap(); + std::string result = cmd.ExecCommand(); + EXPECT_NE(result.find("Invalid options"), std::string::npos); +} + +/** + * @tc.number: Ohos_Aa_Command_Force_Stop_0600 + * @tc.name: ExecCommand + * @tc.desc: Verify the "ohos-aa force-stop --bundlename" command with no value. + */ +HWTEST_F(OhosAaCommandForceStopTest, Ohos_Aa_Command_Force_Stop_0600, Function | MediumTest | Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "Ohos_Aa_Command_Force_Stop_0600"); + + char* argv[] = { + (char*)TOOL_NAME.c_str(), + (char*)cmd_.c_str(), + (char*)"--bundlename", + (char*)"", + }; + int argc = sizeof(argv) / sizeof(argv[0]) - 1; + + ClawAaShellCommand cmd(argc, argv); + cmd.CreateErrorInfoMap(); + std::string result = cmd.ExecCommand(); + // With only --bundlename but no value, argList_ has size 1, not 2. + EXPECT_NE(result.find("Invalid options"), std::string::npos); +} + +/** + * @tc.number: Ohos_Aa_Command_Force_Stop_0700 + * @tc.name: ExecCommand + * @tc.desc: Verify the "ohos-aa force-stop --bundlename extra" command with too many parameters. + */ +HWTEST_F(OhosAaCommandForceStopTest, Ohos_Aa_Command_Force_Stop_0700, Function | MediumTest | Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "Ohos_Aa_Command_Force_Stop_0700"); + + char* argv[] = { + (char*)TOOL_NAME.c_str(), + (char*)cmd_.c_str(), + (char*)"--bundlename", + (char*)"com.example.test", + (char*)"extra", + (char*)"", + }; + int argc = sizeof(argv) / sizeof(argv[0]) - 1; + + ClawAaShellCommand cmd(argc, argv); + cmd.CreateErrorInfoMap(); + std::string result = cmd.ExecCommand(); + EXPECT_NE(result.find("Invalid options"), std::string::npos); +} diff --git a/tools/ohos-aa/tests/ohos_aa_command_start_test.cpp b/tools/ohos-aa/tests/ohos_aa_command_start_test.cpp new file mode 100644 index 0000000000..c0c83a7d39 --- /dev/null +++ b/tools/ohos-aa/tests/ohos_aa_command_start_test.cpp @@ -0,0 +1,559 @@ +/* + * 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 +#include + +#define private public +#define protected public +#include "ohos_aa_command.h" +#undef protected +#undef private +#include "mock_ability_manager_stub.h" +#define private public +#include "ability_manager_client.h" +#undef private +#include "ability_manager_interface.h" +#include "hilog_tag_wrapper.h" + +using namespace testing::ext; +using namespace OHOS; +using namespace OHOS::AAFwk; +using testing::_; +using testing::Return; + +namespace { +const std::string STRING_ABILITY_NAME = "ability"; +const std::string STRING_BUNDLE_NAME = "bundle"; +const std::string STRING_ACTION = "action"; +const std::string STRING_URI = "https://valid.uri.com"; +const std::string STRING_TYPE = "type"; +const std::string STRING_ENTITY = "entity"; +const std::string STRING_MODULE_NAME = "entry"; +} // namespace + +class OhosAaCommandStartTest : public ::testing::Test { +public: + static void SetUpTestCase(); + static void TearDownTestCase(); + void SetUp() override; + void TearDown() override; + std::string cmd_ = "start"; +}; + +void OhosAaCommandStartTest::SetUpTestCase() +{ + // mock a stub + auto managerStubPtr = sptr(new MockAbilityManagerStub()); + + // set the mock stub + auto managerClientPtr = AbilityManagerClient::GetInstance(); + managerClientPtr->proxy_ = managerStubPtr; +} + +void OhosAaCommandStartTest::TearDownTestCase() +{} + +void OhosAaCommandStartTest::SetUp() +{ + // reset optind to 0 + optind = 0; +} + +void OhosAaCommandStartTest::TearDown() +{} + +/** + * @tc.number: Ohos_Aa_Command_Start_0100 + * @tc.name: ExecCommand + * @tc.desc: Verify the "ohos-aa start" command with no option. + */ +HWTEST_F(OhosAaCommandStartTest, Ohos_Aa_Command_Start_0100, Function | MediumTest | Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "Ohos_Aa_Command_Start_0100"); + + char* argv[] = { + (char*)TOOL_NAME.c_str(), + (char*)cmd_.c_str(), + (char*)"", + }; + int argc = sizeof(argv) / sizeof(argv[0]) - 1; + + ClawAaShellCommand cmd(argc, argv); + cmd.CreateErrorInfoMap(); + std::string result = cmd.ExecCommand(); + EXPECT_NE(result.find("error"), std::string::npos); +} + +/** + * @tc.number: Ohos_Aa_Command_Start_0300 + * @tc.name: ExecCommand + * @tc.desc: Verify the "ohos-aa start --abilityname --bundlename " command with success. + */ +HWTEST_F(OhosAaCommandStartTest, Ohos_Aa_Command_Start_0300, Function | MediumTest | Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "Ohos_Aa_Command_Start_0300"); + + char* argv[] = { + (char*)TOOL_NAME.c_str(), + (char*)cmd_.c_str(), + (char*)"--abilityname", + (char*)STRING_ABILITY_NAME.c_str(), + (char*)"--bundlename", + (char*)STRING_BUNDLE_NAME.c_str(), + (char*)"", + }; + int argc = sizeof(argv) / sizeof(argv[0]) - 1; + + ClawAaShellCommand cmd(argc, argv); + cmd.CreateErrorInfoMap(); + std::string result = cmd.ExecCommand(); + EXPECT_NE(result.find("start ability successfully"), std::string::npos); +} + +/** + * @tc.number: Ohos_Aa_Command_Start_0400 + * @tc.name: ExecCommand + * @tc.desc: Verify the "ohos-aa start --abilityname " command without bundle name. + */ +HWTEST_F(OhosAaCommandStartTest, Ohos_Aa_Command_Start_0400, Function | MediumTest | Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "Ohos_Aa_Command_Start_0400"); + + char* argv[] = { + (char*)TOOL_NAME.c_str(), + (char*)cmd_.c_str(), + (char*)"--abilityname", + (char*)STRING_ABILITY_NAME.c_str(), + (char*)"", + }; + int argc = sizeof(argv) / sizeof(argv[0]) - 1; + + ClawAaShellCommand cmd(argc, argv); + cmd.CreateErrorInfoMap(); + std::string result = cmd.ExecCommand(); + EXPECT_NE(result.find("error"), std::string::npos); +} + +/** + * @tc.number: Ohos_Aa_Command_Start_0900 + * @tc.name: ExecCommand + * @tc.desc: Verify start with action for implicit startup. + */ +HWTEST_F(OhosAaCommandStartTest, Ohos_Aa_Command_Start_0900, Function | MediumTest | Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "Ohos_Aa_Command_Start_0900"); + + char* argv[] = { + (char*)TOOL_NAME.c_str(), + (char*)cmd_.c_str(), + (char*)"--action", + (char*)STRING_ACTION.c_str(), + (char*)"", + }; + int argc = sizeof(argv) / sizeof(argv[0]) - 1; + + ClawAaShellCommand cmd(argc, argv); + cmd.CreateErrorInfoMap(); + std::string result = cmd.ExecCommand(); + EXPECT_NE(result.find("start ability successfully"), std::string::npos); +} + +/** + * @tc.number: Ohos_Aa_Command_Start_1000 + * @tc.name: ExecCommand + * @tc.desc: Verify start with uri and type for implicit startup. + */ +HWTEST_F(OhosAaCommandStartTest, Ohos_Aa_Command_Start_1000, Function | MediumTest | Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "Ohos_Aa_Command_Start_1000"); + + char* argv[] = { + (char*)TOOL_NAME.c_str(), + (char*)cmd_.c_str(), + (char*)"--uri", + (char*)STRING_URI.c_str(), + (char*)"--type", + (char*)STRING_TYPE.c_str(), + (char*)"", + }; + int argc = sizeof(argv) / sizeof(argv[0]) - 1; + + ClawAaShellCommand cmd(argc, argv); + cmd.CreateErrorInfoMap(); + std::string result = cmd.ExecCommand(); + EXPECT_NE(result.find("start ability successfully"), std::string::npos); +} + +/** + * @tc.number: Ohos_Aa_Command_Start_1100 + * @tc.name: ExecCommand + * @tc.desc: Verify start with entity. + */ +HWTEST_F(OhosAaCommandStartTest, Ohos_Aa_Command_Start_1100, Function | MediumTest | Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "Ohos_Aa_Command_Start_1100"); + + char* argv[] = { + (char*)TOOL_NAME.c_str(), + (char*)cmd_.c_str(), + (char*)"--entity", + (char*)STRING_ENTITY.c_str(), + (char*)"", + }; + int argc = sizeof(argv) / sizeof(argv[0]) - 1; + + ClawAaShellCommand cmd(argc, argv); + cmd.CreateErrorInfoMap(); + std::string result = cmd.ExecCommand(); + EXPECT_NE(result.find("start ability successfully"), std::string::npos); +} + +/** + * @tc.number: Ohos_Aa_Command_Start_1200 + * @tc.name: ExecCommand + * @tc.desc: Verify start with moduleName. + */ +HWTEST_F(OhosAaCommandStartTest, Ohos_Aa_Command_Start_1200, Function | MediumTest | Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "Ohos_Aa_Command_Start_1200"); + + char* argv[] = { + (char*)TOOL_NAME.c_str(), + (char*)cmd_.c_str(), + (char*)"--abilityname", + (char*)STRING_ABILITY_NAME.c_str(), + (char*)"--bundlename", + (char*)STRING_BUNDLE_NAME.c_str(), + (char*)"--modulename", + (char*)STRING_MODULE_NAME.c_str(), + (char*)"", + }; + int argc = sizeof(argv) / sizeof(argv[0]) - 1; + + ClawAaShellCommand cmd(argc, argv); + cmd.CreateErrorInfoMap(); + std::string result = cmd.ExecCommand(); + EXPECT_NE(result.find("start ability successfully"), std::string::npos); +} + +/** + * @tc.number: Ohos_Aa_Command_Start_1300 + * @tc.name: ExecCommand + * @tc.desc: Verify start with --pi option (integer parameters). + */ +HWTEST_F(OhosAaCommandStartTest, Ohos_Aa_Command_Start_1300, Function | MediumTest | Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "Ohos_Aa_Command_Start_1300"); + + char* argv[] = { + (char*)TOOL_NAME.c_str(), + (char*)cmd_.c_str(), + (char*)"--abilityname", + (char*)STRING_ABILITY_NAME.c_str(), + (char*)"--bundlename", + (char*)STRING_BUNDLE_NAME.c_str(), + (char*)"--pi", + (char*)R"('{"key1":100,"key2":200}')", + (char*)"", + }; + int argc = sizeof(argv) / sizeof(argv[0]) - 1; + + ClawAaShellCommand cmd(argc, argv); + cmd.CreateErrorInfoMap(); + std::string result = cmd.ExecCommand(); + EXPECT_NE(result.find("start ability successfully"), std::string::npos); +} + +/** + * @tc.number: Ohos_Aa_Command_Start_1400 + * @tc.name: ExecCommand + * @tc.desc: Verify start with --pb option (bool parameters). + */ +HWTEST_F(OhosAaCommandStartTest, Ohos_Aa_Command_Start_1400, Function | MediumTest | Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "Ohos_Aa_Command_Start_1400"); + + char* argv[] = { + (char*)TOOL_NAME.c_str(), + (char*)cmd_.c_str(), + (char*)"--abilityname", + (char*)STRING_ABILITY_NAME.c_str(), + (char*)"--bundlename", + (char*)STRING_BUNDLE_NAME.c_str(), + (char*)"--pb", + (char*)R"('{"key1":true,"key2":false}')", + (char*)"", + }; + int argc = sizeof(argv) / sizeof(argv[0]) - 1; + + ClawAaShellCommand cmd(argc, argv); + cmd.CreateErrorInfoMap(); + std::string result = cmd.ExecCommand(); + EXPECT_NE(result.find("start ability successfully"), std::string::npos); +} + +/** + * @tc.number: Ohos_Aa_Command_Start_1500 + * @tc.name: ExecCommand + * @tc.desc: Verify start with --ps option (string parameters). + */ +HWTEST_F(OhosAaCommandStartTest, Ohos_Aa_Command_Start_1500, Function | MediumTest | Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "Ohos_Aa_Command_Start_1500"); + + char* argv[] = { + (char*)TOOL_NAME.c_str(), + (char*)cmd_.c_str(), + (char*)"--abilityname", + (char*)STRING_ABILITY_NAME.c_str(), + (char*)"--bundlename", + (char*)STRING_BUNDLE_NAME.c_str(), + (char*)"--ps", + (char*)R"('{"key1":"value1","key2":"value2"}')", + (char*)"", + }; + int argc = sizeof(argv) / sizeof(argv[0]) - 1; + + ClawAaShellCommand cmd(argc, argv); + cmd.CreateErrorInfoMap(); + std::string result = cmd.ExecCommand(); + EXPECT_NE(result.find("start ability successfully"), std::string::npos); +} + +/** + * @tc.number: Ohos_Aa_Command_Start_1600 + * @tc.name: ExecCommand + * @tc.desc: Verify start with --psn option (null string parameters). + */ +HWTEST_F(OhosAaCommandStartTest, Ohos_Aa_Command_Start_1600, Function | MediumTest | Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "Ohos_Aa_Command_Start_1600"); + + char* argv[] = { + (char*)TOOL_NAME.c_str(), + (char*)cmd_.c_str(), + (char*)"--abilityname", + (char*)STRING_ABILITY_NAME.c_str(), + (char*)"--bundlename", + (char*)STRING_BUNDLE_NAME.c_str(), + (char*)"--psn", + (char*)"key1", + (char*)"", + }; + int argc = sizeof(argv) / sizeof(argv[0]) - 1; + + ClawAaShellCommand cmd(argc, argv); + cmd.CreateErrorInfoMap(); + std::string result = cmd.ExecCommand(); + EXPECT_NE(result.find("start ability successfully"), std::string::npos); +} + +/** + * @tc.number: Ohos_Aa_Command_Start_1700 + * @tc.name: ExecCommand + * @tc.desc: Verify start with --time option. + */ +HWTEST_F(OhosAaCommandStartTest, Ohos_Aa_Command_Start_1700, Function | MediumTest | Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "Ohos_Aa_Command_Start_1700"); + + char* argv[] = { + (char*)TOOL_NAME.c_str(), + (char*)cmd_.c_str(), + (char*)"--abilityname", + (char*)STRING_ABILITY_NAME.c_str(), + (char*)"--bundlename", + (char*)STRING_BUNDLE_NAME.c_str(), + (char*)"--time", + (char*)"", + }; + int argc = sizeof(argv) / sizeof(argv[0]) - 1; + + ClawAaShellCommand cmd(argc, argv); + cmd.CreateErrorInfoMap(); + std::string result = cmd.ExecCommand(); + EXPECT_NE(result.find("start ability successfully"), std::string::npos); +} + +/** + * @tc.number: Ohos_Aa_Command_Start_1800 + * @tc.name: ExecCommand + * @tc.desc: Verify start with invalid --pi option (bad JSON). + */ +HWTEST_F(OhosAaCommandStartTest, Ohos_Aa_Command_Start_1800, Function | MediumTest | Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "Ohos_Aa_Command_Start_1800"); + + char* argv[] = { + (char*)TOOL_NAME.c_str(), + (char*)cmd_.c_str(), + (char*)"--abilityname", + (char*)STRING_ABILITY_NAME.c_str(), + (char*)"--bundlename", + (char*)STRING_BUNDLE_NAME.c_str(), + (char*)"--pi", + (char*)"not_a_json", + (char*)"", + }; + int argc = sizeof(argv) / sizeof(argv[0]) - 1; + + ClawAaShellCommand cmd(argc, argv); + cmd.CreateErrorInfoMap(); + std::string result = cmd.ExecCommand(); + EXPECT_NE(result.find("invalid parameter"), std::string::npos); +} + +/** + * @tc.number: Ohos_Aa_Command_Start_1900 + * @tc.name: ExecCommand + * @tc.desc: Verify start with invalid --pb option (bad JSON). + */ +HWTEST_F(OhosAaCommandStartTest, Ohos_Aa_Command_Start_1900, Function | MediumTest | Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "Ohos_Aa_Command_Start_1900"); + + char* argv[] = { + (char*)TOOL_NAME.c_str(), + (char*)cmd_.c_str(), + (char*)"--abilityname", + (char*)STRING_ABILITY_NAME.c_str(), + (char*)"--bundlename", + (char*)STRING_BUNDLE_NAME.c_str(), + (char*)"--pb", + (char*)"not_a_json", + (char*)"", + }; + int argc = sizeof(argv) / sizeof(argv[0]) - 1; + + ClawAaShellCommand cmd(argc, argv); + cmd.CreateErrorInfoMap(); + std::string result = cmd.ExecCommand(); + EXPECT_NE(result.find("invalid parameter"), std::string::npos); +} + +/** + * @tc.number: Ohos_Aa_Command_Start_2000 + * @tc.name: ExecCommand + * @tc.desc: Verify start with invalid --ps option (bad JSON). + */ +HWTEST_F(OhosAaCommandStartTest, Ohos_Aa_Command_Start_2000, Function | MediumTest | Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "Ohos_Aa_Command_Start_2000"); + + char* argv[] = { + (char*)TOOL_NAME.c_str(), + (char*)cmd_.c_str(), + (char*)"--abilityname", + (char*)STRING_ABILITY_NAME.c_str(), + (char*)"--bundlename", + (char*)STRING_BUNDLE_NAME.c_str(), + (char*)"--ps", + (char*)"not_a_json", + (char*)"", + }; + int argc = sizeof(argv) / sizeof(argv[0]) - 1; + + ClawAaShellCommand cmd(argc, argv); + cmd.CreateErrorInfoMap(); + std::string result = cmd.ExecCommand(); + EXPECT_NE(result.find("invalid parameter"), std::string::npos); +} + +/** + * @tc.number: Ohos_Aa_Command_Start_2100 + * @tc.name: RunAsStartAbility + * @tc.desc: Verify RunAsStartAbility with KillProcess mock returning error. + */ +HWTEST_F(OhosAaCommandStartTest, Ohos_Aa_Command_Start_2100, Function | MediumTest | Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "Ohos_Aa_Command_Start_2100"); + + char* argv[] = { + (char*)TOOL_NAME.c_str(), + (char*)cmd_.c_str(), + (char*)"--abilityname", + (char*)STRING_ABILITY_NAME.c_str(), + (char*)"--bundlename", + (char*)STRING_BUNDLE_NAME.c_str(), + (char*)"", + }; + int argc = sizeof(argv) / sizeof(argv[0]) - 1; + + ClawAaShellCommand cmd(argc, argv); + cmd.CreateErrorInfoMap(); + + std::string result = cmd.ExecCommand(); + EXPECT_NE(result.find("start ability successfully"), std::string::npos); +} + +/** + * @tc.number: Ohos_Aa_Command_Start_2200 + * @tc.name: RunAsStartAbility + * @tc.desc: Verify RunAsStartAbility with unknown option. + */ +HWTEST_F(OhosAaCommandStartTest, Ohos_Aa_Command_Start_2200, Function | MediumTest | Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "Ohos_Aa_Command_Start_2200"); + + char* argv[] = { + (char*)TOOL_NAME.c_str(), + (char*)cmd_.c_str(), + (char*)"--unknown", + (char*)"", + }; + int argc = sizeof(argv) / sizeof(argv[0]) - 1; + + ClawAaShellCommand cmd(argc, argv); + cmd.CreateErrorInfoMap(); + std::string result = cmd.ExecCommand(); + EXPECT_NE(result.find("unknown option"), std::string::npos); +} + +/** + * @tc.number: Ohos_Aa_Command_Start_2400 + * @tc.name: MakeWantFromCmd + * @tc.desc: Verify MakeWantFromCmd with all options combined. + */ +HWTEST_F(OhosAaCommandStartTest, Ohos_Aa_Command_Start_2400, Function | MediumTest | Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "Ohos_Aa_Command_Start_2400"); + + char* argv[] = { + (char*)TOOL_NAME.c_str(), + (char*)cmd_.c_str(), + (char*)"--abilityname", + (char*)STRING_ABILITY_NAME.c_str(), + (char*)"--bundlename", + (char*)STRING_BUNDLE_NAME.c_str(), + (char*)"--modulename", + (char*)STRING_MODULE_NAME.c_str(), + (char*)"--action", + (char*)STRING_ACTION.c_str(), + (char*)"--entity", + (char*)STRING_ENTITY.c_str(), + (char*)"--type", + (char*)STRING_TYPE.c_str(), + (char*)"--uri", + (char*)STRING_URI.c_str(), + (char*)"", + }; + int argc = sizeof(argv) / sizeof(argv[0]) - 1; + + ClawAaShellCommand cmd(argc, argv); + cmd.CreateErrorInfoMap(); + std::string result = cmd.ExecCommand(); + EXPECT_NE(result.find("start ability successfully"), std::string::npos); +} diff --git a/tools/ohos-aa/tests/ohos_aa_command_util_test.cpp b/tools/ohos-aa/tests/ohos_aa_command_util_test.cpp new file mode 100644 index 0000000000..82b4d95481 --- /dev/null +++ b/tools/ohos-aa/tests/ohos_aa_command_util_test.cpp @@ -0,0 +1,1644 @@ +/* + * 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 +#include + +#define private public +#define protected public +#include "ohos_aa_command.h" +#undef protected +#undef private +#include "mock_ability_manager_stub.h" +#define private public +#include "ability_manager_client.h" +#undef private +#include "ability_manager_interface.h" +#include "ability_start_with_wait_observer_utils.h" +#include "hilog_tag_wrapper.h" + +using namespace testing::ext; +using namespace OHOS; +using namespace OHOS::AAFwk; +using namespace OHOS::AppExecFwk; +using testing::_; +using testing::Return; + +namespace { +constexpr int INNER_ERR_START = 10108101; +} // namespace + +class OhosAaCommandUtilTest : public ::testing::Test { +public: + static void SetUpTestCase(); + static void TearDownTestCase(); + void SetUp() override; + void TearDown() override; +}; + +void OhosAaCommandUtilTest::SetUpTestCase() +{ + // mock a stub + auto managerStubPtr = sptr(new MockAbilityManagerStub()); + + // set the mock stub + auto managerClientPtr = AbilityManagerClient::GetInstance(); + managerClientPtr->proxy_ = managerStubPtr; +} + +void OhosAaCommandUtilTest::TearDownTestCase() +{} + +void OhosAaCommandUtilTest::SetUp() +{ + // reset optind to 0 + optind = 0; +} + +void OhosAaCommandUtilTest::TearDown() +{} + +// ==================== IsLongStartOption tests ==================== + +/** + * @tc.number: Ohos_Aa_IsLongStartOption_0100 + * @tc.name: IsLongStartOption + * @tc.desc: Verify "--help" is a valid long option. + */ +HWTEST_F(OhosAaCommandUtilTest, Ohos_Aa_IsLongStartOption_0100, Function | MediumTest | Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "Ohos_Aa_IsLongStartOption_0100"); + + char* argv[] = { + (char*)TOOL_NAME.c_str(), + (char*)"start", + (char*)"", + }; + int argc = sizeof(argv) / sizeof(argv[0]) - 1; + + ClawAaShellCommand cmd(argc, argv); + EXPECT_TRUE(cmd.IsLongStartOption("--help")); +} + +/** + * @tc.number: Ohos_Aa_IsLongStartOption_0200 + * @tc.name: IsLongStartOption + * @tc.desc: Verify "--abilityname" is a valid long option. + */ +HWTEST_F(OhosAaCommandUtilTest, Ohos_Aa_IsLongStartOption_0200, Function | MediumTest | Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "Ohos_Aa_IsLongStartOption_0200"); + + char* argv[] = { + (char*)TOOL_NAME.c_str(), + (char*)"start", + (char*)"", + }; + int argc = sizeof(argv) / sizeof(argv[0]) - 1; + + ClawAaShellCommand cmd(argc, argv); + EXPECT_TRUE(cmd.IsLongStartOption("--abilityname")); +} + +/** + * @tc.number: Ohos_Aa_IsLongStartOption_0300 + * @tc.name: IsLongStartOption + * @tc.desc: Verify "--bundlename" is a valid long option. + */ +HWTEST_F(OhosAaCommandUtilTest, Ohos_Aa_IsLongStartOption_0300, Function | MediumTest | Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "Ohos_Aa_IsLongStartOption_0300"); + + char* argv[] = { + (char*)TOOL_NAME.c_str(), + (char*)"start", + (char*)"", + }; + int argc = sizeof(argv) / sizeof(argv[0]) - 1; + + ClawAaShellCommand cmd(argc, argv); + EXPECT_TRUE(cmd.IsLongStartOption("--bundlename")); +} + +/** + * @tc.number: Ohos_Aa_IsLongStartOption_0400 + * @tc.name: IsLongStartOption + * @tc.desc: Verify "--pi" is a valid long option. + */ +HWTEST_F(OhosAaCommandUtilTest, Ohos_Aa_IsLongStartOption_0400, Function | MediumTest | Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "Ohos_Aa_IsLongStartOption_0400"); + + char* argv[] = { + (char*)TOOL_NAME.c_str(), + (char*)"start", + (char*)"", + }; + int argc = sizeof(argv) / sizeof(argv[0]) - 1; + + ClawAaShellCommand cmd(argc, argv); + EXPECT_TRUE(cmd.IsLongStartOption("--pi")); +} + +/** + * @tc.number: Ohos_Aa_IsLongStartOption_0500 + * @tc.name: IsLongStartOption + * @tc.desc: Verify "--ps" is a valid long option. + */ +HWTEST_F(OhosAaCommandUtilTest, Ohos_Aa_IsLongStartOption_0500, Function | MediumTest | Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "Ohos_Aa_IsLongStartOption_0500"); + + char* argv[] = { + (char*)TOOL_NAME.c_str(), + (char*)"start", + (char*)"", + }; + int argc = sizeof(argv) / sizeof(argv[0]) - 1; + + ClawAaShellCommand cmd(argc, argv); + EXPECT_TRUE(cmd.IsLongStartOption("--ps")); +} + +/** + * @tc.number: Ohos_Aa_IsLongStartOption_0600 + * @tc.name: IsLongStartOption + * @tc.desc: Verify "--pb" is a valid long option. + */ +HWTEST_F(OhosAaCommandUtilTest, Ohos_Aa_IsLongStartOption_0600, Function | MediumTest | Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "Ohos_Aa_IsLongStartOption_0600"); + + char* argv[] = { + (char*)TOOL_NAME.c_str(), + (char*)"start", + (char*)"", + }; + int argc = sizeof(argv) / sizeof(argv[0]) - 1; + + ClawAaShellCommand cmd(argc, argv); + EXPECT_TRUE(cmd.IsLongStartOption("--pb")); +} + +/** + * @tc.number: Ohos_Aa_IsLongStartOption_0700 + * @tc.name: IsLongStartOption + * @tc.desc: Verify "--time" is a valid long option. + */ +HWTEST_F(OhosAaCommandUtilTest, Ohos_Aa_IsLongStartOption_0700, Function | MediumTest | Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "Ohos_Aa_IsLongStartOption_0700"); + + char* argv[] = { + (char*)TOOL_NAME.c_str(), + (char*)"start", + (char*)"", + }; + int argc = sizeof(argv) / sizeof(argv[0]) - 1; + + ClawAaShellCommand cmd(argc, argv); + EXPECT_TRUE(cmd.IsLongStartOption("--time")); +} + +/** + * @tc.number: Ohos_Aa_IsLongStartOption_0800 + * @tc.name: IsLongStartOption + * @tc.desc: Verify "--unknownoption" is NOT a valid long option. + */ +HWTEST_F(OhosAaCommandUtilTest, Ohos_Aa_IsLongStartOption_0800, Function | MediumTest | Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "Ohos_Aa_IsLongStartOption_0800"); + + char* argv[] = { + (char*)TOOL_NAME.c_str(), + (char*)"start", + (char*)"", + }; + int argc = sizeof(argv) / sizeof(argv[0]) - 1; + + ClawAaShellCommand cmd(argc, argv); + EXPECT_FALSE(cmd.IsLongStartOption("--unknownoption")); +} + +/** + * @tc.number: Ohos_Aa_IsLongStartOption_0900 + * @tc.name: IsLongStartOption + * @tc.desc: Verify "-a" (short option) is NOT a long option. + */ +HWTEST_F(OhosAaCommandUtilTest, Ohos_Aa_IsLongStartOption_0900, Function | MediumTest | Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "Ohos_Aa_IsLongStartOption_0900"); + + char* argv[] = { + (char*)TOOL_NAME.c_str(), + (char*)"start", + (char*)"", + }; + int argc = sizeof(argv) / sizeof(argv[0]) - 1; + + ClawAaShellCommand cmd(argc, argv); + EXPECT_FALSE(cmd.IsLongStartOption("-a")); +} + +/** + * @tc.number: Ohos_Aa_IsLongStartOption_1000 + * @tc.name: IsLongStartOption + * @tc.desc: Verify empty string is NOT a long option. + */ +HWTEST_F(OhosAaCommandUtilTest, Ohos_Aa_IsLongStartOption_1000, Function | MediumTest | Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "Ohos_Aa_IsLongStartOption_1000"); + + char* argv[] = { + (char*)TOOL_NAME.c_str(), + (char*)"start", + (char*)"", + }; + int argc = sizeof(argv) / sizeof(argv[0]) - 1; + + ClawAaShellCommand cmd(argc, argv); + EXPECT_FALSE(cmd.IsLongStartOption("")); +} + +/** + * @tc.number: Ohos_Aa_IsLongStartOption_1100 + * @tc.name: IsLongStartOption + * @tc.desc: Verify single "-" is NOT a long option. + */ +HWTEST_F(OhosAaCommandUtilTest, Ohos_Aa_IsLongStartOption_1100, Function | MediumTest | Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "Ohos_Aa_IsLongStartOption_1100"); + + char* argv[] = { + (char*)TOOL_NAME.c_str(), + (char*)"start", + (char*)"", + }; + int argc = sizeof(argv) / sizeof(argv[0]) - 1; + + ClawAaShellCommand cmd(argc, argv); + EXPECT_FALSE(cmd.IsLongStartOption("-")); +} + +// ==================== IsShortStartOption tests ==================== + +/** + * @tc.number: Ohos_Aa_IsShortStartOption_0100 + * @tc.name: IsShortStartOption + * @tc.desc: Verify "-c" is a valid short option. + */ +HWTEST_F(OhosAaCommandUtilTest, Ohos_Aa_IsShortStartOption_0100, Function | MediumTest | Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "Ohos_Aa_IsShortStartOption_0100"); + + char* argv[] = { + (char*)TOOL_NAME.c_str(), + (char*)"start", + (char*)"", + }; + int argc = sizeof(argv) / sizeof(argv[0]) - 1; + + ClawAaShellCommand cmd(argc, argv); + EXPECT_TRUE(cmd.IsShortStartOption("-c")); +} + +/** + * @tc.number: Ohos_Aa_IsShortStartOption_0200 + * @tc.name: IsShortStartOption + * @tc.desc: Verify "-e" is a valid short option. + */ +HWTEST_F(OhosAaCommandUtilTest, Ohos_Aa_IsShortStartOption_0200, Function | MediumTest | Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "Ohos_Aa_IsShortStartOption_0200"); + + char* argv[] = { + (char*)TOOL_NAME.c_str(), + (char*)"start", + (char*)"", + }; + int argc = sizeof(argv) / sizeof(argv[0]) - 1; + + ClawAaShellCommand cmd(argc, argv); + EXPECT_TRUE(cmd.IsShortStartOption("-e")); +} + +/** + * @tc.number: Ohos_Aa_IsShortStartOption_0300 + * @tc.name: IsShortStartOption + * @tc.desc: Verify "-x" is NOT a valid short option (not in SHORT_OPTION_CHARS). + */ +HWTEST_F(OhosAaCommandUtilTest, Ohos_Aa_IsShortStartOption_0300, Function | MediumTest | Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "Ohos_Aa_IsShortStartOption_0300"); + + char* argv[] = { + (char*)TOOL_NAME.c_str(), + (char*)"start", + (char*)"", + }; + int argc = sizeof(argv) / sizeof(argv[0]) - 1; + + ClawAaShellCommand cmd(argc, argv); + EXPECT_FALSE(cmd.IsShortStartOption("-x")); +} + +/** + * @tc.number: Ohos_Aa_IsShortStartOption_0400 + * @tc.name: IsShortStartOption + * @tc.desc: Verify "-d" is a valid short option. + */ +HWTEST_F(OhosAaCommandUtilTest, Ohos_Aa_IsShortStartOption_0400, Function | MediumTest | Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "Ohos_Aa_IsShortStartOption_0400"); + + char* argv[] = { + (char*)TOOL_NAME.c_str(), + (char*)"start", + (char*)"", + }; + int argc = sizeof(argv) / sizeof(argv[0]) - 1; + + ClawAaShellCommand cmd(argc, argv); + EXPECT_TRUE(cmd.IsShortStartOption("-d")); +} + +/** + * @tc.number: Ohos_Aa_IsShortStartOption_0500 + * @tc.name: IsShortStartOption + * @tc.desc: Verify "-a" is a valid short option. + */ +HWTEST_F(OhosAaCommandUtilTest, Ohos_Aa_IsShortStartOption_0500, Function | MediumTest | Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "Ohos_Aa_IsShortStartOption_0500"); + + char* argv[] = { + (char*)TOOL_NAME.c_str(), + (char*)"start", + (char*)"", + }; + int argc = sizeof(argv) / sizeof(argv[0]) - 1; + + ClawAaShellCommand cmd(argc, argv); + EXPECT_TRUE(cmd.IsShortStartOption("-a")); +} + +/** + * @tc.number: Ohos_Aa_IsShortStartOption_0600 + * @tc.name: IsShortStartOption + * @tc.desc: Verify "-b" is a valid short option. + */ +HWTEST_F(OhosAaCommandUtilTest, Ohos_Aa_IsShortStartOption_0600, Function | MediumTest | Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "Ohos_Aa_IsShortStartOption_0600"); + + char* argv[] = { + (char*)TOOL_NAME.c_str(), + (char*)"start", + (char*)"", + }; + int argc = sizeof(argv) / sizeof(argv[0]) - 1; + + ClawAaShellCommand cmd(argc, argv); + EXPECT_TRUE(cmd.IsShortStartOption("-b")); +} + +/** + * @tc.number: Ohos_Aa_IsShortStartOption_0700 + * @tc.name: IsShortStartOption + * @tc.desc: Verify "--help" is NOT a short option. + */ +HWTEST_F(OhosAaCommandUtilTest, Ohos_Aa_IsShortStartOption_0700, Function | MediumTest | Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "Ohos_Aa_IsShortStartOption_0700"); + + char* argv[] = { + (char*)TOOL_NAME.c_str(), + (char*)"start", + (char*)"", + }; + int argc = sizeof(argv) / sizeof(argv[0]) - 1; + + ClawAaShellCommand cmd(argc, argv); + EXPECT_FALSE(cmd.IsShortStartOption("--help")); +} + +// ==================== IsStartOption tests ==================== + +/** + * @tc.number: Ohos_Aa_IsStartOption_0100 + * @tc.name: IsStartOption + * @tc.desc: Verify "--help" is a valid start option. + */ +HWTEST_F(OhosAaCommandUtilTest, Ohos_Aa_IsStartOption_0100, Function | MediumTest | Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "Ohos_Aa_IsStartOption_0100"); + + char* argv[] = { + (char*)TOOL_NAME.c_str(), + (char*)"start", + (char*)"", + }; + int argc = sizeof(argv) / sizeof(argv[0]) - 1; + + ClawAaShellCommand cmd(argc, argv); + EXPECT_TRUE(cmd.IsStartOption("--help")); +} + +/** + * @tc.number: Ohos_Aa_IsStartOption_0200 + * @tc.name: IsStartOption + * @tc.desc: Verify "-e" is a valid start option (short). + */ +HWTEST_F(OhosAaCommandUtilTest, Ohos_Aa_IsStartOption_0200, Function | MediumTest | Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "Ohos_Aa_IsStartOption_0200"); + + char* argv[] = { + (char*)TOOL_NAME.c_str(), + (char*)"start", + (char*)"", + }; + int argc = sizeof(argv) / sizeof(argv[0]) - 1; + + ClawAaShellCommand cmd(argc, argv); + EXPECT_TRUE(cmd.IsStartOption("-e")); +} + +/** + * @tc.number: Ohos_Aa_IsStartOption_0300 + * @tc.name: IsStartOption + * @tc.desc: Verify "--unknownoption" is NOT a valid start option. + */ +HWTEST_F(OhosAaCommandUtilTest, Ohos_Aa_IsStartOption_0300, Function | MediumTest | Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "Ohos_Aa_IsStartOption_0300"); + + char* argv[] = { + (char*)TOOL_NAME.c_str(), + (char*)"start", + (char*)"", + }; + int argc = sizeof(argv) / sizeof(argv[0]) - 1; + + ClawAaShellCommand cmd(argc, argv); + EXPECT_FALSE(cmd.IsStartOption("--unknownoption")); +} + +/** + * @tc.number: Ohos_Aa_IsStartOption_0400 + * @tc.name: IsStartOption + * @tc.desc: Verify "-x" is NOT a valid start option. + */ +HWTEST_F(OhosAaCommandUtilTest, Ohos_Aa_IsStartOption_0400, Function | MediumTest | Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "Ohos_Aa_IsStartOption_0400"); + + char* argv[] = { + (char*)TOOL_NAME.c_str(), + (char*)"start", + (char*)"", + }; + int argc = sizeof(argv) / sizeof(argv[0]) - 1; + + ClawAaShellCommand cmd(argc, argv); + EXPECT_FALSE(cmd.IsStartOption("-x")); +} + +/** + * @tc.number: Ohos_Aa_IsStartOption_0500 + * @tc.name: IsStartOption + * @tc.desc: Verify empty string is NOT a valid start option. + */ +HWTEST_F(OhosAaCommandUtilTest, Ohos_Aa_IsStartOption_0500, Function | MediumTest | Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "Ohos_Aa_IsStartOption_0500"); + + char* argv[] = { + (char*)TOOL_NAME.c_str(), + (char*)"start", + (char*)"", + }; + int argc = sizeof(argv) / sizeof(argv[0]) - 1; + + ClawAaShellCommand cmd(argc, argv); + EXPECT_FALSE(cmd.IsStartOption("")); +} + +/** + * @tc.number: Ohos_Aa_IsStartOption_0600 + * @tc.name: IsStartOption + * @tc.desc: Verify plain text (no leading -) is NOT a valid start option. + */ +HWTEST_F(OhosAaCommandUtilTest, Ohos_Aa_IsStartOption_0600, Function | MediumTest | Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "Ohos_Aa_IsStartOption_0600"); + + char* argv[] = { + (char*)TOOL_NAME.c_str(), + (char*)"start", + (char*)"", + }; + int argc = sizeof(argv) / sizeof(argv[0]) - 1; + + ClawAaShellCommand cmd(argc, argv); + EXPECT_FALSE(cmd.IsStartOption("value")); +} + +// ==================== CheckParameters tests ==================== + +/** + * @tc.number: Ohos_Aa_CheckParameters_0100 + * @tc.name: CheckParameters + * @tc.desc: Verify CheckParameters with correct number of extra arguments (0). + */ +HWTEST_F(OhosAaCommandUtilTest, Ohos_Aa_CheckParameters_0100, Function | MediumTest | Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "Ohos_Aa_CheckParameters_0100"); + + char* argv[] = { + (char*)TOOL_NAME.c_str(), + (char*)"start", + (char*)"--abilityname", + (char*)"TestAbility", + (char*)"", + }; + int argc = sizeof(argv) / sizeof(argv[0]) - 1; + + ClawAaShellCommand cmd(argc, argv); + // When optind is at the end of args and extraArguments is 0, should return true + // We set up the scenario where optind points beyond argc + optind = argc; + EXPECT_FALSE(cmd.CheckParameters(0)); +} + +/** + * @tc.number: Ohos_Aa_CheckParameters_0200 + * @tc.name: CheckParameters + * @tc.desc: Verify CheckParameters with extraArguments = 1 when there is one extra arg. + */ +HWTEST_F(OhosAaCommandUtilTest, Ohos_Aa_CheckParameters_0200, Function | MediumTest | Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "Ohos_Aa_CheckParameters_0200"); + + char* argv[] = { + (char*)TOOL_NAME.c_str(), + (char*)"start", + (char*)"--abilityname", + (char*)"TestAbility", + (char*)"extra_value", + (char*)"", + }; + int argc = sizeof(argv) / sizeof(argv[0]) - 1; + + ClawAaShellCommand cmd(argc, argv); + // Simulate optind after parsing --abilityname, pointing to extra_value + optind = 3; // after --abilityname optarg + EXPECT_TRUE(cmd.CheckParameters(1)); +} + +/** + * @tc.number: Ohos_Aa_CheckParameters_0300 + * @tc.name: CheckParameters + * @tc.desc: Verify CheckParameters with extraArguments = 1 when there are 0 extra args. + */ +HWTEST_F(OhosAaCommandUtilTest, Ohos_Aa_CheckParameters_0300, Function | MediumTest | Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "Ohos_Aa_CheckParameters_0300"); + + char* argv[] = { + (char*)TOOL_NAME.c_str(), + (char*)"start", + (char*)"--abilityname", + (char*)"TestAbility", + (char*)"", + }; + int argc = sizeof(argv) / sizeof(argv[0]) - 1; + + ClawAaShellCommand cmd(argc, argv); + optind = 3; // After --abilityname, only the terminating empty string is left + EXPECT_FALSE(cmd.CheckParameters(1)); +} + +// ==================== ParseParamInteger tests ==================== + +/** + * @tc.number: Ohos_Aa_ParseParamInteger_0100 + * @tc.name: ParseParamInteger + * @tc.desc: Verify ParseParamInteger with valid JSON. + */ +HWTEST_F(OhosAaCommandUtilTest, Ohos_Aa_ParseParamInteger_0100, Function | MediumTest | Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "Ohos_Aa_ParseParamInteger_0100"); + + char* argv[] = { + (char*)TOOL_NAME.c_str(), + (char*)"start", + (char*)"", + }; + int argc = sizeof(argv) / sizeof(argv[0]) - 1; + + ClawAaShellCommand cmd(argc, argv); + cmd.CreateErrorInfoMap(); + + // Simulate optarg pointing to a valid JSON string + char param[] = R"({"key1":100,"key2":200})"; + optind = 0; + ::optarg = param; + + ParametersInteger pi; + ErrCode result = cmd.ParseParamInteger(pi); + EXPECT_EQ(result, OHOS::ERR_OK); + EXPECT_EQ(pi.size(), 2u); + EXPECT_EQ(pi["key1"], 100); + EXPECT_EQ(pi["key2"], 200); +} + +/** + * @tc.number: Ohos_Aa_ParseParamInteger_0200 + * @tc.name: ParseParamInteger + * @tc.desc: Verify ParseParamInteger with invalid JSON. + */ +HWTEST_F(OhosAaCommandUtilTest, Ohos_Aa_ParseParamInteger_0200, Function | MediumTest | Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "Ohos_Aa_ParseParamInteger_0200"); + + char* argv[] = { + (char*)TOOL_NAME.c_str(), + (char*)"start", + (char*)"", + }; + int argc = sizeof(argv) / sizeof(argv[0]) - 1; + + ClawAaShellCommand cmd(argc, argv); + cmd.CreateErrorInfoMap(); + + char param[] = "not_a_json"; + ::optarg = param; + + ParametersInteger pi; + ErrCode result = cmd.ParseParamInteger(pi); + EXPECT_EQ(result, OHOS::ERR_INVALID_VALUE); +} + +/** + * @tc.number: Ohos_Aa_ParseParamInteger_0300 + * @tc.name: ParseParamInteger + * @tc.desc: Verify ParseParamInteger with JSON wrapped in single quotes. + */ +HWTEST_F(OhosAaCommandUtilTest, Ohos_Aa_ParseParamInteger_0300, Function | MediumTest | Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "Ohos_Aa_ParseParamInteger_0300"); + + char* argv[] = { + (char*)TOOL_NAME.c_str(), + (char*)"start", + (char*)"", + }; + int argc = sizeof(argv) / sizeof(argv[0]) - 1; + + ClawAaShellCommand cmd(argc, argv); + cmd.CreateErrorInfoMap(); + + char param[] = R"('{"key1":100,"key2":200}')"; + ::optarg = param; + + ParametersInteger pi; + ErrCode result = cmd.ParseParamInteger(pi); + EXPECT_EQ(result, OHOS::ERR_OK); + EXPECT_EQ(pi.size(), 2u); + EXPECT_EQ(pi["key1"], 100); + EXPECT_EQ(pi["key2"], 200); +} + +/** + * @tc.number: Ohos_Aa_ParseParamInteger_0400 + * @tc.name: ParseParamInteger + * @tc.desc: Verify ParseParamInteger with negative integer value. + */ +HWTEST_F(OhosAaCommandUtilTest, Ohos_Aa_ParseParamInteger_0400, Function | MediumTest | Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "Ohos_Aa_ParseParamInteger_0400"); + + char* argv[] = { + (char*)TOOL_NAME.c_str(), + (char*)"start", + (char*)"", + }; + int argc = sizeof(argv) / sizeof(argv[0]) - 1; + + ClawAaShellCommand cmd(argc, argv); + cmd.CreateErrorInfoMap(); + + char param[] = R"({"key1":-100})"; + ::optarg = param; + + ParametersInteger pi; + ErrCode result = cmd.ParseParamInteger(pi); + EXPECT_EQ(result, OHOS::ERR_OK); + EXPECT_EQ(pi["key1"], -100); +} + +// ==================== ParseParamBool tests ==================== + +/** + * @tc.number: Ohos_Aa_ParseParamBool_0100 + * @tc.name: ParseParamBool + * @tc.desc: Verify ParseParamBool with valid JSON. + */ +HWTEST_F(OhosAaCommandUtilTest, Ohos_Aa_ParseParamBool_0100, Function | MediumTest | Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "Ohos_Aa_ParseParamBool_0100"); + + char* argv[] = { + (char*)TOOL_NAME.c_str(), + (char*)"start", + (char*)"", + }; + int argc = sizeof(argv) / sizeof(argv[0]) - 1; + + ClawAaShellCommand cmd(argc, argv); + cmd.CreateErrorInfoMap(); + + char param[] = R"({"key1":true,"key2":false})"; + ::optarg = param; + + ParametersBool pb; + ErrCode result = cmd.ParseParamBool(pb); + EXPECT_EQ(result, OHOS::ERR_OK); + EXPECT_EQ(pb.size(), 2u); + EXPECT_TRUE(pb["key1"]); + EXPECT_FALSE(pb["key2"]); +} + +/** + * @tc.number: Ohos_Aa_ParseParamBool_0200 + * @tc.name: ParseParamBool + * @tc.desc: Verify ParseParamBool with invalid JSON. + */ +HWTEST_F(OhosAaCommandUtilTest, Ohos_Aa_ParseParamBool_0200, Function | MediumTest | Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "Ohos_Aa_ParseParamBool_0200"); + + char* argv[] = { + (char*)TOOL_NAME.c_str(), + (char*)"start", + (char*)"", + }; + int argc = sizeof(argv) / sizeof(argv[0]) - 1; + + ClawAaShellCommand cmd(argc, argv); + cmd.CreateErrorInfoMap(); + + char param[] = "not_a_json"; + ::optarg = param; + + ParametersBool pb; + ErrCode result = cmd.ParseParamBool(pb); + EXPECT_EQ(result, OHOS::ERR_INVALID_VALUE); +} + +/** + * @tc.number: Ohos_Aa_ParseParamBool_0300 + * @tc.name: ParseParamBool + * @tc.desc: Verify ParseParamBool with JSON wrapped in single quotes. + */ +HWTEST_F(OhosAaCommandUtilTest, Ohos_Aa_ParseParamBool_0300, Function | MediumTest | Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "Ohos_Aa_ParseParamBool_0300"); + + char* argv[] = { + (char*)TOOL_NAME.c_str(), + (char*)"start", + (char*)"", + }; + int argc = sizeof(argv) / sizeof(argv[0]) - 1; + + ClawAaShellCommand cmd(argc, argv); + cmd.CreateErrorInfoMap(); + + char param[] = R"('{"key1":true}')"; + ::optarg = param; + + ParametersBool pb; + ErrCode result = cmd.ParseParamBool(pb); + EXPECT_EQ(result, OHOS::ERR_OK); + EXPECT_EQ(pb.size(), 1u); + EXPECT_TRUE(pb["key1"]); +} + +// ==================== ParseParamString tests ==================== + +/** + * @tc.number: Ohos_Aa_ParseParamString_0100 + * @tc.name: ParseParamString + * @tc.desc: Verify ParseParamString with valid JSON. + */ +HWTEST_F(OhosAaCommandUtilTest, Ohos_Aa_ParseParamString_0100, Function | MediumTest | Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "Ohos_Aa_ParseParamString_0100"); + + char* argv[] = { + (char*)TOOL_NAME.c_str(), + (char*)"start", + (char*)"", + }; + int argc = sizeof(argv) / sizeof(argv[0]) - 1; + + ClawAaShellCommand cmd(argc, argv); + cmd.CreateErrorInfoMap(); + + char param[] = R"({"key1":"value1","key2":"value2"})"; + ::optarg = param; + + ParametersString ps; + ErrCode result = cmd.ParseParamString(ps); + EXPECT_EQ(result, OHOS::ERR_OK); + EXPECT_EQ(ps.size(), 2u); + EXPECT_EQ(ps["key1"], "value1"); + EXPECT_EQ(ps["key2"], "value2"); +} + +/** + * @tc.number: Ohos_Aa_ParseParamString_0200 + * @tc.name: ParseParamString + * @tc.desc: Verify ParseParamString with invalid JSON. + */ +HWTEST_F(OhosAaCommandUtilTest, Ohos_Aa_ParseParamString_0200, Function | MediumTest | Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "Ohos_Aa_ParseParamString_0200"); + + char* argv[] = { + (char*)TOOL_NAME.c_str(), + (char*)"start", + (char*)"", + }; + int argc = sizeof(argv) / sizeof(argv[0]) - 1; + + ClawAaShellCommand cmd(argc, argv); + cmd.CreateErrorInfoMap(); + + char param[] = "not_a_json"; + ::optarg = param; + + ParametersString ps; + ErrCode result = cmd.ParseParamString(ps); + EXPECT_EQ(result, OHOS::ERR_INVALID_VALUE); +} + +/** + * @tc.number: Ohos_Aa_ParseParamString_0300 + * @tc.name: ParseParamString + * @tc.desc: Verify ParseParamString with JSON wrapped in single quotes. + */ +HWTEST_F(OhosAaCommandUtilTest, Ohos_Aa_ParseParamString_0300, Function | MediumTest | Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "Ohos_Aa_ParseParamString_0300"); + + char* argv[] = { + (char*)TOOL_NAME.c_str(), + (char*)"start", + (char*)"", + }; + int argc = sizeof(argv) / sizeof(argv[0]) - 1; + + ClawAaShellCommand cmd(argc, argv); + cmd.CreateErrorInfoMap(); + + char param[] = R"('{"key1":"value1"}')"; + ::optarg = param; + + ParametersString ps; + ErrCode result = cmd.ParseParamString(ps); + EXPECT_EQ(result, OHOS::ERR_OK); + EXPECT_EQ(ps.size(), 1u); + EXPECT_EQ(ps["key1"], "value1"); +} + +// ==================== MatchOrderString tests ==================== + +/** + * @tc.number: Ohos_Aa_MatchOrderString_0100 + * @tc.name: MatchOrderString + * @tc.desc: Verify MatchOrderString with matching regex. + */ +HWTEST_F(OhosAaCommandUtilTest, Ohos_Aa_MatchOrderString_0100, Function | MediumTest | Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "Ohos_Aa_MatchOrderString_0100"); + + char* argv[] = { + (char*)TOOL_NAME.c_str(), + (char*)"start", + (char*)"", + }; + int argc = sizeof(argv) / sizeof(argv[0]) - 1; + + ClawAaShellCommand cmd(argc, argv); + std::regex regexDumpHeap(R"(^\s*(dumpheap)\s*$)"); + EXPECT_TRUE(cmd.MatchOrderString(regexDumpHeap, "dumpheap")); +} + +/** + * @tc.number: Ohos_Aa_MatchOrderString_0200 + * @tc.name: MatchOrderString + * @tc.desc: Verify MatchOrderString with non-matching regex. + */ +HWTEST_F(OhosAaCommandUtilTest, Ohos_Aa_MatchOrderString_0200, Function | MediumTest | Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "Ohos_Aa_MatchOrderString_0200"); + + char* argv[] = { + (char*)TOOL_NAME.c_str(), + (char*)"start", + (char*)"", + }; + int argc = sizeof(argv) / sizeof(argv[0]) - 1; + + ClawAaShellCommand cmd(argc, argv); + std::regex regexDumpHeap(R"(^\s*(dumpheap)\s*$)"); + EXPECT_FALSE(cmd.MatchOrderString(regexDumpHeap, "invalid_cmd")); +} + +/** + * @tc.number: Ohos_Aa_MatchOrderString_0300 + * @tc.name: MatchOrderString + * @tc.desc: Verify MatchOrderString with empty string returns false. + */ +HWTEST_F(OhosAaCommandUtilTest, Ohos_Aa_MatchOrderString_0300, Function | MediumTest | Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "Ohos_Aa_MatchOrderString_0300"); + + char* argv[] = { + (char*)TOOL_NAME.c_str(), + (char*)"start", + (char*)"", + }; + int argc = sizeof(argv) / sizeof(argv[0]) - 1; + + ClawAaShellCommand cmd(argc, argv); + std::regex regexDumpHeap(R"(^\s*(dumpheap)\s*$)"); + EXPECT_FALSE(cmd.MatchOrderString(regexDumpHeap, "")); +} + +/** + * @tc.number: Ohos_Aa_MatchOrderString_0400 + * @tc.name: MatchOrderString + * @tc.desc: Verify MatchOrderString with whitespace-padded matching command. + */ +HWTEST_F(OhosAaCommandUtilTest, Ohos_Aa_MatchOrderString_0400, Function | MediumTest | Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "Ohos_Aa_MatchOrderString_0400"); + + char* argv[] = { + (char*)TOOL_NAME.c_str(), + (char*)"start", + (char*)"", + }; + int argc = sizeof(argv) / sizeof(argv[0]) - 1; + + ClawAaShellCommand cmd(argc, argv); + std::regex regexDumpHeap(R"(^\s*(dumpheap)\s*$)"); + EXPECT_TRUE(cmd.MatchOrderString(regexDumpHeap, " dumpheap ")); +} + +// ==================== CheckPerfCmdString tests ==================== + +/** + * @tc.number: Ohos_Aa_CheckPerfCmdString_0100 + * @tc.name: CheckPerfCmdString + * @tc.desc: Verify CheckPerfCmdString with "dumpheap" command. + */ +HWTEST_F(OhosAaCommandUtilTest, Ohos_Aa_CheckPerfCmdString_0100, Function | MediumTest | Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "Ohos_Aa_CheckPerfCmdString_0100"); + + char* argv[] = { + (char*)TOOL_NAME.c_str(), + (char*)"start", + (char*)"", + }; + int argc = sizeof(argv) / sizeof(argv[0]) - 1; + + ClawAaShellCommand cmd(argc, argv); + std::string perfCmd; + EXPECT_TRUE(cmd.CheckPerfCmdString("dumpheap", 1024, perfCmd)); + EXPECT_EQ(perfCmd, "dumpheap"); +} + +/** + * @tc.number: Ohos_Aa_CheckPerfCmdString_0200 + * @tc.name: CheckPerfCmdString + * @tc.desc: Verify CheckPerfCmdString with "sleep" command. + */ +HWTEST_F(OhosAaCommandUtilTest, Ohos_Aa_CheckPerfCmdString_0200, Function | MediumTest | Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "Ohos_Aa_CheckPerfCmdString_0200"); + + char* argv[] = { + (char*)TOOL_NAME.c_str(), + (char*)"start", + (char*)"", + }; + int argc = sizeof(argv) / sizeof(argv[0]) - 1; + + ClawAaShellCommand cmd(argc, argv); + std::string perfCmd; + EXPECT_TRUE(cmd.CheckPerfCmdString("sleep", 1024, perfCmd)); + EXPECT_EQ(perfCmd, "sleep"); +} + +/** + * @tc.number: Ohos_Aa_CheckPerfCmdString_0300 + * @tc.name: CheckPerfCmdString + * @tc.desc: Verify CheckPerfCmdString with "sleep 5000" command. + */ +HWTEST_F(OhosAaCommandUtilTest, Ohos_Aa_CheckPerfCmdString_0300, Function | MediumTest | Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "Ohos_Aa_CheckPerfCmdString_0300"); + + char* argv[] = { + (char*)TOOL_NAME.c_str(), + (char*)"start", + (char*)"", + }; + int argc = sizeof(argv) / sizeof(argv[0]) - 1; + + ClawAaShellCommand cmd(argc, argv); + std::string perfCmd; + EXPECT_TRUE(cmd.CheckPerfCmdString("sleep 5000", 1024, perfCmd)); +} + +/** + * @tc.number: Ohos_Aa_CheckPerfCmdString_0400 + * @tc.name: CheckPerfCmdString + * @tc.desc: Verify CheckPerfCmdString with null optarg returns false. + */ +HWTEST_F(OhosAaCommandUtilTest, Ohos_Aa_CheckPerfCmdString_0400, Function | MediumTest | Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "Ohos_Aa_CheckPerfCmdString_0400"); + + char* argv[] = { + (char*)TOOL_NAME.c_str(), + (char*)"start", + (char*)"", + }; + int argc = sizeof(argv) / sizeof(argv[0]) - 1; + + ClawAaShellCommand cmd(argc, argv); + std::string perfCmd; + EXPECT_FALSE(cmd.CheckPerfCmdString(nullptr, 1024, perfCmd)); +} + +/** + * @tc.number: Ohos_Aa_CheckPerfCmdString_0500 + * @tc.name: CheckPerfCmdString + * @tc.desc: Verify CheckPerfCmdString with string exceeding max length returns false. + */ +HWTEST_F(OhosAaCommandUtilTest, Ohos_Aa_CheckPerfCmdString_0500, Function | MediumTest | Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "Ohos_Aa_CheckPerfCmdString_0500"); + + char* argv[] = { + (char*)TOOL_NAME.c_str(), + (char*)"start", + (char*)"", + }; + int argc = sizeof(argv) / sizeof(argv[0]) - 1; + + ClawAaShellCommand cmd(argc, argv); + std::string perfCmd; + // Create a string longer than paramLength (10) + std::string longString(20, 'a'); + EXPECT_FALSE(cmd.CheckPerfCmdString(longString.c_str(), 10, perfCmd)); +} + +/** + * @tc.number: Ohos_Aa_CheckPerfCmdString_0600 + * @tc.name: CheckPerfCmdString + * @tc.desc: Verify CheckPerfCmdString with invalid command. + */ +HWTEST_F(OhosAaCommandUtilTest, Ohos_Aa_CheckPerfCmdString_0600, Function | MediumTest | Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "Ohos_Aa_CheckPerfCmdString_0600"); + + char* argv[] = { + (char*)TOOL_NAME.c_str(), + (char*)"start", + (char*)"", + }; + int argc = sizeof(argv) / sizeof(argv[0]) - 1; + + ClawAaShellCommand cmd(argc, argv); + std::string perfCmd; + EXPECT_FALSE(cmd.CheckPerfCmdString("invalid_cmd", 1024, perfCmd)); +} + +/** + * @tc.number: Ohos_Aa_CheckPerfCmdString_0700 + * @tc.name: CheckPerfCmdString + * @tc.desc: Verify CheckPerfCmdString with "profile nativeperf" command. + */ +HWTEST_F(OhosAaCommandUtilTest, Ohos_Aa_CheckPerfCmdString_0700, Function | MediumTest | Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "Ohos_Aa_CheckPerfCmdString_0700"); + + char* argv[] = { + (char*)TOOL_NAME.c_str(), + (char*)"start", + (char*)"", + }; + int argc = sizeof(argv) / sizeof(argv[0]) - 1; + + ClawAaShellCommand cmd(argc, argv); + std::string perfCmd; + EXPECT_TRUE(cmd.CheckPerfCmdString("profile nativeperf", 1024, perfCmd)); +} + +/** + * @tc.number: Ohos_Aa_CheckPerfCmdString_0800 + * @tc.name: CheckPerfCmdString + * @tc.desc: Verify CheckPerfCmdString with "profile jsperf" command. + */ +HWTEST_F(OhosAaCommandUtilTest, Ohos_Aa_CheckPerfCmdString_0800, Function | MediumTest | Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "Ohos_Aa_CheckPerfCmdString_0800"); + + char* argv[] = { + (char*)TOOL_NAME.c_str(), + (char*)"start", + (char*)"", + }; + int argc = sizeof(argv) / sizeof(argv[0]) - 1; + + ClawAaShellCommand cmd(argc, argv); + std::string perfCmd; + EXPECT_TRUE(cmd.CheckPerfCmdString("profile jsperf", 1024, perfCmd)); +} + +/** + * @tc.number: Ohos_Aa_CheckPerfCmdString_0900 + * @tc.name: CheckPerfCmdString + * @tc.desc: Verify CheckPerfCmdString with "baseLineProfile" command. + */ +HWTEST_F(OhosAaCommandUtilTest, Ohos_Aa_CheckPerfCmdString_0900, Function | MediumTest | Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "Ohos_Aa_CheckPerfCmdString_0900"); + + char* argv[] = { + (char*)TOOL_NAME.c_str(), + (char*)"start", + (char*)"", + }; + int argc = sizeof(argv) / sizeof(argv[0]) - 1; + + ClawAaShellCommand cmd(argc, argv); + std::string perfCmd; + EXPECT_TRUE(cmd.CheckPerfCmdString("baseLineProfile", 1024, perfCmd)); +} + +// ==================== IsImplicitStartAction tests ==================== + +/** + * @tc.number: Ohos_Aa_IsImplicitStartAction_0100 + * @tc.name: IsImplicitStartAction + * @tc.desc: Verify that a Want with abilityName is NOT implicit. + */ +HWTEST_F(OhosAaCommandUtilTest, Ohos_Aa_IsImplicitStartAction_0100, Function | MediumTest | Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "Ohos_Aa_IsImplicitStartAction_0100"); + + char* argv[] = { + (char*)TOOL_NAME.c_str(), + (char*)"start", + (char*)"", + }; + int argc = sizeof(argv) / sizeof(argv[0]) - 1; + + ClawAaShellCommand cmd(argc, argv); + Want want; + ElementName element("", "com.example", "TestAbility"); + want.SetElement(element); + EXPECT_FALSE(cmd.IsImplicitStartAction(want)); +} + +/** + * @tc.number: Ohos_Aa_IsImplicitStartAction_0200 + * @tc.name: IsImplicitStartAction + * @tc.desc: Verify that a Want without abilityName and with action is implicit. + */ +HWTEST_F(OhosAaCommandUtilTest, Ohos_Aa_IsImplicitStartAction_0200, Function | MediumTest | Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "Ohos_Aa_IsImplicitStartAction_0200"); + + char* argv[] = { + (char*)TOOL_NAME.c_str(), + (char*)"start", + (char*)"", + }; + int argc = sizeof(argv) / sizeof(argv[0]) - 1; + + ClawAaShellCommand cmd(argc, argv); + Want want; + want.SetAction("ohos.want.action.someAction"); + EXPECT_TRUE(cmd.IsImplicitStartAction(want)); +} + +/** + * @tc.number: Ohos_Aa_IsImplicitStartAction_0300 + * @tc.name: IsImplicitStartAction + * @tc.desc: Verify that a Want with "ohos.want.action.select" action is NOT implicit (black action). + */ +HWTEST_F(OhosAaCommandUtilTest, Ohos_Aa_IsImplicitStartAction_0300, Function | MediumTest | Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "Ohos_Aa_IsImplicitStartAction_0300"); + + char* argv[] = { + (char*)TOOL_NAME.c_str(), + (char*)"start", + (char*)"", + }; + int argc = sizeof(argv) / sizeof(argv[0]) - 1; + + ClawAaShellCommand cmd(argc, argv); + Want want; + want.SetAction("ohos.want.action.select"); + EXPECT_FALSE(cmd.IsImplicitStartAction(want)); +} + +// ==================== FormatOutputForWithWait tests ==================== + +/** + * @tc.number: Ohos_Aa_FormatOutputForWithWait_0100 + * @tc.name: FormatOutputForWithWait + * @tc.desc: Verify FormatOutputForWithWait with TERMINATE_FOR_NONE. + */ +HWTEST_F(OhosAaCommandUtilTest, Ohos_Aa_FormatOutputForWithWait_0100, Function | MediumTest | Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "Ohos_Aa_FormatOutputForWithWait_0100"); + + char* argv[] = { + (char*)TOOL_NAME.c_str(), + (char*)"start", + (char*)"", + }; + int argc = sizeof(argv) / sizeof(argv[0]) - 1; + + ClawAaShellCommand cmd(argc, argv); + cmd.CreateErrorInfoMap(); + + Want want; + AbilityStartWithWaitObserverData data; + data.reason = static_cast( + AbilityStartWithWaitObserverUtil::TerminateReason::TERMINATE_FOR_NONE); + data.startTime = 1000; + data.foregroundTime = 2000; + data.coldStart = true; + data.bundleName = "com.example"; + data.abilityName = "TestAbility"; + + cmd.FormatOutputForWithWait(want, data); + std::string result = cmd.resultReceiver_; + EXPECT_NE(result.find("StartMode: Cold"), std::string::npos); + EXPECT_NE(result.find("BundleName: com.example"), std::string::npos); + EXPECT_NE(result.find("AbilityName: TestAbility"), std::string::npos); + EXPECT_NE(result.find("TotalTime:"), std::string::npos); + EXPECT_NE(result.find("WaitTime:"), std::string::npos); +} + +/** + * @tc.number: Ohos_Aa_FormatOutputForWithWait_0200 + * @tc.name: FormatOutputForWithWait + * @tc.desc: Verify FormatOutputForWithWait with TERMINATE_FOR_NON_UI_ABILITY. + */ +HWTEST_F(OhosAaCommandUtilTest, Ohos_Aa_FormatOutputForWithWait_0200, Function | MediumTest | Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "Ohos_Aa_FormatOutputForWithWait_0200"); + + char* argv[] = { + (char*)TOOL_NAME.c_str(), + (char*)"start", + (char*)"", + }; + int argc = sizeof(argv) / sizeof(argv[0]) - 1; + + ClawAaShellCommand cmd(argc, argv); + cmd.CreateErrorInfoMap(); + + Want want; + AbilityStartWithWaitObserverData data; + data.reason = static_cast( + AbilityStartWithWaitObserverUtil::TerminateReason::TERMINATE_FOR_NON_UI_ABILITY); + + cmd.FormatOutputForWithWait(want, data); + std::string result = cmd.resultReceiver_; + EXPECT_NE(result.find("The wait option does not support starting non-uiability"), std::string::npos); +} + +/** + * @tc.number: Ohos_Aa_FormatOutputForWithWait_0300 + * @tc.name: FormatOutputForWithWait + * @tc.desc: Verify FormatOutputForWithWait with hot start. + */ +HWTEST_F(OhosAaCommandUtilTest, Ohos_Aa_FormatOutputForWithWait_0300, Function | MediumTest | Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "Ohos_Aa_FormatOutputForWithWait_0300"); + + char* argv[] = { + (char*)TOOL_NAME.c_str(), + (char*)"start", + (char*)"", + }; + int argc = sizeof(argv) / sizeof(argv[0]) - 1; + + ClawAaShellCommand cmd(argc, argv); + cmd.CreateErrorInfoMap(); + + Want want; + AbilityStartWithWaitObserverData data; + data.reason = static_cast( + AbilityStartWithWaitObserverUtil::TerminateReason::TERMINATE_FOR_NONE); + data.startTime = 1000; + data.foregroundTime = 2000; + data.coldStart = false; + data.bundleName = "com.example"; + data.abilityName = "TestAbility"; + + cmd.FormatOutputForWithWait(want, data); + std::string result = cmd.resultReceiver_; + EXPECT_NE(result.find("StartMode: Hot"), std::string::npos); +} + +/** + * @tc.number: Ohos_Aa_FormatOutputForWithWait_0400 + * @tc.name: FormatOutputForWithWait + * @tc.desc: Verify FormatOutputForWithWait with module name set. + */ +HWTEST_F(OhosAaCommandUtilTest, Ohos_Aa_FormatOutputForWithWait_0400, Function | MediumTest | Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "Ohos_Aa_FormatOutputForWithWait_0400"); + + char* argv[] = { + (char*)TOOL_NAME.c_str(), + (char*)"start", + (char*)"", + }; + int argc = sizeof(argv) / sizeof(argv[0]) - 1; + + ClawAaShellCommand cmd(argc, argv); + cmd.CreateErrorInfoMap(); + + Want want; + ElementName element("", "com.example", "TestAbility", "entry"); + want.SetElement(element); + AbilityStartWithWaitObserverData data; + data.reason = static_cast( + AbilityStartWithWaitObserverUtil::TerminateReason::TERMINATE_FOR_NONE); + data.startTime = 1000; + data.foregroundTime = 2000; + data.coldStart = true; + data.bundleName = "com.example"; + data.abilityName = "TestAbility"; + + cmd.FormatOutputForWithWait(want, data); + std::string result = cmd.resultReceiver_; + EXPECT_NE(result.find("ModuleName: entry"), std::string::npos); +} + +// ==================== StartAbilityWithWait tests ==================== + +/** + * @tc.number: Ohos_Aa_StartAbilityWithWait_0100 + * @tc.name: StartAbilityWithWait + * @tc.desc: Verify StartAbilityWithWait with implicit start action returns normally. + */ +HWTEST_F(OhosAaCommandUtilTest, Ohos_Aa_StartAbilityWithWait_0100, Function | MediumTest | Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "Ohos_Aa_StartAbilityWithWait_0100"); + + char* argv[] = { + (char*)TOOL_NAME.c_str(), + (char*)"start", + (char*)"", + }; + int argc = sizeof(argv) / sizeof(argv[0]) - 1; + + ClawAaShellCommand cmd(argc, argv); + cmd.CreateErrorInfoMap(); + + Want want; + want.SetAction("ohos.want.action.someAction"); + ErrCode result = cmd.StartAbilityWithWait(want); + EXPECT_EQ(result, ERR_OK); +} + +// ==================== ConvertPid tests ==================== + +/** + * @tc.number: Ohos_Aa_ConvertPid_0100 + * @tc.name: ConvertPid + * @tc.desc: Verify ConvertPid with valid numeric string. + */ +HWTEST_F(OhosAaCommandUtilTest, Ohos_Aa_ConvertPid_0100, Function | MediumTest | Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "Ohos_Aa_ConvertPid_0100"); + + char* argv[] = { + (char*)TOOL_NAME.c_str(), + (char*)"start", + (char*)"", + }; + int argc = sizeof(argv) / sizeof(argv[0]) - 1; + + ClawAaShellCommand cmd(argc, argv); + std::string pidStr = "12345"; + pid_t pid = cmd.ConvertPid(pidStr); + EXPECT_EQ(pid, 12345); +} + +/** + * @tc.number: Ohos_Aa_ConvertPid_0200 + * @tc.name: ConvertPid + * @tc.desc: Verify ConvertPid with invalid string returns 0. + */ +HWTEST_F(OhosAaCommandUtilTest, Ohos_Aa_ConvertPid_0200, Function | MediumTest | Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "Ohos_Aa_ConvertPid_0200"); + + char* argv[] = { + (char*)TOOL_NAME.c_str(), + (char*)"start", + (char*)"", + }; + int argc = sizeof(argv) / sizeof(argv[0]) - 1; + + ClawAaShellCommand cmd(argc, argv); + std::string pidStr = "not_a_number"; + pid_t pid = cmd.ConvertPid(pidStr); + EXPECT_EQ(pid, 0); +} + +// ==================== CheckStartAbilityResult tests ==================== + +/** + * @tc.number: Ohos_Aa_CheckStartAbilityResult_0100 + * @tc.name: CheckStartAbilityResult + * @tc.desc: Verify CheckStartAbilityResult with known error code. + */ +HWTEST_F(OhosAaCommandUtilTest, Ohos_Aa_CheckStartAbilityResult_0100, Function | MediumTest | Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "Ohos_Aa_CheckStartAbilityResult_0100"); + + char* argv[] = { + (char*)TOOL_NAME.c_str(), + (char*)"start", + (char*)"", + }; + int argc = sizeof(argv) / sizeof(argv[0]) - 1; + + ClawAaShellCommand cmd(argc, argv); + cmd.CreateErrorInfoMap(); + + ErrCode result = RESOLVE_ABILITY_ERR; + cmd.CheckStartAbilityResult(result); + EXPECT_EQ(result, RESOLVE_ABILITY_ERR); +} + +/** + * @tc.number: Ohos_Aa_CheckStartAbilityResult_0200 + * @tc.name: CheckStartAbilityResult + * @tc.desc: Verify CheckStartAbilityResult with unknown error code sets INNER_ERR. + */ +HWTEST_F(OhosAaCommandUtilTest, Ohos_Aa_CheckStartAbilityResult_0200, Function | MediumTest | Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "Ohos_Aa_CheckStartAbilityResult_0200"); + + char* argv[] = { + (char*)TOOL_NAME.c_str(), + (char*)"start", + (char*)"", + }; + int argc = sizeof(argv) / sizeof(argv[0]) - 1; + + ClawAaShellCommand cmd(argc, argv); + cmd.CreateErrorInfoMap(); + + ErrCode result = 999999; // Unknown error code + cmd.CheckStartAbilityResult(result); + EXPECT_EQ(result, INNER_ERR); +} + +// ==================== GetErrorInfoFromCode tests ==================== + +/** + * @tc.number: Ohos_Aa_GetErrorInfoFromCode_0100 + * @tc.name: GetErrorInfoFromCode + * @tc.desc: Verify GetErrorInfoFromCode with known error code. + */ +HWTEST_F(OhosAaCommandUtilTest, Ohos_Aa_GetErrorInfoFromCode_0100, Function | MediumTest | Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "Ohos_Aa_GetErrorInfoFromCode_0100"); + + char* argv[] = { + (char*)TOOL_NAME.c_str(), + (char*)"start", + (char*)"", + }; + int argc = sizeof(argv) / sizeof(argv[0]) - 1; + + ClawAaShellCommand cmd(argc, argv); + cmd.CreateErrorInfoMap(); + + AaToolErrorInfo info = cmd.GetErrorInfoFromCode(RESOLVE_ABILITY_ERR); + EXPECT_EQ(info.code, "ERR_ABILITY_NOT_FOUND"); + EXPECT_FALSE(info.message.empty()); +} + +/** + * @tc.number: Ohos_Aa_GetErrorInfoFromCode_0200 + * @tc.name: GetErrorInfoFromCode + * @tc.desc: Verify GetErrorInfoFromCode with unknown error code returns empty. + */ +HWTEST_F(OhosAaCommandUtilTest, Ohos_Aa_GetErrorInfoFromCode_0200, Function | MediumTest | Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "Ohos_Aa_GetErrorInfoFromCode_0200"); + + char* argv[] = { + (char*)TOOL_NAME.c_str(), + (char*)"start", + (char*)"", + }; + int argc = sizeof(argv) / sizeof(argv[0]) - 1; + + ClawAaShellCommand cmd(argc, argv); + cmd.CreateErrorInfoMap(); + + AaToolErrorInfo info = cmd.GetErrorInfoFromCode(999999); + EXPECT_TRUE(info.code.empty()); + EXPECT_TRUE(info.message.empty()); +} + +// ==================== CreateCommandMap tests ==================== + +/** + * @tc.number: Ohos_Aa_CreateCommandMap_0100 + * @tc.name: CreateCommandMap + * @tc.desc: Verify CreateCommandMap creates expected command entries. + */ +HWTEST_F(OhosAaCommandUtilTest, Ohos_Aa_CreateCommandMap_0100, Function | MediumTest | Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "Ohos_Aa_CreateCommandMap_0100"); + + char* argv[] = { + (char*)TOOL_NAME.c_str(), + (char*)"start", + (char*)"", + }; + int argc = sizeof(argv) / sizeof(argv[0]) - 1; + + ClawAaShellCommand cmd(argc, argv); + ErrCode result = cmd.CreateCommandMap(); + EXPECT_EQ(result, ERR_OK); + // Verify commandMap_ contains expected entries + EXPECT_NE(cmd.commandMap_.find("--help"), cmd.commandMap_.end()); + EXPECT_NE(cmd.commandMap_.find("help"), cmd.commandMap_.end()); + EXPECT_NE(cmd.commandMap_.find("start"), cmd.commandMap_.end()); + EXPECT_NE(cmd.commandMap_.find("force-stop"), cmd.commandMap_.end()); +} + +// ==================== CreateErrorInfoMap tests ==================== + +/** + * @tc.number: Ohos_Aa_CreateErrorInfoMap_0100 + * @tc.name: CreateErrorInfoMap + * @tc.desc: Verify CreateErrorInfoMap creates expected error entries. + */ +HWTEST_F(OhosAaCommandUtilTest, Ohos_Aa_CreateErrorInfoMap_0100, Function | MediumTest | Level1) +{ + TAG_LOGI(AAFwkTag::TEST, "Ohos_Aa_CreateErrorInfoMap_0100"); + + char* argv[] = { + (char*)TOOL_NAME.c_str(), + (char*)"start", + (char*)"", + }; + int argc = sizeof(argv) / sizeof(argv[0]) - 1; + + ClawAaShellCommand cmd(argc, argv); + ErrCode result = cmd.CreateErrorInfoMap(); + EXPECT_EQ(result, ERR_OK); + // Verify errorInfoMap_ contains known error codes + EXPECT_NE(cmd.errorInfoMap_.find(RESOLVE_ABILITY_ERR), cmd.errorInfoMap_.end()); + EXPECT_NE(cmd.errorInfoMap_.find(KILL_PROCESS_FAILED), cmd.errorInfoMap_.end()); + EXPECT_NE(cmd.errorInfoMap_.find(INNER_ERR_START), cmd.errorInfoMap_.end()); +} diff --git a/tools/ohos-arktsScript/BUILD.gn b/tools/ohos-arktsScript/BUILD.gn new file mode 100644 index 0000000000..fb7abacca7 --- /dev/null +++ b/tools/ohos-arktsScript/BUILD.gn @@ -0,0 +1,111 @@ +# 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/cli/ohos_cli_executable.gni") +import("//foundation/ability/ability_runtime/ability_runtime.gni") + +config("ohos-arkts-config") { + include_dirs = [ + "include", + "${ability_runtime_innerkits_path}/runtime/include", + "${ability_runtime_path}/interfaces/kits/native/appkit/ability_runtime/context", + "${ability_runtime_path}/interfaces/kits/native/appkit/ability_bundle_manager_helper", + "${ability_runtime_innerkits_path}/app_manager/include/appmgr", + "${ability_runtime_innerkits_path}/ability_manager/include", + ] + cflags_cc = [ "-fexceptions" ] +} + +arkts_script_deps = [ + "${ability_runtime_innerkits_path}/runtime:runtime", + "${ability_runtime_native_path}/appkit:appkit_manager_helper", + "${ability_runtime_native_path}/appkit:app_context", + "${ability_runtime_native_path}/appkit:app_context_utils", +] + +arkts_script_external_deps = [ + "ability_base:base", + "bundle_framework:appexecfwk_core", + "bundle_framework:appexecfwk_base", + "c_utils:utils", + "eventhandler:libeventhandler", + "hilog:libhilog", + "napi:ace_napi", + "ipc:ipc_core", + "json:nlohmann_json_static", +] + +ohos_shared_library("arkts_script") { + output_name = "arkts_script" + + use_exceptions = true + sanitize = { + cfi = true + cfi_cross_dso = true + cfi_policy = "adaptive" + debug = false + } + branch_protector_ret = "pac_ret" + + sources = [ + "src/arkts_script.cpp", + "src/js_arkts_script.cpp", + ] + + configs = [ + ":ohos-arkts-config", + ] + + public_configs = [ + ":ohos-arkts-config", + ] + + deps = arkts_script_deps + + external_deps = arkts_script_external_deps + + install_enable = true + subsystem_name = "ability" + part_name = "ability_runtime" +} + +ohos_cli_executable("ohos-arkTSScript") { + use_exceptions = true + sanitize = { + cfi = true + cfi_cross_dso = true + cfi_policy = "adaptive" + debug = false + } + + branch_protector_ret = "pac_ret" + + cli_config_file = "config.json" + + sources = [ + "src/main.cpp", + ] + + configs = [ + ":ohos-arkts-config", + ] + + deps = [ ":arkts_script" ] + arkts_script_deps + + external_deps = arkts_script_external_deps + + install_enable = true + install_images = [ "system" ] + subsystem_name = "ability" + part_name = "ability_runtime" +} diff --git a/tools/ohos-arktsScript/config.json b/tools/ohos-arktsScript/config.json new file mode 100644 index 0000000000..436cc430dd --- /dev/null +++ b/tools/ohos-arktsScript/config.json @@ -0,0 +1,60 @@ +{ + "name": "ohos-arkTSScript", + "version": "1.0.0", + "description": "Run a specified function from an ArkTS script ABC file", + "executablePath": "/system/bin/cli_tool/executable/ohos-arkTSScript", + "requirePermissions": [], + + "inputSchema": { + "type": "object", + "description": "Tool input parameters", + "properties": { + "help": { + "type": "boolean", + "description": "Display this help message", + "default": false + }, + "abcPath": { + "type": "string", + "description": "ABC file path" + }, + "scriptPath": { + "type": "string", + "description": "Optional script file or class name" + }, + "functionName": { + "type": "string", + "description": "Function name to execute" + }, + "args": { + "type": "string", + "description": "JSON object string for function arguments, for example {\"arg0\":10,\"arg1\":20}" + } + }, + "required": [ + "abcPath", + "functionName" + ] + }, + "outputSchema": { + "type": "object", + "properties": { + "code": { + "type": "integer" + }, + "result": { + "type": "object", + "properties": {} + }, + "uri": { + "type": "string" + }, + "flag": { + "type": "integer" + } + }, + "required": [ + "code" + ] + } +} diff --git a/tools/ohos-arktsScript/include/arkts_script.h b/tools/ohos-arktsScript/include/arkts_script.h new file mode 100644 index 0000000000..dd03608cae --- /dev/null +++ b/tools/ohos-arktsScript/include/arkts_script.h @@ -0,0 +1,143 @@ +/* + * 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 ARKTSSCRIPT_H +#define ARKTSSCRIPT_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "context_impl.h" +#include "js_runtime.h" +#include "napi/native_api.h" + +namespace OHOS { +namespace ArktsScript { + +using ContextImpl = OHOS::AbilityRuntime::ContextImpl; +using JsRuntime = OHOS::AbilityRuntime::JsRuntime; + +enum class ScriptArgType { + UNDEFINED, + STRING, + INT32, + DOUBLE, + BOOLEAN, + JSON_VALUE, +}; + +struct ScriptArg { + ScriptArgType type = ScriptArgType::UNDEFINED; + std::string value; +}; + +struct ScriptArgs { + std::string abcPath; + std::string scriptName; + std::string funName; + std::vector arguments; + bool showHelp = false; +}; + +struct ScriptError { + std::string message; + std::string type; +}; + +struct ExecutionContext { + std::atomic resultReady{false}; + std::atomic scriptDone{false}; + std::mutex mutex; + std::string result; + ScriptError error; +}; + +using ResultCallback = std::function; + +struct CompletionChannel { + int eventFd = -1; + int epollFd = -1; +}; + +struct ExitState { + std::shared_ptr execContext; +}; + +struct ExecutionSnapshot { + std::string result; + ScriptError error; + bool resultReady = false; + bool scriptDone = false; +}; + +class ArktsScript final { +public: + ArktsScript() = delete; + ~ArktsScript() = delete; + + static bool ParseArguments(int argc, char* argv[], ScriptArgs& args); + static std::shared_ptr CreateScriptContext(); + static std::unique_ptr CreateJsRuntime(const std::shared_ptr& context); + static bool LoadAbcFile(JsRuntime* runtime, const std::string& path); + static void OutputResult(const std::string& result); + static void OutputError(const ScriptError& error); + static int RunArkTsScript(int argc, char* argv[]); + +private: + static bool ParseHelpOption(int argc, char* argv[], ScriptArgs& args); + static bool ParseScriptOption(int argc, char* argv[], int& index, ScriptArgs& args, + std::map& indexedArgs, bool& hasArgsJson); + static bool FinalizeParsedArguments(const std::map& indexedArgs, + ScriptArgs& args); + static bool ParseFunctionAndArguments(int argc, char* argv[], int& index, ScriptArgs& args); + static void PrintUsage(); + static void CloseCompletionChannel(CompletionChannel& channel); + static bool CreateCompletionChannel(CompletionChannel& channel); + static bool SignalCompletion(int eventFd); + static bool PublishResult(const std::shared_ptr& execContext, int eventFd, + const std::string& result); + static bool PublishFailure(const std::shared_ptr& execContext, int eventFd, + const ScriptError& error); + static bool MarkScriptDone(const std::shared_ptr& execContext, int eventFd); + static void ReadCompletionSignal(const CompletionChannel& channel); + static ExecutionSnapshot TakeExecutionSnapshot(const std::shared_ptr& execContext); + static void HandleExecutionSnapshot(const ExecutionSnapshot& snapshot, const CompletionChannel& channel); + static void MonitorCompletion(ExitState state, CompletionChannel channel); + static bool StartMonitor(CompletionChannel& channel, std::shared_ptr& execContext, + std::thread& monitorThread); + static int FinalizeAndJoin(const std::shared_ptr& execContext, CompletionChannel& channel, + std::thread& monitorThread, const ScriptError& error); + static ResultCallback CreateCompleteCallback(const std::shared_ptr& execContext, + CompletionChannel channel); + static bool PrepareRuntimeEnvironment(const ResultCallback& completeCallback, + std::shared_ptr& context, std::unique_ptr& runtimeOwner, ScriptError& error); + static bool LoadScriptFile(const ScriptArgs& args, JsRuntime* runtime, ScriptError& error); + static bool ResolveScriptFunction(const ScriptArgs& args, JsRuntime* runtime, + napi_value& receiver, napi_value& func, ScriptError& error); + static bool CallResolvedFunction(const ScriptArgs& args, napi_env env, napi_value receiver, napi_value func, + ScriptError& error); + [[noreturn]] static void ExitFromMonitor(const CompletionChannel& channel, int exitCode); +}; + +} // namespace ArktsScript +} // namespace OHOS + +#endif // ARKTSSCRIPT_H diff --git a/tools/ohos-arktsScript/include/js_arkts_script.h b/tools/ohos-arktsScript/include/js_arkts_script.h new file mode 100644 index 0000000000..fc1b4c5d82 --- /dev/null +++ b/tools/ohos-arktsScript/include/js_arkts_script.h @@ -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. + */ + +#ifndef ARKTSSCRIPT_JS_H +#define ARKTSSCRIPT_JS_H + +#include +#include +#include + +#include "arkts_script.h" +#include "context.h" +#include "napi/native_api.h" +#include "nlohmann/json.hpp" + +namespace OHOS { +namespace AppExecFwk { +struct ApplicationInfo; +} +namespace ArktsScript { + +class JsArktsScript final { +public: + JsArktsScript() = delete; + ~JsArktsScript() = delete; + + static bool BindContextToGlobal(napi_env env, const std::shared_ptr& context); + static bool BindCompletearktsScript(napi_env env, const ResultCallback& callback); + static bool ResolveFunction(JsRuntime* runtime, napi_env env, const std::string& abcPath, + const std::string& scriptName, const std::string& funName, napi_value& receiver, napi_value& func); + static napi_value ConvertArgumentsToNapi(napi_env env, const std::vector& arguments); + static std::string StringifyObject(napi_env env, napi_value result); + +private: + struct CompletePayload { + int32_t code = 0; + nlohmann::json resultJson; + bool hasResult = false; + std::string uri; + bool hasUri = false; + int32_t flag = 0; + bool hasFlag = false; + }; + + static std::string NormalizeScriptName(const std::string& scriptName); + static bool ResolveFunctionFromExports(JsRuntime* runtime, napi_env env, const std::string& abcPath, + const std::string& scriptName, const std::string& funName, napi_value& receiver, napi_value& func); + static bool ResolveFunctionFromGlobal(napi_env env, const std::string& scriptName, const std::string& funName, + napi_value& receiver, napi_value& func); + static bool SetNamedStringOrNull(napi_env env, napi_value object, const char* name, const std::string& value); + static bool SetNamedNull(napi_env env, napi_value object, const char* name); + static napi_value CreateApplicationInfoObject(napi_env env, + const std::shared_ptr& appInfo); + static void SetApplicationInfoProperty(napi_env env, napi_value object, + const std::shared_ptr& context); + static void SetNullContextProperties(napi_env env, napi_value object); + static void SetContextDirectoryProperties(napi_env env, napi_value object, + const std::shared_ptr& context); + static napi_value CreateApplicationContextCallback(napi_env callbackEnv, napi_callback_info info); + static napi_value CreateBundleContextCallback(napi_env callbackEnv, napi_callback_info info); + static void BindContextFunction(napi_env env, napi_value object, const char* name, napi_callback callback); + static napi_value CreateScriptContextObject(napi_env env, + const std::shared_ptr& context, + const std::string& bundleNameOverride = std::string()); + static bool GetUtf8String(napi_env env, napi_value value, std::string& output); + static bool GetInt32Property(napi_env env, napi_value object, const char* name, int32_t& output); + static bool GetOptionalInt32Property(napi_env env, napi_value object, const char* name, + int32_t& output, bool& hasValue); + static bool GetOptionalStringProperty(napi_env env, napi_value object, const char* name, + std::string& output, bool& hasValue); + static bool GetOptionalJsonObjectProperty(napi_env env, napi_value object, const char* name, + nlohmann::json& output, bool& hasValue); + static bool ReadCompletePayload(napi_env env, napi_value value, CompletePayload& payload, std::string& error); + static nlohmann::json BuildCompletePayloadJson(const CompletePayload& payload); + static void ClearCompleteCallbackData(); + static void FinishCompleteCallback(bool success, const std::string& result, const ScriptError& error); + static napi_value CompletearktsScriptWithError(const std::string& error); + static napi_value CompletearktsScript(napi_env env, napi_callback_info info); +}; + +} // namespace ArktsScript +} // namespace OHOS + +#endif // ARKTSSCRIPT_JS_H diff --git a/tools/ohos-arktsScript/src/arkts_script.cpp b/tools/ohos-arktsScript/src/arkts_script.cpp new file mode 100644 index 0000000000..821471a0db --- /dev/null +++ b/tools/ohos-arktsScript/src/arkts_script.cpp @@ -0,0 +1,806 @@ +/* + * 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 "arkts_script.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "bundle_mgr_helper.h" +#include "hilog_tag_wrapper.h" +#include "js_arkts_script.h" +#include "nlohmann/json.hpp" + +namespace OHOS { +namespace ArktsScript { +using OHOS::AbilityRuntime::JsRuntime; +namespace { + +constexpr uint64_t SIGNAL_VALUE = 1; +constexpr int MAX_EVENTS = 1; +constexpr size_t CALL_ARGC = 1; +constexpr char ABC_PATH_OPTION[] = "--abcPath"; +constexpr char SCRIPT_PATH_OPTION[] = "--scriptPath"; +constexpr char FUNCTION_NAME_OPTION[] = "--functionName"; +constexpr char ARGS_OPTION[] = "--args"; +constexpr char ARG_NAME_PREFIX[] = "arg"; +constexpr size_t ARG_NAME_PREFIX_LENGTH = sizeof(ARG_NAME_PREFIX) - 1; +constexpr size_t SINGLE_QUOTE_WRAP_LENGTH = 2; + +constexpr int32_t SCRIPT_CONTEXT_BUNDLE_INFO_FLAGS = + static_cast(AppExecFwk::GetBundleInfoFlag::GET_BUNDLE_INFO_WITH_APPLICATION); + +const std::string HELP_MSG = + "ohos-arktsScript - ArkTS script execution tool for loading ABC files and invoking target functions\n\n" + "Usage:\n" + " ohos-arktsScript [options]\n\n" + "Parameters:\n" + " --abcPath ABC file path to load (required)\n" + " --scriptPath Script file or class name used to locate the target module (optional)\n" + " --functionName Function or method name to execute (required)\n" + " --args JSON object arguments passed to the function " + "(optional, keys must be argN, such as {\"arg0\":10,\"arg1\":20})\n" + " --help Display this help message\n\n" + "Examples:\n" + " # Execute an exported function from an ABC file\n" + " ohos-arktsScript --abcPath /data/test/module.abc --functionName run\n\n" + " # Execute a method from a specified script class\n" + " ohos-arktsScript --abcPath /data/test/module.abc --scriptPath TestScript.ets " + "--functionName calculate --args '{\"arg0\":10,\"arg1\":20}'\n\n" + " # Execute with JSON object and array arguments\n" + " ohos-arktsScript --abcPath /data/test/module.abc --functionName parseValues " + "--args '{\"arg0\":{\"name\":\"tool\"},\"arg1\":[\"a\",\"b\"]}'\n"; + +struct StringOptionBinding { + const char* optionName; + std::string ScriptArgs::*field; +}; + +constexpr std::array STRING_OPTION_BINDINGS = {{ + { ABC_PATH_OPTION, &ScriptArgs::abcPath }, + { SCRIPT_PATH_OPTION, &ScriptArgs::scriptName }, + { FUNCTION_NAME_OPTION, &ScriptArgs::funName }, +}}; + +bool InitScriptContextFromBundleInfo(const std::shared_ptr& scriptContext, + const AppExecFwk::BundleInfo& bundleInfo) +{ + if (scriptContext == nullptr) { + return false; + } + if (bundleInfo.applicationInfo.name.empty() && bundleInfo.applicationInfo.bundleName.empty()) { + TAG_LOGE(AAFwkTag::APPKIT, "caller applicationInfo empty"); + return false; + } + + scriptContext->SetApplicationInfo(std::make_shared(bundleInfo.applicationInfo)); + scriptContext->SetProcessName(bundleInfo.applicationInfo.process); + TAG_LOGD(AAFwkTag::APPKIT, + "script context initialized from bundleName=%{public}s", + bundleInfo.applicationInfo.bundleName.c_str()); + return true; +} + +bool InitScriptContextFromCaller(const std::shared_ptr& scriptContext) +{ + auto bundleMgrHelper = DelayedSingleton::GetInstance(); + if (bundleMgrHelper == nullptr) { + TAG_LOGE(AAFwkTag::APPKIT, "bundleMgrHelper nullptr"); + return false; + } + + AppExecFwk::BundleInfo bundleInfo; + ErrCode ret = bundleMgrHelper->GetBundleInfoForSelf(SCRIPT_CONTEXT_BUNDLE_INFO_FLAGS, bundleInfo); + if (ret != ERR_OK) { + TAG_LOGE(AAFwkTag::APPKIT, "getBundleInfo failed ret=%{public}d", ret); + return false; + } + return InitScriptContextFromBundleInfo(scriptContext, bundleInfo); +} + +bool IsArgName(const std::string& name) +{ + if (name.size() <= ARG_NAME_PREFIX_LENGTH || + name.compare(0, ARG_NAME_PREFIX_LENGTH, ARG_NAME_PREFIX) != 0) { + return false; + } + return std::all_of(name.begin() + ARG_NAME_PREFIX_LENGTH, name.end(), + [](unsigned char ch) { return std::isdigit(ch) != 0; }); +} + +bool ParseArgNameIndex(const std::string& name, uint32_t& index) +{ + if (!IsArgName(name)) { + return false; + } + const std::string indexText = name.substr(ARG_NAME_PREFIX_LENGTH); + auto ret = std::from_chars(indexText.data(), indexText.data() + indexText.size(), index); + return ret.ec == std::errc() && ret.ptr == indexText.data() + indexText.size(); +} + +bool ParseJsonValueToScriptArg(const nlohmann::json& value, ScriptArg& arg) +{ + if (value.is_string()) { + arg.type = ScriptArgType::STRING; + arg.value = value.get(); + return true; + } + if (value.is_boolean()) { + arg.type = ScriptArgType::BOOLEAN; + arg.value = value.get() ? "1" : "0"; + return true; + } + if (value.is_number_integer()) { + if (value.is_number_unsigned()) { + uint64_t number = value.get(); + if (number <= static_cast(std::numeric_limits::max())) { + arg.type = ScriptArgType::INT32; + } else { + arg.type = ScriptArgType::DOUBLE; + } + } else { + int64_t number = value.get(); + if (number >= std::numeric_limits::min() && number <= std::numeric_limits::max()) { + arg.type = ScriptArgType::INT32; + } else { + arg.type = ScriptArgType::DOUBLE; + } + } + arg.value = value.dump(); + return true; + } + if (value.is_number_float()) { + arg.type = ScriptArgType::DOUBLE; + arg.value = value.dump(); + return true; + } + if (value.is_object() || value.is_array()) { + arg.type = ScriptArgType::JSON_VALUE; + arg.value = value.dump(); + return true; + } + return false; +} + +bool ParseArgsJson(const std::string& rawValue, std::map& indexedArgs) +{ + std::string jsonText = rawValue; + // remove outer single quotes + if (jsonText.size() >= SINGLE_QUOTE_WRAP_LENGTH && jsonText.front() == '\'' && jsonText.back() == '\'') { + jsonText = jsonText.substr(1, jsonText.size() - SINGLE_QUOTE_WRAP_LENGTH); + } + auto argsJson = nlohmann::json::parse(jsonText, nullptr, false); + if (argsJson.is_discarded() || !argsJson.is_object()) { + TAG_LOGE(AAFwkTag::APPKIT, "parse args json failed, invalid json object, rawValue: %{public}s", + rawValue.c_str()); + return false; + } + + for (auto it = argsJson.begin(); it != argsJson.end(); ++it) { + uint32_t argIndex = 0; + if (!ParseArgNameIndex(it.key(), argIndex)) { + TAG_LOGE(AAFwkTag::APPKIT, "parse args json failed, invalid arg name: %{public}s", it.key().c_str()); + return false; + } + ScriptArg arg; + if (!ParseJsonValueToScriptArg(it.value(), arg)) { + TAG_LOGE(AAFwkTag::APPKIT, "parse args json failed, invalid arg value, key: %{public}s", + it.key().c_str()); + return false; + } + if (!indexedArgs.emplace(argIndex, std::move(arg)).second) { + TAG_LOGE(AAFwkTag::APPKIT, "parse args json failed, duplicate arg index: %{public}u", argIndex); + return false; + } + } + return true; +} + +bool ReadOptionValue(int argc, char* argv[], int& index, std::string& value) +{ + if (index + 1 >= argc || argv[index + 1] == nullptr) { + return false; + } + value = argv[++index]; + return true; +} + +bool ReadAndAssignStringOption(int argc, char* argv[], int& index, std::string& target, const char* optionName) +{ + std::string value; + if (!ReadOptionValue(argc, argv, index, value)) { + TAG_LOGE(AAFwkTag::APPKIT, "Missing value for %{public}s", optionName); + return false; + } + target = value; + return true; +} + +bool HandleStringOption(const std::string& option, int argc, char* argv[], int& index, ScriptArgs& args) +{ + for (const auto& binding : STRING_OPTION_BINDINGS) { + if (option == binding.optionName) { + return ReadAndAssignStringOption(argc, argv, index, args.*(binding.field), binding.optionName); + } + } + return false; +} + +bool HandleArgsOption(int argc, char* argv[], int& index, std::map& indexedArgs, bool& hasArgsJson) +{ + std::string value; + if (hasArgsJson || !ReadOptionValue(argc, argv, index, value)) { + TAG_LOGE(AAFwkTag::APPKIT, "Invalid --args option"); + return false; + } + if (!ParseArgsJson(value, indexedArgs)) { + TAG_LOGE(AAFwkTag::APPKIT, "Invalid value for --args"); + return false; + } + hasArgsJson = true; + return true; +} +} // namespace + +bool ArktsScript::ParseHelpOption(int argc, char* argv[], ScriptArgs& args) +{ + for (int i = 1; i < argc; i++) { + if (argv[i] == nullptr) { + continue; + } + if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) { + PrintUsage(); + args.showHelp = true; + return true; + } + } + return false; +} + +bool ArktsScript::ParseScriptOption(int argc, char* argv[], int& index, ScriptArgs& args, + std::map& indexedArgs, bool& hasArgsJson) +{ + std::string option = argv[index]; + if (HandleStringOption(option, argc, argv, index, args)) { + return true; + } + if (option == ARGS_OPTION) { + return HandleArgsOption(argc, argv, index, indexedArgs, hasArgsJson); + } + if (option == ABC_PATH_OPTION || option == SCRIPT_PATH_OPTION || option == FUNCTION_NAME_OPTION) { + // Matched a known string option but failed while reading its value. + // HandleStringOption already emitted the exact error message. + return false; + } + TAG_LOGE(AAFwkTag::APPKIT, "Unknown option: %{public}s", option.c_str()); + return false; +} + +bool ArktsScript::FinalizeParsedArguments(const std::map& indexedArgs, + ScriptArgs& args) +{ + if (args.abcPath.empty()) { + TAG_LOGE(AAFwkTag::APPKIT, "Missing --abcPath option"); + return false; + } + if (args.funName.empty()) { + TAG_LOGE(AAFwkTag::APPKIT, "Missing --functionName option"); + return false; + } + + args.arguments.clear(); + if (!indexedArgs.empty()) { + const uint32_t maxArgIndex = indexedArgs.rbegin()->first; + args.arguments.resize(static_cast(maxArgIndex) + 1); + for (const auto& item : indexedArgs) { + args.arguments[item.first] = item.second; + } + } + TAG_LOGD(AAFwkTag::APPKIT, + "parsed arguments, abcPath: %{public}s, scriptName: %{public}s, functionName: %{public}s, argc: %{public}zu", + args.abcPath.c_str(), args.scriptName.c_str(), args.funName.c_str(), args.arguments.size()); + return true; +} + +bool ArktsScript::ParseFunctionAndArguments(int argc, char* argv[], int& index, ScriptArgs& args) +{ + std::map indexedArgs; + bool hasArgsJson = false; + while (index < argc) { + if (argv[index] != nullptr && + !ParseScriptOption(argc, argv, index, args, indexedArgs, hasArgsJson)) { + return false; + } + index++; + } + + return FinalizeParsedArguments(indexedArgs, args); +} + +void ArktsScript::PrintUsage() +{ + std::cout << HELP_MSG << std::endl; + fflush(stdout); +} + +bool ArktsScript::ParseArguments(int argc, char* argv[], ScriptArgs& args) +{ + if (argc <= 0 || argv == nullptr || argv[0] == nullptr) { + return false; + } + if (ParseHelpOption(argc, argv, args)) { + return true; + } + + int i = 1; + if (argc <= i) { + TAG_LOGE(AAFwkTag::APPKIT, "Missing arguments"); + return false; + } + + return ParseFunctionAndArguments(argc, argv, i, args); +} + +std::shared_ptr ArktsScript::CreateScriptContext() +{ + auto scriptContext = std::make_shared(); + if (scriptContext == nullptr) { + TAG_LOGE(AAFwkTag::APPKIT, "failed to allocate ContextImpl"); + return nullptr; + } + + if (!InitScriptContextFromCaller(scriptContext)) { + TAG_LOGE(AAFwkTag::APPKIT, "failed to initialize script context from caller"); + return nullptr; + } + + return scriptContext; +} + +std::unique_ptr ArktsScript::CreateJsRuntime(const std::shared_ptr& context) +{ + AbilityRuntime::Runtime::Options options; + options.lang = AbilityRuntime::Runtime::Language::JS; + options.isBundle = true; + options.isStageModel = true; + if (context != nullptr) { + options.bundleName = context->GetBundleName(); + options.codePath = context->GetBundleCodePath(); + options.bundleCodeDir = context->GetBundleCodeDir(); + } + + auto runtime = JsRuntime::Create(options); + if (runtime == nullptr) { + TAG_LOGE(AAFwkTag::APPKIT, "Failed to create JsRuntime"); + return nullptr; + } + + TAG_LOGD(AAFwkTag::APPKIT, "create JsRuntime result: %{public}d", static_cast(runtime != nullptr)); + return runtime; +} + +bool ArktsScript::LoadAbcFile(JsRuntime* runtime, const std::string& path) +{ + if (runtime == nullptr) { + TAG_LOGE(AAFwkTag::APPKIT, "runtime is null when loading abc"); + return false; + } + + bool loaded = runtime->RunScript(path, "", false); + TAG_LOGI(AAFwkTag::APPKIT, "RunScript isLoaded: %{public}d, runScriptPath: %{public}s, targetPath: %{public}s", + static_cast(loaded), path.c_str(), path.c_str()); + return loaded; +} + +void ArktsScript::OutputResult(const std::string& result) +{ + std::cout << result << std::endl; + fflush(stdout); +} + +void ArktsScript::OutputError(const ScriptError& error) +{ + nlohmann::json errorOutput; + errorOutput["success"] = false; + errorOutput["errorType"] = error.type.empty() ? "EXECUTION_ERROR" : error.type; + errorOutput["error"] = error.message; + std::cerr << errorOutput.dump() << std::endl; + fflush(stderr); +} + +void ArktsScript::ExitFromMonitor(const CompletionChannel& channel, int exitCode) +{ + CompletionChannel localChannel = channel; + CloseCompletionChannel(localChannel); + _exit(exitCode); +} + +bool ArktsScript::CreateCompletionChannel(CompletionChannel& channel) +{ + channel.eventFd = eventfd(0, EFD_CLOEXEC); + if (channel.eventFd < 0) { + TAG_LOGE(AAFwkTag::APPKIT, "failed to create eventfd, errno=%{public}d", errno); + return false; + } + + channel.epollFd = epoll_create1(EPOLL_CLOEXEC); + if (channel.epollFd < 0) { + TAG_LOGE(AAFwkTag::APPKIT, "failed to create epoll fd, errno=%{public}d", errno); + close(channel.eventFd); + channel.eventFd = -1; + return false; + } + + epoll_event event {}; + event.events = EPOLLIN; + event.data.fd = channel.eventFd; + if (epoll_ctl(channel.epollFd, EPOLL_CTL_ADD, channel.eventFd, &event) != 0) { + TAG_LOGE(AAFwkTag::APPKIT, "failed to register eventfd into epoll, errno=%{public}d", errno); + close(channel.epollFd); + close(channel.eventFd); + channel.epollFd = -1; + channel.eventFd = -1; + return false; + } + + return true; +} + +void ArktsScript::CloseCompletionChannel(CompletionChannel& channel) +{ + if (channel.epollFd >= 0) { + close(channel.epollFd); + channel.epollFd = -1; + } + if (channel.eventFd >= 0) { + close(channel.eventFd); + channel.eventFd = -1; + } +} + +bool ArktsScript::SignalCompletion(int eventFd) +{ + if (eventFd < 0) { + return false; + } + + ssize_t ret = -1; + do { + ret = write(eventFd, &SIGNAL_VALUE, sizeof(SIGNAL_VALUE)); + } while (ret < 0 && errno == EINTR); + if (ret != static_cast(sizeof(SIGNAL_VALUE))) { + TAG_LOGE(AAFwkTag::APPKIT, "failed to signal eventfd, errno=%{public}d", errno); + return false; + } + return true; +} + +bool ArktsScript::PublishResult(const std::shared_ptr& execContext, int eventFd, + const std::string& result) +{ + if (execContext == nullptr) { + return false; + } + + { + std::lock_guard lock(execContext->mutex); + if (execContext->resultReady.load(std::memory_order_acquire)) { + TAG_LOGW(AAFwkTag::APPKIT, "duplicate result ignored"); + return false; + } + execContext->result = result; + execContext->error = {}; + execContext->resultReady.store(true, std::memory_order_release); + } + + if (!SignalCompletion(eventFd)) { + TAG_LOGE(AAFwkTag::APPKIT, "result recorded but signal failed"); + } + return true; +} + +bool ArktsScript::PublishFailure(const std::shared_ptr& execContext, int eventFd, + const ScriptError& error) +{ + if (execContext == nullptr) { + return false; + } + + { + std::lock_guard lock(execContext->mutex); + if (!execContext->error.message.empty()) { + TAG_LOGW(AAFwkTag::APPKIT, "duplicate failure ignored"); + return false; + } + execContext->result.clear(); + execContext->error = error; + execContext->scriptDone.store(true, std::memory_order_release); + } + + if (!SignalCompletion(eventFd)) { + TAG_LOGE(AAFwkTag::APPKIT, "failure recorded but signal failed"); + } + return true; +} + +bool ArktsScript::MarkScriptDone(const std::shared_ptr& execContext, int eventFd) +{ + if (execContext == nullptr) { + return false; + } + + execContext->scriptDone.store(true, std::memory_order_release); + if (!SignalCompletion(eventFd)) { + TAG_LOGE(AAFwkTag::APPKIT, "scriptDone recorded but signal failed"); + } + return true; +} + +void ArktsScript::ReadCompletionSignal(const CompletionChannel& channel) +{ + uint64_t count = 0; + ssize_t readSize = -1; + do { + readSize = read(channel.eventFd, &count, sizeof(count)); + } while (readSize < 0 && errno == EINTR); + if (readSize < 0 && errno != EAGAIN) { + TAG_LOGE(AAFwkTag::APPKIT, "failed to read eventfd, errno=%{public}d", errno); + ArktsScript::OutputError({"failed to read completion signal", "SYSTEM_ERROR"}); + ExitFromMonitor(channel, EXIT_FAILURE); + } +} + +ExecutionSnapshot ArktsScript::TakeExecutionSnapshot(const std::shared_ptr& execContext) +{ + ExecutionSnapshot snapshot; + if (execContext == nullptr) { + return snapshot; + } + + std::lock_guard lock(execContext->mutex); + snapshot.result = execContext->result; + snapshot.error = execContext->error; + snapshot.resultReady = execContext->resultReady.load(std::memory_order_acquire); + snapshot.scriptDone = execContext->scriptDone.load(std::memory_order_acquire); + return snapshot; +} + +void ArktsScript::HandleExecutionSnapshot(const ExecutionSnapshot& snapshot, const CompletionChannel& channel) +{ + if (!snapshot.error.message.empty()) { + ArktsScript::OutputError(snapshot.error); + ExitFromMonitor(channel, EXIT_FAILURE); + } + + if (snapshot.resultReady && snapshot.scriptDone) { + ArktsScript::OutputResult(snapshot.result); + ExitFromMonitor(channel, EXIT_SUCCESS); + } +} + +void ArktsScript::MonitorCompletion(ExitState state, CompletionChannel channel) +{ + epoll_event events[MAX_EVENTS] {}; + + while (channel.epollFd >= 0 && channel.eventFd >= 0) { + int readyCount = epoll_wait(channel.epollFd, events, MAX_EVENTS, -1); + if (readyCount < 0) { + if (errno == EINTR) { + continue; + } + TAG_LOGE(AAFwkTag::APPKIT, "epoll_wait failed, errno=%{public}d", errno); + ArktsScript::OutputError({"failed to wait completion signal", "SYSTEM_ERROR"}); + ExitFromMonitor(channel, EXIT_FAILURE); + } + + if (readyCount == 0 || events[0].data.fd != channel.eventFd) { + continue; + } + + ReadCompletionSignal(channel); + HandleExecutionSnapshot(TakeExecutionSnapshot(state.execContext), channel); + } +} + +bool ArktsScript::StartMonitor(CompletionChannel& channel, std::shared_ptr& execContext, + std::thread& monitorThread) +{ + if (!CreateCompletionChannel(channel)) { + ArktsScript::OutputError({"failed to create completion channel", "SYSTEM_ERROR"}); + return false; + } + + execContext = std::make_shared(); + ExitState exitState { execContext }; + monitorThread = std::thread(MonitorCompletion, exitState, channel); + TAG_LOGD(AAFwkTag::APPKIT, "completion monitor started"); + return true; +} + +int ArktsScript::FinalizeAndJoin(const std::shared_ptr& execContext, CompletionChannel& channel, + std::thread& monitorThread, const ScriptError& error) +{ + if (execContext != nullptr) { + PublishFailure(execContext, channel.eventFd, error); + } + if (monitorThread.joinable()) { + monitorThread.join(); + } + return EXIT_FAILURE; +} + +ResultCallback ArktsScript::CreateCompleteCallback(const std::shared_ptr& execContext, + CompletionChannel channel) +{ + return [execContext, channel](bool success, const std::string& result, const ScriptError& error) { + if (success) { + PublishResult(execContext, channel.eventFd, result); + return; + } + PublishFailure(execContext, channel.eventFd, error); + }; +} + +bool ArktsScript::PrepareRuntimeEnvironment(const ResultCallback& completeCallback, + std::shared_ptr& context, + std::unique_ptr& runtimeOwner, ScriptError& error) +{ + context = ArktsScript::CreateScriptContext(); + if (context == nullptr) { + error.message = "failed to create script context"; + error.type = "CONTEXT_ERROR"; + return false; + } + + runtimeOwner = ArktsScript::CreateJsRuntime(context); + if (runtimeOwner == nullptr) { + error.message = "failed to create js runtime"; + error.type = "RUNTIME_ERROR"; + return false; + } + + napi_env env = runtimeOwner->GetNapiEnv(); + if (env == nullptr) { + error.message = "failed to get napi_env"; + error.type = "ENV_ERROR"; + return false; + } + if (!JsArktsScript::BindContextToGlobal(env, context)) { + error.message = "failed to bind context to globalThis"; + error.type = "BIND_ERROR"; + return false; + } + if (!JsArktsScript::BindCompletearktsScript(env, completeCallback)) { + error.message = "failed to bind CompletearktsScript"; + error.type = "BIND_ERROR"; + return false; + } + TAG_LOGD(AAFwkTag::APPKIT, "runtime environment prepared"); + return true; +} + +bool ArktsScript::LoadScriptFile(const ScriptArgs& args, JsRuntime* runtime, ScriptError& error) +{ + if (!ArktsScript::LoadAbcFile(runtime, args.abcPath)) { + error.message = "failed to load ABC file, runScriptPath: " + args.abcPath + + ", targetPath: " + args.abcPath; + error.type = "LOAD_ERROR"; + return false; + } + return true; +} + +bool ArktsScript::ResolveScriptFunction(const ScriptArgs& args, JsRuntime* runtime, + napi_value& receiver, napi_value& func, ScriptError& error) +{ + napi_env env = runtime != nullptr ? runtime->GetNapiEnv() : nullptr; + bool isResolved = JsArktsScript::ResolveFunction(runtime, env, args.abcPath, args.scriptName, args.funName, + receiver, func); + TAG_LOGI(AAFwkTag::APPKIT, "ResolveFunction isResolved: %{public}d, functionName: %{public}s", + static_cast(isResolved), args.funName.c_str()); + if (!isResolved) { + error.message = "failed to resolve function: " + args.funName + + ", loaded abcPath: " + args.abcPath + + ", targetPath: " + args.abcPath + + ", scriptName: " + args.scriptName; + error.type = "FUNCTION_ERROR"; + return false; + } + return true; +} + +bool ArktsScript::CallResolvedFunction(const ScriptArgs& args, napi_env env, napi_value receiver, napi_value func, + ScriptError& error) +{ + napi_value argsArray = JsArktsScript::ConvertArgumentsToNapi(env, args.arguments); + if (argsArray == nullptr) { + TAG_LOGE(AAFwkTag::APPKIT, "call resolved function failed, failed to convert arguments"); + error.message = "failed to convert arguments"; + error.type = "ARGUMENT_ERROR"; + return false; + } + + napi_value callResult = nullptr; + napi_status status = napi_call_function(env, receiver, func, CALL_ARGC, &argsArray, &callResult); + if (status != napi_ok) { + TAG_LOGE(AAFwkTag::APPKIT, "CallResolvedFunction status: %{public}d, functionName: %{public}s", + static_cast(status), + args.funName.c_str()); + error.message = "failed to call function: " + args.funName + ", napi_status: " + std::to_string(status); + error.type = "CALL_ERROR"; + return false; + } + TAG_LOGI(AAFwkTag::APPKIT, "CallResolvedFunction status: %{public}d, functionName: %{public}s", + static_cast(status), args.funName.c_str()); + return true; +} + +int ArktsScript::RunArkTsScript(int argc, char* argv[]) +{ + ScriptArgs args; + if (!ArktsScript::ParseArguments(argc, argv, args)) { + ArktsScript::OutputError({"failed to parse arguments", "ARGUMENT_ERROR"}); + return EXIT_FAILURE; + } + if (args.showHelp) { + return EXIT_SUCCESS; + } + + CompletionChannel channel; + std::shared_ptr execContext; + std::thread monitorThread; + if (!StartMonitor(channel, execContext, monitorThread)) { + return EXIT_FAILURE; + } + + ScriptError error; + std::shared_ptr context; + std::unique_ptr runtimeOwner; + ResultCallback completeCallback = CreateCompleteCallback(execContext, channel); + if (!PrepareRuntimeEnvironment(completeCallback, context, runtimeOwner, error)) { + return FinalizeAndJoin(execContext, channel, monitorThread, error); + } + + napi_env env = runtimeOwner->GetNapiEnv(); + napi_value receiver = nullptr; + napi_value func = nullptr; + if (!LoadScriptFile(args, runtimeOwner.get(), error) || + !ResolveScriptFunction(args, runtimeOwner.get(), receiver, func, error) || + !CallResolvedFunction(args, env, receiver, func, error)) { + return FinalizeAndJoin(execContext, channel, monitorThread, error); + } + + MarkScriptDone(execContext, channel.eventFd); + if (monitorThread.joinable()) { + monitorThread.join(); + } + return EXIT_FAILURE; +} + +} // namespace ArktsScript +} // namespace OHOS diff --git a/tools/ohos-arktsScript/src/js_arkts_script.cpp b/tools/ohos-arktsScript/src/js_arkts_script.cpp new file mode 100644 index 0000000000..4967fd2d63 --- /dev/null +++ b/tools/ohos-arktsScript/src/js_arkts_script.cpp @@ -0,0 +1,921 @@ +/* + * 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_arkts_script.h" + +#include +#include +#include +#include +#include +#include + +#include "application_context.h" +#include "ecmascript/napi/include/jsnapi.h" +#include "hilog_tag_wrapper.h" +#include "js_runtime.h" +#include "js_runtime_utils.h" +#include "nlohmann/json.hpp" +#include "native_engine/impl/ark/ark_native_engine.h" + +namespace OHOS { +namespace ArktsScript { + +using OHOS::AbilityRuntime::JsRuntime; +using OHOS::AbilityRuntime::CreateJsNull; +using OHOS::AbilityRuntime::CreateJsValue; + +namespace { + +constexpr char SCRIPT_EXTENSION[] = ".ets"; +constexpr size_t SCRIPT_EXTENSION_LENGTH = sizeof(SCRIPT_EXTENSION) - 1; +constexpr size_t CREATE_BUNDLE_CONTEXT_ARGC = 1; + +struct CompleteCallbackData { + ResultCallback callback; +}; + +static CompleteCallbackData* g_completeCallbackData = nullptr; + +std::string NormalizeScriptNameValue(const std::string& scriptName) +{ + if (scriptName.size() > SCRIPT_EXTENSION_LENGTH && + scriptName.substr(scriptName.size() - SCRIPT_EXTENSION_LENGTH) == SCRIPT_EXTENSION) { + return scriptName.substr(0, scriptName.size() - SCRIPT_EXTENSION_LENGTH); + } + return scriptName; +} + +bool IsFunction(napi_env env, napi_value value) +{ + if (env == nullptr || value == nullptr) { + return false; + } + + napi_valuetype valueType = napi_undefined; + return napi_typeof(env, value, &valueType) == napi_ok && valueType == napi_function; +} + +bool GetNamedProperty(napi_env env, napi_value object, const std::string& name, napi_value& property) +{ + property = nullptr; + if (env == nullptr || object == nullptr || name.empty()) { + return false; + } + + napi_status status = napi_get_named_property(env, object, name.c_str(), &property); + return status == napi_ok && property != nullptr; +} + +std::vector BuildModulePathCandidates(const std::string& abcPath, const std::string& scriptName) +{ + std::vector candidates; + auto addCandidate = [&candidates](const std::string& candidate) { + if (!candidate.empty() && + std::find(candidates.begin(), candidates.end(), candidate) == candidates.end()) { + candidates.emplace_back(candidate); + } + }; + + addCandidate(abcPath); + if (!scriptName.empty()) { + std::string normalizedScriptName = NormalizeScriptNameValue(scriptName); + addCandidate(normalizedScriptName); + addCandidate(scriptName); + } + return candidates; +} + +bool GetExportObject(JsRuntime* runtime, napi_env env, const std::string& modulePath, + const std::string& exportName, napi_value& exportValue) +{ + exportValue = nullptr; + if (runtime == nullptr || env == nullptr || modulePath.empty() || exportName.empty()) { + return false; + } + + auto vm = runtime->GetEcmaVm(); + if (vm == nullptr) { + TAG_LOGE(AAFwkTag::APPKIT, "null ecma vm when resolving export"); + return false; + } + + panda::Local exportObj = panda::JSNApi::GetExportObject(vm, modulePath, exportName); + if (exportObj->IsNull()) { + TAG_LOGD(AAFwkTag::APPKIT, "export object not found, modulePath: %{public}s, exportName: %{public}s", + modulePath.c_str(), exportName.c_str()); + return false; + } + + exportValue = ArkNativeEngine::ArkValueToNapiValue(env, exportObj); + return exportValue != nullptr; +} + +bool ResolveNamedExportFunction(JsRuntime* runtime, napi_env env, napi_value global, const std::string& modulePath, + const std::string& funName, napi_value& receiver, napi_value& func) +{ + TAG_LOGD(AAFwkTag::APPKIT, "try named export, target modulePath: %{public}s, exportName: %{public}s", + modulePath.c_str(), funName.c_str()); + napi_value namedExport = nullptr; + if (!GetExportObject(runtime, env, modulePath, funName, namedExport) || !IsFunction(env, namedExport)) { + return false; + } + + func = namedExport; + receiver = global; + TAG_LOGD(AAFwkTag::APPKIT, "resolved named export %{public}s from %{public}s", + funName.c_str(), modulePath.c_str()); + return true; +} + +bool ResolveDefaultExportInstanceMethod(napi_env env, napi_value defaultExport, const std::string& funName, + const std::string& modulePath, napi_value& receiver, napi_value& func) +{ + if (!IsFunction(env, defaultExport)) { + return false; + } + + napi_value instance = nullptr; + constexpr size_t defaultExportConstructorArgc = 0; + auto status = napi_new_instance(env, defaultExport, defaultExportConstructorArgc, nullptr, &instance); + if (status != napi_ok || instance == nullptr) { + return false; + } + + napi_value method = nullptr; + if (!GetNamedProperty(env, instance, funName, method) || !IsFunction(env, method)) { + return false; + } + + receiver = instance; + func = method; + TAG_LOGD(AAFwkTag::APPKIT, "resolved default export instance method %{public}s from %{public}s", + funName.c_str(), modulePath.c_str()); + return true; +} + +bool ResolveDefaultExportPrototype(napi_env env, napi_value defaultExport, const std::string& modulePath, + napi_value& targetReceiver) +{ + targetReceiver = defaultExport; + if (!IsFunction(env, defaultExport)) { + return true; + } + if (GetNamedProperty(env, defaultExport, "prototype", targetReceiver) && targetReceiver != nullptr) { + return true; + } + + TAG_LOGW(AAFwkTag::APPKIT, "failed to get default export prototype from %{public}s", modulePath.c_str()); + return false; +} + +bool ResolveDefaultExportFunction(JsRuntime* runtime, napi_env env, const std::string& modulePath, + const std::string& funName, napi_value& receiver, napi_value& func) +{ + TAG_LOGD(AAFwkTag::APPKIT, "try default export, target modulePath: %{public}s, exportName: default", + modulePath.c_str()); + napi_value defaultExport = nullptr; + if (!GetExportObject(runtime, env, modulePath, "default", defaultExport)) { + return false; + } + + if (ResolveDefaultExportInstanceMethod(env, defaultExport, funName, modulePath, receiver, func)) { + return true; + } + + napi_value targetReceiver = nullptr; + if (!ResolveDefaultExportPrototype(env, defaultExport, modulePath, targetReceiver)) { + return false; + } + + napi_value method = nullptr; + if (!GetNamedProperty(env, targetReceiver, funName, method) || !IsFunction(env, method)) { + return false; + } + + receiver = targetReceiver; + func = method; + TAG_LOGD(AAFwkTag::APPKIT, "resolved default export method %{public}s from %{public}s", + funName.c_str(), modulePath.c_str()); + return true; +} + +napi_value ParseJsonStringToNapi(napi_env env, const std::string& jsonString) +{ + if (env == nullptr) { + return nullptr; + } + + napi_value global = nullptr; + napi_status status = napi_get_global(env, &global); + if (status != napi_ok || global == nullptr) { + return nullptr; + } + + napi_value jsonObj = nullptr; + status = napi_get_named_property(env, global, "JSON", &jsonObj); + if (status != napi_ok || jsonObj == nullptr) { + return nullptr; + } + + napi_value parseFunc = nullptr; + status = napi_get_named_property(env, jsonObj, "parse", &parseFunc); + if (status != napi_ok || parseFunc == nullptr) { + return nullptr; + } + + napi_value jsonArg = nullptr; + status = napi_create_string_utf8(env, jsonString.c_str(), jsonString.size(), &jsonArg); + if (status != napi_ok || jsonArg == nullptr) { + return nullptr; + } + + constexpr size_t parseArgc = 1; + napi_value argv[parseArgc] = { jsonArg }; + napi_value result = nullptr; + status = napi_call_function(env, jsonObj, parseFunc, parseArgc, argv, &result); + if (status != napi_ok) { + return nullptr; + } + return result; +} + +napi_value CreateScriptArgValue(napi_env env, const ScriptArg& arg) +{ + if (env == nullptr) { + return nullptr; + } + + switch (arg.type) { + case ScriptArgType::UNDEFINED: { + napi_value value = nullptr; + napi_status status = napi_get_undefined(env, &value); + return status == napi_ok ? value : nullptr; + } + case ScriptArgType::STRING: { + napi_value value = nullptr; + napi_status status = napi_create_string_utf8(env, arg.value.c_str(), arg.value.size(), &value); + return status == napi_ok ? value : nullptr; + } + case ScriptArgType::BOOLEAN: { + napi_value value = nullptr; + napi_status status = napi_get_boolean(env, arg.value == "1", &value); + return status == napi_ok ? value : nullptr; + } + case ScriptArgType::INT32: { + int32_t number = 0; + auto ret = std::from_chars(arg.value.data(), arg.value.data() + arg.value.size(), number); + if (ret.ec != std::errc() || ret.ptr != arg.value.data() + arg.value.size()) { + return nullptr; + } + napi_value value = nullptr; + napi_status status = napi_create_int32(env, number, &value); + return status == napi_ok ? value : nullptr; + } + case ScriptArgType::DOUBLE: { + char* end = nullptr; + errno = 0; + double number = std::strtod(arg.value.c_str(), &end); + if (errno == ERANGE || end == nullptr || *end != '\0') { + return nullptr; + } + napi_value value = nullptr; + napi_status status = napi_create_double(env, number, &value); + return status == napi_ok ? value : nullptr; + } + case ScriptArgType::JSON_VALUE: + return ParseJsonStringToNapi(env, arg.value); + default: + return nullptr; + } +} + +} // namespace + +std::string JsArktsScript::NormalizeScriptName(const std::string& scriptName) +{ + return NormalizeScriptNameValue(scriptName); +} + +bool JsArktsScript::SetNamedStringOrNull(napi_env env, napi_value object, const char* name, + const std::string& value) +{ + AbilityRuntime::HandleScope handleScope(env); + if (value.empty()) { + napi_value nullValue = CreateJsNull(env); + return nullValue != nullptr && napi_set_named_property(env, object, name, nullValue) == napi_ok; + } + napi_value jsValue = CreateJsValue(env, value); + return jsValue != nullptr && napi_set_named_property(env, object, name, jsValue) == napi_ok; +} + +bool JsArktsScript::SetNamedNull(napi_env env, napi_value object, const char* name) +{ + AbilityRuntime::HandleScope handleScope(env); + napi_value nullValue = CreateJsNull(env); + return nullValue != nullptr && napi_set_named_property(env, object, name, nullValue) == napi_ok; +} + +napi_value JsArktsScript::CreateApplicationInfoObject(napi_env env, + const std::shared_ptr& appInfo) +{ + AbilityRuntime::HandleEscape escapeScope(env); + napi_value object = nullptr; + if (napi_create_object(env, &object) != napi_ok || object == nullptr) { + return nullptr; + } + + if (appInfo == nullptr) { + SetNamedNull(env, object, "name"); + SetNamedNull(env, object, "bundleName"); + SetNamedNull(env, object, "process"); + return escapeScope.Escape(object); + } + + SetNamedStringOrNull(env, object, "name", appInfo->name); + SetNamedStringOrNull(env, object, "bundleName", appInfo->bundleName); + SetNamedStringOrNull(env, object, "process", appInfo->process); + + return escapeScope.Escape(object); +} + +void JsArktsScript::SetApplicationInfoProperty(napi_env env, napi_value object, + const std::shared_ptr& context) +{ + std::shared_ptr appInfo = nullptr; + if (context != nullptr) { + appInfo = context->GetApplicationInfo(); + } + napi_value appInfoObject = CreateApplicationInfoObject(env, appInfo); + if (appInfoObject != nullptr) { + napi_set_named_property(env, object, "applicationInfo", appInfoObject); + } else { + SetNamedNull(env, object, "applicationInfo"); + } +} + +void JsArktsScript::SetNullContextProperties(napi_env env, napi_value object) +{ + const char* properties[] = { + "cacheDir", "tempDir", "filesDir", "databaseDir", "preferencesDir", "bundleCodeDir", + "distributedFilesDir", "resourceDir", "cloudFileDir", "logFileDir" + }; + for (const auto* property : properties) { + SetNamedNull(env, object, property); + } +} + +void JsArktsScript::SetContextDirectoryProperties(napi_env env, napi_value object, + const std::shared_ptr& context) +{ + if (context == nullptr) { + SetNullContextProperties(env, object); + return; + } + + SetNamedStringOrNull(env, object, "cacheDir", context->GetCacheDir()); + SetNamedStringOrNull(env, object, "tempDir", context->GetTempDir()); + SetNamedStringOrNull(env, object, "filesDir", context->GetFilesDir()); + SetNamedStringOrNull(env, object, "databaseDir", context->GetDatabaseDir()); + SetNamedStringOrNull(env, object, "preferencesDir", context->GetPreferencesDir()); + SetNamedStringOrNull(env, object, "bundleCodeDir", context->GetBundleCodeDir()); + SetNamedStringOrNull(env, object, "distributedFilesDir", context->GetDistributedFilesDir()); + SetNamedStringOrNull(env, object, "cloudFileDir", context->GetCloudFileDir()); + SetNamedStringOrNull(env, object, "logFileDir", context->GetLogFileDir()); +} + +napi_value JsArktsScript::CreateApplicationContextCallback(napi_env callbackEnv, napi_callback_info) +{ + auto appContext = OHOS::AbilityRuntime::Context::GetApplicationContext(); + return CreateScriptContextObject(callbackEnv, appContext); +} + +napi_value JsArktsScript::CreateBundleContextCallback(napi_env callbackEnv, napi_callback_info info) +{ + size_t argc = CREATE_BUNDLE_CONTEXT_ARGC; + napi_value argv[CREATE_BUNDLE_CONTEXT_ARGC] = {nullptr}; + napi_get_cb_info(callbackEnv, info, &argc, argv, nullptr, nullptr); + + std::string bundleName; + if (argc > 0) { + GetUtf8String(callbackEnv, argv[0], bundleName); + } + + auto appContext = OHOS::AbilityRuntime::Context::GetApplicationContext(); + if (appContext == nullptr) { + return CreateScriptContextObject(callbackEnv, appContext, bundleName); + } + + const std::string targetBundleName = bundleName.empty() ? appContext->GetBundleName() : bundleName; + auto bundleContext = appContext->CreateBundleContext(targetBundleName); + if (bundleContext != nullptr) { + return CreateScriptContextObject(callbackEnv, bundleContext, targetBundleName); + } + return CreateScriptContextObject(callbackEnv, appContext, targetBundleName); +} + +void JsArktsScript::BindContextFunction(napi_env env, napi_value object, const char* name, napi_callback callback) +{ + napi_value func = nullptr; + if (napi_create_function(env, name, NAPI_AUTO_LENGTH, callback, nullptr, &func) == napi_ok && func != nullptr) { + napi_set_named_property(env, object, name, func); + } +} + +napi_value JsArktsScript::CreateScriptContextObject(napi_env env, + const std::shared_ptr& context, + const std::string& bundleNameOverride) +{ + AbilityRuntime::HandleEscape escapeScope(env); + napi_value object = nullptr; + if (napi_create_object(env, &object) != napi_ok || object == nullptr) { + return nullptr; + } + + const std::string bundleName = !bundleNameOverride.empty() ? bundleNameOverride : + (context != nullptr ? context->GetBundleName() : ""); + const std::string processName = context != nullptr ? context->GetProcessName() : ""; + SetApplicationInfoProperty(env, object, context); + SetContextDirectoryProperties(env, object, context); + SetNamedStringOrNull(env, object, "bundleName", bundleName); + SetNamedStringOrNull(env, object, "processName", processName); + napi_set_named_property(env, object, "area", CreateJsNull(env)); + BindContextFunction(env, object, "getApplicationContext", CreateApplicationContextCallback); + BindContextFunction(env, object, "createBundleContext", CreateBundleContextCallback); + return escapeScope.Escape(object); +} + +bool JsArktsScript::GetUtf8String(napi_env env, napi_value value, std::string& output) +{ + if (env == nullptr || value == nullptr) { + return false; + } + + size_t length = 0; + napi_status status = napi_get_value_string_utf8(env, value, nullptr, 0, &length); + if (status != napi_ok) { + TAG_LOGE(AAFwkTag::APPKIT, "napi_get_value_string_utf8(length) failed"); + return false; + } + + std::string buffer(length + 1, '\0'); + status = napi_get_value_string_utf8(env, value, buffer.data(), buffer.size(), &length); + if (status != napi_ok) { + TAG_LOGE(AAFwkTag::APPKIT, "napi_get_value_string_utf8(data) failed"); + return false; + } + + buffer.resize(length); + output = buffer; + return true; +} + +bool JsArktsScript::GetInt32Property(napi_env env, napi_value object, const char* name, int32_t& output) +{ + if (env == nullptr || object == nullptr || name == nullptr) { + return false; + } + + AbilityRuntime::HandleScope handleScope(env); + napi_value property = nullptr; + napi_status status = napi_get_named_property(env, object, name, &property); + if (status != napi_ok || property == nullptr) { + return false; + } + + napi_valuetype valueType = napi_undefined; + status = napi_typeof(env, property, &valueType); + if (status != napi_ok || valueType != napi_number) { + return false; + } + + status = napi_get_value_int32(env, property, &output); + return status == napi_ok; +} + +bool JsArktsScript::GetOptionalInt32Property(napi_env env, napi_value object, + const char* name, int32_t& output, bool& hasValue) +{ + hasValue = false; + if (env == nullptr || object == nullptr || name == nullptr) { + return false; + } + + AbilityRuntime::HandleScope handleScope(env); + napi_value property = nullptr; + napi_status status = napi_get_named_property(env, object, name, &property); + if (status != napi_ok || property == nullptr) { + return true; + } + + napi_valuetype valueType = napi_undefined; + status = napi_typeof(env, property, &valueType); + if (status != napi_ok || valueType == napi_undefined || valueType == napi_null) { + return true; + } + if (valueType != napi_number) { + return false; + } + + status = napi_get_value_int32(env, property, &output); + if (status != napi_ok) { + return false; + } + hasValue = true; + return true; +} + +bool JsArktsScript::GetOptionalStringProperty(napi_env env, napi_value object, + const char* name, std::string& output, bool& hasValue) +{ + hasValue = false; + if (env == nullptr || object == nullptr || name == nullptr) { + return false; + } + + AbilityRuntime::HandleScope handleScope(env); + napi_value property = nullptr; + napi_status status = napi_get_named_property(env, object, name, &property); + if (status != napi_ok || property == nullptr) { + return true; + } + + napi_valuetype valueType = napi_undefined; + status = napi_typeof(env, property, &valueType); + if (status != napi_ok || valueType == napi_undefined || valueType == napi_null) { + return true; + } + if (valueType != napi_string || !GetUtf8String(env, property, output)) { + return false; + } + hasValue = true; + return true; +} + +bool JsArktsScript::GetOptionalJsonObjectProperty(napi_env env, napi_value object, + const char* name, nlohmann::json& output, bool& hasValue) +{ + hasValue = false; + if (env == nullptr || object == nullptr || name == nullptr) { + return false; + } + + AbilityRuntime::HandleScope handleScope(env); + napi_value property = nullptr; + napi_status status = napi_get_named_property(env, object, name, &property); + if (status != napi_ok || property == nullptr) { + return true; + } + + napi_valuetype valueType = napi_undefined; + status = napi_typeof(env, property, &valueType); + if (status != napi_ok || valueType == napi_undefined || valueType == napi_null) { + return true; + } + + std::string rawValue = JsArktsScript::StringifyObject(env, property); + if (rawValue.empty()) { + return true; + } + + try { + output = nlohmann::json::parse(rawValue); + hasValue = true; + return true; + } catch (const std::exception&) { + output = rawValue; + hasValue = true; + return true; + } +} + +void JsArktsScript::ClearCompleteCallbackData() +{ + delete g_completeCallbackData; + g_completeCallbackData = nullptr; +} + +void JsArktsScript::FinishCompleteCallback(bool success, const std::string& result, const ScriptError& error) +{ + if (g_completeCallbackData != nullptr && g_completeCallbackData->callback) { + g_completeCallbackData->callback(success, result, error); + } + ClearCompleteCallbackData(); +} + +napi_value JsArktsScript::CompletearktsScriptWithError(const std::string& error) +{ + TAG_LOGE(AAFwkTag::APPKIT, "%{public}s", error.c_str()); + FinishCompleteCallback(false, "", {error, "ARGUMENT_ERROR"}); + return nullptr; +} + +bool JsArktsScript::ReadCompletePayload(napi_env env, napi_value value, CompletePayload& payload, std::string& error) +{ + if (!GetInt32Property(env, value, "code", payload.code)) { + error = "CompletearktsScript requires a numeric code"; + return false; + } + if (!GetOptionalJsonObjectProperty(env, value, "result", payload.resultJson, payload.hasResult)) { + error = "CompletearktsScript failed to read result"; + return false; + } + if (!GetOptionalStringProperty(env, value, "uri", payload.uri, payload.hasUri)) { + error = "CompletearktsScript failed to read uri"; + return false; + } + if (!GetOptionalInt32Property(env, value, "flag", payload.flag, payload.hasFlag)) { + error = "CompletearktsScript failed to read flag"; + return false; + } + return true; +} + +nlohmann::json JsArktsScript::BuildCompletePayloadJson(const CompletePayload& payload) +{ + nlohmann::json result; + result["code"] = payload.code; + if (payload.hasResult) { + result["result"] = payload.resultJson; + } + if (payload.hasUri) { + result["uri"] = payload.uri; + } + if (payload.hasFlag) { + result["flag"] = payload.flag; + } + return result; +} + +napi_value JsArktsScript::CompletearktsScript(napi_env env, napi_callback_info info) +{ + AbilityRuntime::HandleScope handleScope(env); + + constexpr size_t RESULT_ARGC = 1; + constexpr size_t RESULT_INDEX = 0; + size_t argc = RESULT_ARGC; + napi_value argv[RESULT_ARGC] = {nullptr}; + napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr); + + if (argc < RESULT_ARGC) { + return CompletearktsScriptWithError("CompletearktsScript requires a result argument"); + } + + napi_valuetype valueType = napi_undefined; + if (napi_typeof(env, argv[RESULT_INDEX], &valueType) != napi_ok || valueType != napi_object) { + return CompletearktsScriptWithError("CompletearktsScript requires an object argument"); + } + + CompletePayload payload; + std::string error; + if (!ReadCompletePayload(env, argv[RESULT_INDEX], payload, error)) { + return CompletearktsScriptWithError(error); + } + + std::string resultStr = BuildCompletePayloadJson(payload).dump(); + TAG_LOGI(AAFwkTag::APPKIT, + "CompletearktsScript resultCode: %{public}d, hasResult: %{public}d, hasUri: %{public}d, hasFlag: %{public}d", + payload.code, static_cast(payload.hasResult), static_cast(payload.hasUri), + static_cast(payload.hasFlag)); + + FinishCompleteCallback(true, resultStr, {}); + + return nullptr; +} + +bool JsArktsScript::BindContextToGlobal(napi_env env, const std::shared_ptr& context) +{ + if (env == nullptr) { + TAG_LOGE(AAFwkTag::APPKIT, "invalid env when binding context"); + return false; + } + + AbilityRuntime::HandleScope handleScope(env); + napi_value globalObj = nullptr; + napi_status status = napi_get_global(env, &globalObj); + if (status != napi_ok || globalObj == nullptr) { + TAG_LOGE(AAFwkTag::APPKIT, "napi_get_global failed"); + return false; + } + + napi_value scriptContext = CreateScriptContextObject(env, context); + if (scriptContext == nullptr) { + TAG_LOGE(AAFwkTag::APPKIT, "failed to create scriptContext"); + return false; + } + napi_set_named_property(env, globalObj, "scriptContext", scriptContext); + napi_set_named_property(env, globalObj, "context", scriptContext); + return true; +} + +bool JsArktsScript::BindCompletearktsScript(napi_env env, const ResultCallback& callback) +{ + if (env == nullptr) { + return false; + } + + AbilityRuntime::HandleScope handleScope(env); + napi_value global = nullptr; + napi_status status = napi_get_global(env, &global); + if (status != napi_ok || global == nullptr) { + return false; + } + + if (g_completeCallbackData != nullptr) { + ClearCompleteCallbackData(); + } + g_completeCallbackData = new (std::nothrow) CompleteCallbackData{callback}; + if (g_completeCallbackData == nullptr) { + return false; + } + + napi_value func = nullptr; + status = napi_create_function(env, "CompletearktsScript", NAPI_AUTO_LENGTH, + CompletearktsScript, nullptr, &func); + if (status != napi_ok || func == nullptr) { + ClearCompleteCallbackData(); + return false; + } + + napi_set_named_property(env, global, "CompletearktsScript", func); + return true; +} + +bool JsArktsScript::ResolveFunctionFromExports(JsRuntime* runtime, napi_env env, const std::string& abcPath, + const std::string& scriptName, const std::string& funName, napi_value& receiver, napi_value& func) +{ + if (runtime == nullptr || env == nullptr) { + return false; + } + + napi_value global = nullptr; + if (napi_get_global(env, &global) != napi_ok || global == nullptr) { + return false; + } + + const auto modulePathCandidates = BuildModulePathCandidates(abcPath, scriptName); + func = nullptr; + receiver = global; + + TAG_LOGD(AAFwkTag::APPKIT, + "resolve export target, loaded abcPath: %{public}s, scriptName: %{public}s, funcName: %{public}s", + abcPath.c_str(), scriptName.c_str(), funName.c_str()); + + for (const auto& modulePath : modulePathCandidates) { + if (ResolveNamedExportFunction(runtime, env, global, modulePath, funName, receiver, func)) { + return true; + } + } + + for (const auto& modulePath : modulePathCandidates) { + if (ResolveDefaultExportFunction(runtime, env, modulePath, funName, receiver, func)) { + return true; + } + } + + TAG_LOGD(AAFwkTag::APPKIT, + "resolve export target failed, loaded abcPath: %{public}s, scriptName: %{public}s, funcName: %{public}s", + abcPath.c_str(), scriptName.c_str(), funName.c_str()); + return false; +} + +bool JsArktsScript::ResolveFunctionFromGlobal(napi_env env, const std::string& scriptName, + const std::string& funName, napi_value& receiver, napi_value& func) +{ + if (env == nullptr) { + return false; + } + napi_value global = nullptr; + napi_status status = napi_get_global(env, &global); + if (status != napi_ok || global == nullptr) { + return false; + } + + receiver = global; + func = nullptr; + if (scriptName.empty()) { + status = napi_get_named_property(env, global, funName.c_str(), &func); + if (status == napi_ok && IsFunction(env, func)) { + TAG_LOGD(AAFwkTag::APPKIT, "resolved global function %{public}s", funName.c_str()); + return true; + } + return false; + } + + const std::string normalizedScriptName = NormalizeScriptName(scriptName); + napi_value scriptCtor = nullptr; + status = napi_get_named_property(env, global, normalizedScriptName.c_str(), &scriptCtor); + if (status != napi_ok || !IsFunction(env, scriptCtor)) { + return false; + } + + status = napi_new_instance(env, scriptCtor, 0, nullptr, &receiver); + if (status != napi_ok || receiver == nullptr) { + return false; + } + + status = napi_get_named_property(env, receiver, funName.c_str(), &func); + if (status == napi_ok && IsFunction(env, func)) { + TAG_LOGD(AAFwkTag::APPKIT, "resolved global class method %{public}s from %{public}s", + funName.c_str(), normalizedScriptName.c_str()); + return true; + } + return false; +} + +bool JsArktsScript::ResolveFunction(JsRuntime* runtime, napi_env env, const std::string& abcPath, + const std::string& scriptName, const std::string& funName, napi_value& receiver, napi_value& func) +{ + if (ResolveFunctionFromExports(runtime, env, abcPath, scriptName, funName, receiver, func)) { + return true; + } + return ResolveFunctionFromGlobal(env, scriptName, funName, receiver, func); +} + +napi_value JsArktsScript::ConvertArgumentsToNapi(napi_env env, const std::vector& arguments) +{ + if (env == nullptr) { + return nullptr; + } + + AbilityRuntime::HandleEscape escapeScope(env); + napi_value argsArray = nullptr; + napi_status status = napi_create_array_with_length(env, arguments.size(), &argsArray); + if (status != napi_ok || argsArray == nullptr) { + return nullptr; + } + + for (size_t i = 0; i < arguments.size(); i++) { + napi_value argValue = CreateScriptArgValue(env, arguments[i]); + if (argValue == nullptr) { + TAG_LOGE(AAFwkTag::APPKIT, "failed to convert argument %{public}zu", i); + return nullptr; + } + status = napi_set_element(env, argsArray, i, argValue); + if (status != napi_ok) { + return nullptr; + } + } + + return escapeScope.Escape(argsArray); +} + +std::string JsArktsScript::StringifyObject(napi_env env, napi_value result) +{ + TAG_LOGD(AAFwkTag::APPKIT, "stringify object"); + AbilityRuntime::HandleScope handleScope(env); + + napi_value global; + auto status = napi_get_global(env, &global); + if (status != napi_ok) { + TAG_LOGE(AAFwkTag::APPKIT, "get global failed %{public}d", status); + return ""; + } + + napi_value jsonObj; + status = napi_get_named_property(env, global, "JSON", &jsonObj); + if (status != napi_ok) { + TAG_LOGE(AAFwkTag::APPKIT, "get JSON object failed %{public}d", status); + return ""; + } + + napi_value stringifyFunc; + status = napi_get_named_property(env, jsonObj, "stringify", &stringifyFunc); + if (status != napi_ok) { + TAG_LOGE(AAFwkTag::APPKIT, "get stringify object failed %{public}d", status); + return ""; + } + + napi_value stringifyResult; + constexpr size_t STRINGIFY_ARGC = 1; + napi_value argv[STRINGIFY_ARGC] = { result }; + status = napi_call_function(env, jsonObj, stringifyFunc, STRINGIFY_ARGC, argv, &stringifyResult); + if (status != napi_ok) { + TAG_LOGE(AAFwkTag::APPKIT, "call JSON.stringify failed %{public}d", status); + return ""; + } + + std::string str; + if (!AbilityRuntime::ConvertFromJsValue(env, stringifyResult, str)) { + TAG_LOGW(AAFwkTag::APPKIT, "convert napi value failed"); + return ""; + } + + TAG_LOGD(AAFwkTag::APPKIT, "stringify object %{private}s", str.c_str()); + return str; +} + +} // namespace ArktsScript +} // namespace OHOS diff --git a/tools/ohos-arktsScript/src/main.cpp b/tools/ohos-arktsScript/src/main.cpp new file mode 100644 index 0000000000..9f3264f784 --- /dev/null +++ b/tools/ohos-arktsScript/src/main.cpp @@ -0,0 +1,32 @@ +/* + * 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 "arkts_script.h" + +#include "hilog_tag_wrapper.h" +#include "ipc_skeleton.h" + +#include + +int main(int argc, char* argv[]) +{ + TAG_LOGD(AAFwkTag::APPKIT, + "ohos-arkts process identity: processUid=%{public}d, processPid=%{public}d, callingUid=%{public}d, " + "callingPid=%{public}d, tokenId=%{public}u, fullTokenId=%{public}llu", + getuid(), getpid(), OHOS::IPCSkeleton::GetCallingUid(), OHOS::IPCSkeleton::GetCallingPid(), + OHOS::IPCSkeleton::GetCallingTokenID(), + static_cast(OHOS::IPCSkeleton::GetCallingFullTokenID())); + return OHOS::ArktsScript::ArktsScript::RunArkTsScript(argc, argv); +} diff --git a/tools/ohos-example/config.json b/tools/ohos-example/config.json index 0c411094fe..121d72da86 100644 --- a/tools/ohos-example/config.json +++ b/tools/ohos-example/config.json @@ -3,12 +3,20 @@ "version": "1.0.0", "description": "Example CLI tool, demonstrates CLI tool specification implementation (with subcommand format)", "executablePath": "/system/bin/cli_tool/executable/ohos-example", - "hasSubcommands": true, - "timeout": 30, + "hasSubCommand": true, "requirePermissions": [], + "inputSchema": { + "type": "object", + "properties": {} + }, + "outputSchema": { + "type": "object", + "properties": {} + }, "subcommands": { "run": { "description": "Run example tool, execute and return results", + "requirePermissions": [], "inputSchema": { "type": "object", "properties": { @@ -28,23 +36,15 @@ } } }, - "eventTypes": ["progress", "result"], + "eventTypes": ["progress"], "eventSchemas": { - "result": { - "type": "object", - "description": "Execution result event", - "properties": { - "result": { - "type": "string", - "description": "Execution result string" - } - }, - "required": ["result"] - }, "progress": { "type": "object", "description": "Execution progress event", "properties": { + "type": { + "const": "progress" + }, "percentage": { "type": "integer", "minimum": 0, @@ -56,24 +56,16 @@ "description": "Current status" } }, - "required": ["percentage", "status"] + "required": ["type", "percentage", "status"] } - }, - "argMapping": { - "type": "positional", - "order": ["argLine"] } }, "version": { "description": "Display version information", + "requirePermissions": [], "inputSchema": { "type": "object", - "properties": { - "reserved": { - "type": "string", - "description": "Reserved placeholder parameter for future use, not required for normal calls" - } - } + "properties": {} }, "outputSchema": { "type": "object", @@ -89,12 +81,6 @@ } }, "required": ["version"] - }, - "argMapping": { - "type": "flag", - "templates": { - "reserved": "--reserved={value}" - } } } } diff --git a/tools/ohos-example/src/main.cpp b/tools/ohos-example/src/main.cpp index ea472c848e..09861f6475 100644 --- a/tools/ohos-example/src/main.cpp +++ b/tools/ohos-example/src/main.cpp @@ -14,104 +14,167 @@ #include #include #include -#include -#include -#include -// Constants for magic numbers namespace { - constexpr int PROGRESS_MAX = 100; - constexpr int MIN_ARGC = 2; +constexpr int PROGRESS_MAX = 100; +constexpr int MIN_ARGC = 2; +constexpr const char* VERSION = "1.0.0"; +constexpr const char* BUILD_TIME = "2026-04-04 00:00:00"; +} + +std::string EscapeJson(const std::string& input) +{ + std::string escaped; + escaped.reserve(input.size()); + for (char ch : input) { + switch (ch) { + case '\\': + escaped += "\\\\"; + break; + case '"': + escaped += "\\\""; + break; + case '\n': + escaped += "\\n"; + break; + case '\r': + escaped += "\\r"; + break; + case '\t': + escaped += "\\t"; + break; + default: + escaped += ch; + break; + } + } + return escaped; } void EmitProgress(int percentage, const std::string& status) { - std::cout << "{\"event\": \"progress\", \"data\": {" - << "\"percentage\": " << percentage << ", " - << "\"status\": \"" << status << "\"" - << "}}" << std::endl; + std::cout << "{\"type\":\"progress\",\"percentage\":" << percentage + << ",\"status\":\"" << EscapeJson(status) << "\"}" << std::endl; } -void EmitResult(const std::string& result) +void EmitSuccessResult(const std::string& dataJson) { - std::cout << "{\"event\": \"result\", \"data\": {" - << "\"result\": \"" << result << "\"" - << "}}" << std::endl; + std::cout << "{\"type\":\"result\",\"status\":\"success\",\"data\":" + << dataJson << "}" << std::endl; } -void EmitResultWithFields(const std::string& version, const std::string& buildTime) +void EmitError(const std::string& errCode, const std::string& errMsg, const std::string& suggestion) { - std::cout << "{\"event\": \"result\", \"data\": {" - << "\"version\": \"" << version << "\", " - << "\"build_time\": \"" << buildTime << "\"" - << "}}" << std::endl; -} - -int RunCommand(const std::vector& args) -{ - EmitProgress(0, "starting"); - - std::string result = "执行完成"; - for (size_t i = 0; i < args.size(); i++) { - if (i > 0 || !result.empty()) { - result += " "; - } - result += args[i]; - - int progress = static_cast((i + 1) * PROGRESS_MAX / (args.size() + 1)); - EmitProgress(progress, "running"); - } - - EmitProgress(PROGRESS_MAX, "completed"); - EmitResult(result); - - return 0; -} - -int VersionCommand() -{ - std::string version = "1.0.0"; - - const char* buildTime = "2026-04-04 00:00:00"; - - EmitResultWithFields(version, buildTime); - - return 0; + std::cout << "{\"type\":\"result\",\"status\":\"failed\",\"errCode\":\"" + << EscapeJson(errCode) << "\",\"errMsg\":\"" << EscapeJson(errMsg) + << "\",\"suggestion\":\"" << EscapeJson(suggestion) << "\"}" << std::endl; } void ShowHelp() { - std::cout << "Usage: ohos-example [args]" << std::endl; + std::cout << "Usage: ohos-example [options]" << std::endl; std::cout << "Subcommands:" << std::endl; - std::cout << " run [args...] Run the tool with arguments" << std::endl; - std::cout << " version Show version information" << std::endl; - std::cout << " help Show this help message" << std::endl; + std::cout << " run --argLine Run the tool with a single string argument" << std::endl; + std::cout << " version Show version information" << std::endl; + std::cout << " help Show this help message" << std::endl; +} + +void ShowRunHelp() +{ + std::cout << "Usage: ohos-example run [options]" << std::endl; + std::cout << "Options:" << std::endl; + std::cout << " --argLine Argument string to pass to the tool" << std::endl; + std::cout << " --help, -h Show this help message" << std::endl; +} + +void ShowVersionHelp() +{ + std::cout << "Usage: ohos-example version [options]" << std::endl; + std::cout << "Options:" << std::endl; + std::cout << " --help, -h Show this help message" << std::endl; +} + +int RunCommand(int argc, char* argv[]) +{ + std::string argLine; + int i = 2; + while (i < argc) { + std::string arg = argv[i]; + if (arg == "--help" || arg == "-h") { + ShowRunHelp(); + return 0; + } + if (arg == "--argLine") { + if (i + 1 >= argc) { + EmitError("ERR_MISSING_PARAM", "Missing value for parameter 'argLine'.", + "Use: ohos-example run --argLine "); + return 1; + } + ++i; + argLine = argv[i]; + ++i; + continue; + } + + EmitError("ERR_UNKNOWN_PARAM", "Unknown parameter '" + arg + "' for subcommand 'run'.", + "Use: ohos-example run --argLine "); + return 1; + } + + if (argLine.empty()) { + EmitError("ERR_MISSING_PARAM", "Missing required parameter 'argLine'.", + "Use: ohos-example run --argLine "); + return 1; + } + + EmitProgress(0, "starting"); + EmitProgress(50, "running"); + EmitProgress(PROGRESS_MAX, "completed"); + EmitSuccessResult("{\"result\":\"执行完成 " + EscapeJson(argLine) + "\"}"); + return 0; +} + +int VersionCommand(int argc, char* argv[]) +{ + if (argc == 3) { + std::string arg = argv[2]; + if (arg == "--help" || arg == "-h") { + ShowVersionHelp(); + return 0; + } + } + if (argc != 2) { + EmitError("ERR_UNKNOWN_PARAM", "Subcommand 'version' does not accept extra parameters.", + "Use: ohos-example version"); + return 1; + } + + EmitSuccessResult("{\"version\":\"" + std::string(VERSION) + "\",\"build_time\":\"" + + std::string(BUILD_TIME) + "\"}"); + return 0; } int main(int argc, char* argv[]) { if (argc < MIN_ARGC) { - ShowHelp(); + EmitError("ERR_MISSING_PARAM", "Missing required subcommand.", + "Use one of: ohos-example run --argLine , ohos-example version"); return 1; } std::string subcommand = argv[1]; - if (subcommand == "run") { - std::vector args; - for (int i = 2; i < argc; ++i) { - args.push_back(argv[i]); - } - return RunCommand(args); - } else if (subcommand == "version") { - return VersionCommand(); - } else if (subcommand == "help" || subcommand == "--help" || subcommand == "-h") { + return RunCommand(argc, argv); + } + if (subcommand == "version") { + return VersionCommand(argc, argv); + } + if (subcommand == "help" || subcommand == "--help" || subcommand == "-h") { ShowHelp(); return 0; - } else { - ShowHelp(); - return 1; } - return 0; + EmitError("ERR_INVALID_PARAM", "Unknown subcommand '" + subcommand + "'.", + "Use one of: run, version, help"); + return 1; } diff --git a/tools/ohos-simple/config.json b/tools/ohos-simple/config.json index 56771f8225..4f294acc97 100644 --- a/tools/ohos-simple/config.json +++ b/tools/ohos-simple/config.json @@ -3,30 +3,7 @@ "version": "1.0.0", "description": "Simple CLI tool, supports message output, repeat count and verbose mode", "executablePath": "/system/bin/cli_tool/executable/ohos-simple", - "timeout": 30, "requirePermissions": [], - "eventTypes": ["result"], - "eventSchemas": { - "result": { - "type": "object", - "description": "Execution result event", - "properties": { - "status": { - "type": "string", - "description": "Execution status" - }, - "message": { - "type": "string", - "description": "Processed output message" - }, - "repeat_count": { - "type": "integer", - "description": "Actual repeat count" - } - }, - "required": ["status", "message", "repeat_count"] - } - }, "inputSchema": { "type": "object", "description": "Tool input parameters", @@ -55,10 +32,6 @@ "type": "object", "description": "Tool execution result", "properties": { - "status": { - "type": "string", - "description": "Execution status, success when completed successfully" - }, "message": { "type": "string", "description": "Processed output message" @@ -68,17 +41,6 @@ "description": "Actual repeat count" } }, - "required": ["status", "message", "repeat_count"] - }, - "argMapping": { - "type": "flag", - "templates": { - "message": "--message={value}", - "count": "--count={value}", - "verbose": { - "if_true": "--verbose", - "if_false": "" - } - } + "required": ["message", "repeat_count"] } } diff --git a/tools/ohos-simple/src/main.cpp b/tools/ohos-simple/src/main.cpp index 12bac58ef4..d8e5c2acfe 100644 --- a/tools/ohos-simple/src/main.cpp +++ b/tools/ohos-simple/src/main.cpp @@ -11,63 +11,136 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include +#include +#include #include #include -#include -// Constants for magic numbers namespace { - constexpr int MESSAGE_PREFIX_LEN = 10; - constexpr int COUNT_PREFIX_LEN = 8; - constexpr int MAX_COUNT = 10; - constexpr int MIN_COUNT = 1; +constexpr int MAX_COUNT = 10; +constexpr int MIN_COUNT = 1; } -void EmitResult(const std::string& status, const std::string& message, int repeatCount) +struct SimpleConfig { + std::string message = "Hello from ohos-simple"; + int count = 1; + bool verbose = false; +}; + +std::string EscapeJson(const std::string& input) { - std::cout << "{\"event\": \"result\", \"data\": {" - << "\"status\": \"" << status << "\", " - << "\"message\": \"" << message << "\", " - << "\"repeat_count\": " << repeatCount - << "}}" << std::endl; + std::string escaped; + escaped.reserve(input.size()); + for (char ch : input) { + switch (ch) { + case '\\': + escaped += "\\\\"; + break; + case '"': + escaped += "\\\""; + break; + case '\n': + escaped += "\\n"; + break; + case '\r': + escaped += "\\r"; + break; + case '\t': + escaped += "\\t"; + break; + default: + escaped += ch; + break; + } + } + return escaped; +} + +void EmitSuccessResult(const std::string& dataJson) +{ + std::cout << "{\"type\":\"result\",\"status\":\"success\",\"data\":" + << dataJson << "}" << std::endl; +} + +void EmitError(const std::string& errCode, const std::string& errMsg, const std::string& suggestion) +{ + std::cout << "{\"type\":\"result\",\"status\":\"failed\",\"errCode\":\"" + << EscapeJson(errCode) << "\",\"errMsg\":\"" << EscapeJson(errMsg) + << "\",\"suggestion\":\"" << EscapeJson(suggestion) << "\"}" << std::endl; } void ShowHelp() { std::cout << "Usage: ohos-simple [options]" << std::endl; std::cout << "Options:" << std::endl; - std::cout << " --message= Set message to display (default: 'Hello from ohos-simple')" << std::endl; - std::cout << " --count= Number of repetitions (1-10, default: 1)" << std::endl; + std::cout << " --message Set message to display (default: 'Hello from ohos-simple')" << std::endl; + std::cout << " --count Number of repetitions (1-10, default: 1)" << std::endl; std::cout << " --verbose Enable verbose output" << std::endl; std::cout << " --help, -h Show this help message" << std::endl; } -bool ParseArguments(int argc, char* argv[], std::string& message, int& count, bool& verbose) +bool ParseInteger(const std::string& value, int& result) { - for (int i = 1; i < argc; ++i) { - std::string arg = argv[i]; - - if (arg.find("--message=") == 0) { - message = arg.substr(MESSAGE_PREFIX_LEN); - } else if (arg.find("--count=") == 0) { - count = std::atoi(arg.substr(COUNT_PREFIX_LEN).c_str()); - if (count < MIN_COUNT) { - count = MIN_COUNT; - } - if (count > MAX_COUNT) { - count = MAX_COUNT; - } - } else if (arg == "--verbose") { - verbose = true; - } else if (arg == "--help" || arg == "-h") { - ShowHelp(); - return false; - } + char* end = nullptr; + errno = 0; + long parsed = std::strtol(value.c_str(), &end, 10); + if (errno != 0 || end == value.c_str() || *end != '\0' || parsed < INT_MIN || parsed > INT_MAX) { + return false; } + result = static_cast(parsed); return true; } -std::string ExecuteTask(const std::string& message, int count, bool verbose) +int ParseArguments(int argc, char* argv[], SimpleConfig& config) +{ + int i = 1; + while (i < argc) { + std::string arg = argv[i]; + if (arg == "--message") { + if (i + 1 >= argc) { + EmitError("ERR_MISSING_PARAM", "Missing value for parameter 'message'.", + "Use: ohos-simple --message [--count ] [--verbose]"); + return 1; + } + ++i; + config.message = argv[i]; + ++i; + continue; + } + if (arg == "--count") { + if (i + 1 >= argc) { + EmitError("ERR_MISSING_PARAM", "Missing value for parameter 'count'.", + "Use: ohos-simple --count [--message ] [--verbose]"); + return 1; + } + ++i; + if (!ParseInteger(argv[i], config.count)) { + EmitError("ERR_INVALID_PARAM", "Parameter 'count' must be an integer.", + "Use an integer between 1 and 10, for example: --count 2"); + return 1; + } + ++i; + continue; + } + if (arg == "--verbose") { + config.verbose = true; + ++i; + continue; + } + if (arg == "--help" || arg == "-h") { + ShowHelp(); + return 2; + } + + EmitError("ERR_UNKNOWN_PARAM", "Unknown parameter '" + arg + "'.", + "Supported parameters are: --message, --count, --verbose"); + return 1; + } + return 0; +} + +std::string ExecuteTask(const std::string& message, int count) { std::string result; for (int i = 0; i < count; ++i) { @@ -76,27 +149,33 @@ std::string ExecuteTask(const std::string& message, int count, bool verbose) } result += message; } - return result; } int main(int argc, char* argv[]) { - std::string message = "Hello from ohos-simple"; - int count = 1; - bool verbose = false; - - if (!ParseArguments(argc, argv, message, count, verbose)) { - return 0; + SimpleConfig config; + int parseResult = ParseArguments(argc, argv, config); + if (parseResult != 0) { + return parseResult == 2 ? 0 : 1; } - if (message.empty()) { + if (config.message.empty()) { + EmitError("ERR_INVALID_PARAM", "Parameter 'message' must not be empty.", + "Provide a non-empty string, for example: --message hello"); + return 1; + } + if (config.count < MIN_COUNT || config.count > MAX_COUNT) { + EmitError("ERR_INVALID_PARAM", "Parameter 'count' must be between 1 and 10.", + "Use an integer between 1 and 10, for example: --count 2"); return 1; } - std::string result = ExecuteTask(message, count, verbose); - - EmitResult("success", result, count); - + std::string result = ExecuteTask(config.message, config.count); + if (config.verbose) { + result = "[verbose] " + result; + } + EmitSuccessResult("{\"message\":\"" + EscapeJson(result) + "\",\"repeat_count\":" + + std::to_string(config.count) + "}"); return 0; } diff --git a/tools/ohos-timer/config.json b/tools/ohos-timer/config.json index 5e813b8ac3..829c3f6898 100644 --- a/tools/ohos-timer/config.json +++ b/tools/ohos-timer/config.json @@ -3,7 +3,6 @@ "version": "1.0.0", "description": "Timer tool example, supports progress event output", "executablePath": "/system/bin/cli_tool/executable/ohos-timer", - "timeout": 30, "requirePermissions": [], "eventTypes": ["progress"], "inputSchema": { @@ -21,6 +20,10 @@ "default": 1, "minimum": 1 }, + "errMsg": { + "type": "string", + "description": "Error message written to stderr when duration elapses" + }, "showProgress": { "type": "boolean", "description": "Show progress events", @@ -38,10 +41,6 @@ "type": "object", "description": "Tool output result", "properties": { - "status": { - "type": "string", - "description": "Execution status" - }, "duration": { "type": "integer", "description": "Planned duration (seconds)" @@ -51,13 +50,16 @@ "description": "Actual duration (seconds)" } }, - "required": ["status", "duration", "actual_duration"] + "required": ["duration", "actual_duration"] }, "eventSchemas": { "progress": { "type": "object", "description": "Progress event", "properties": { + "type": { + "const": "progress" + }, "percentage": { "type": "integer", "minimum": 0, @@ -69,22 +71,7 @@ "description": "Current status" } }, - "required": ["percentage", "status"] - } - }, - "argMapping": { - "type": "flag", - "templates": { - "duration": "--duration={value}", - "interval": "--interval={value}", - "showProgress": { - "if_true": "--progress", - "if_false": "" - }, - "verbose": { - "if_true": "--verbose", - "if_false": "" - } + "required": ["type", "percentage", "status"] } } } diff --git a/tools/ohos-timer/src/main.cpp b/tools/ohos-timer/src/main.cpp index 8a421093d5..0eb37174cf 100644 --- a/tools/ohos-timer/src/main.cpp +++ b/tools/ohos-timer/src/main.cpp @@ -11,134 +11,172 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include +#include +#include +#include #include #include -#include -#include #include -#include -#include -#include -// Constants for magic numbers namespace { - constexpr int PROGRESS_PERCENTAGE_MAX = 100; - constexpr int DEFAULT_INTERVAL = 1; - constexpr int MIN_DURATION = 1; - constexpr int MIN_INTERVAL = 1; - constexpr int DURATION_PREFIX_LEN = 11; - constexpr int INTERVAL_PREFIX_LEN = 11; - constexpr int HELP_ARGC = 2; - constexpr int ARG_PARSE_START_INDEX = 1; +constexpr int PROGRESS_MAX = 100; +constexpr int DEFAULT_INTERVAL = 1; +constexpr int MIN_DURATION = 1; +constexpr int MIN_INTERVAL = 1; } struct TimerConfig { int duration = 0; int interval = DEFAULT_INTERVAL; + std::string errMsg; bool showProgress = false; bool verbose = false; }; -void EmitProgress(int percentage, const std::string& status) +std::string EscapeJson(const std::string& input) { - std::cout << "{\"event\": \"progress\", \"data\": {" - << "\"percentage\": " << percentage << ", " - << "\"status\": \"" << status << "\"" - << "}}" << std::endl; + std::string escaped; + escaped.reserve(input.size()); + for (char ch : input) { + switch (ch) { + case '\\': + escaped += "\\\\"; + break; + case '"': + escaped += "\\\""; + break; + case '\n': + escaped += "\\n"; + break; + case '\r': + escaped += "\\r"; + break; + case '\t': + escaped += "\\t"; + break; + default: + escaped += ch; + break; + } + } + return escaped; } -void EmitResult(const std::string& status, int duration, int actualDuration) +void EmitProgress(int percentage, const std::string& status) { - std::cout << "{\"event\": \"result\", \"data\": {" - << "\"status\": \"" << status << "\", " - << "\"duration\": " << duration << ", " - << "\"actual_duration\": " << actualDuration - << "}}" << std::endl; + std::cout << "{\"type\":\"progress\",\"percentage\":" << percentage + << ",\"status\":\"" << EscapeJson(status) << "\"}" << std::endl; +} + +void EmitSuccessResult(const std::string& dataJson) +{ + std::cout << "{\"type\":\"result\",\"status\":\"success\",\"data\":" + << dataJson << "}" << std::endl; +} + +void EmitError(const std::string& errCode, const std::string& errMsg, const std::string& suggestion) +{ + std::cout << "{\"type\":\"result\",\"status\":\"failed\",\"errCode\":\"" + << EscapeJson(errCode) << "\",\"errMsg\":\"" << EscapeJson(errMsg) + << "\",\"suggestion\":\"" << EscapeJson(suggestion) << "\"}" << std::endl; } void ShowHelp() { std::cout << "Usage: ohos-timer [options]" << std::endl; std::cout << "Options:" << std::endl; - std::cout << " --duration= Duration in seconds (required, minimum 1)" << std::endl; - std::cout << " --interval= Progress update interval in seconds (optional, default 1)" << std::endl; - std::cout << " --progress Enable progress events" << std::endl; - std::cout << " --verbose Enable verbose output" << std::endl; - std::cout << " --help, -h Show this help message" << std::endl; + std::cout << " --duration Duration in seconds (required, minimum 1)" << std::endl; + std::cout << " --interval Progress update interval in seconds (default 1)" << std::endl; + std::cout << " --errMsg Error message written to stderr when duration elapses" << std::endl; + std::cout << " --showProgress Enable progress events" << std::endl; + std::cout << " --verbose Enable verbose mode" << std::endl; + std::cout << " --help, -h Show this help message" << std::endl; } -bool ParseArguments(int argc, char* argv[], TimerConfig& config) +bool ParseInteger(const std::string& value, int& result) { - for (int i = ARG_PARSE_START_INDEX; i < argc; ++i) { + char* end = nullptr; + errno = 0; + long parsed = std::strtol(value.c_str(), &end, 10); + if (errno != 0 || end == value.c_str() || *end != '\0' || parsed < INT_MIN || parsed > INT_MAX) { + return false; + } + result = static_cast(parsed); + return true; +} + +int ParseArguments(int argc, char* argv[], TimerConfig& config) +{ + int i = 1; + while (i < argc) { std::string arg = argv[i]; - - if (arg.find("--duration=") == 0) { - std::string value = arg.substr(DURATION_PREFIX_LEN); - char* end = nullptr; - errno = 0; - long val = std::strtol(value.c_str(), &end, 10); - if (errno != 0 || end == value.c_str() || *end != '\0' || val < 0 || val > INT_MAX) { - return false; + if (arg == "--duration") { + if (i + 1 >= argc) { + EmitError("ERR_MISSING_PARAM", "Missing value for parameter 'duration'.", + "Use: ohos-timer --duration [--interval ] [--showProgress] [--verbose]"); + return 1; } - config.duration = static_cast(val); - } else if (arg.find("--interval=") == 0) { - std::string value = arg.substr(INTERVAL_PREFIX_LEN); - char* end = nullptr; - errno = 0; - long val = std::strtol(value.c_str(), &end, 10); - if (errno != 0 || end == value.c_str() || *end != '\0' || val < 0 || val > INT_MAX) { - return false; + ++i; + if (!ParseInteger(argv[i], config.duration)) { + EmitError("ERR_INVALID_PARAM", "Parameter 'duration' must be an integer.", + "Use a positive integer, for example: --duration 5"); + return 1; } - config.interval = static_cast(val); - } else if (arg == "--progress") { - config.showProgress = true; - } else if (arg == "--verbose") { - config.verbose = true; - } else if (arg == "--help" || arg == "-h") { - ShowHelp(); - return false; + ++i; + continue; } + if (arg == "--interval") { + if (i + 1 >= argc) { + EmitError("ERR_MISSING_PARAM", "Missing value for parameter 'interval'.", + "Use: ohos-timer --interval [--duration ] [--showProgress] [--verbose]"); + return 1; + } + ++i; + if (!ParseInteger(argv[i], config.interval)) { + EmitError("ERR_INVALID_PARAM", "Parameter 'interval' must be an integer.", + "Use a positive integer, for example: --interval 1"); + return 1; + } + ++i; + continue; + } + if (arg == "--errMsg") { + if (i + 1 >= argc) { + EmitError("ERR_MISSING_PARAM", "Missing value for parameter 'errMsg'.", + "Use: ohos-timer --duration [--errMsg ] [--showProgress] [--verbose]"); + return 1; + } + ++i; + config.errMsg = argv[i]; + ++i; + continue; + } + if (arg == "--showProgress") { + config.showProgress = true; + ++i; + continue; + } + if (arg == "--verbose") { + config.verbose = true; + ++i; + continue; + } + if (arg == "--help" || arg == "-h") { + ShowHelp(); + return 2; + } + + EmitError("ERR_UNKNOWN_PARAM", "Unknown parameter '" + arg + "'.", + "Supported parameters are: --duration, --interval, --errMsg, --showProgress, --verbose"); + return 1; } - return true; -} - -bool ValidateArguments(const TimerConfig& config) -{ - if (config.duration < MIN_DURATION) { - return false; - } - - if (config.interval < MIN_INTERVAL) { - return false; - } - - if (config.interval > config.duration) { - return false; - } - - return true; -} - -void UpdateProgress(int elapsed, int duration, int& lastPercentage) -{ - if (duration == 0) { - return; - } - - int percentage = static_cast( - (elapsed * PROGRESS_PERCENTAGE_MAX) / duration - ); - - if (percentage > lastPercentage && percentage > 0) { - EmitProgress(percentage, "running"); - lastPercentage = percentage; - } + return 0; } int ExecuteTimer(const TimerConfig& config) { auto startTime = std::chrono::steady_clock::now(); - int lastPercentage = -1; int elapsed = 0; if (config.showProgress) { @@ -146,46 +184,52 @@ int ExecuteTimer(const TimerConfig& config) } while (elapsed < config.duration) { - if (config.showProgress) { - UpdateProgress(elapsed, config.duration, lastPercentage); - } - std::this_thread::sleep_for(std::chrono::seconds(config.interval)); - - elapsed = static_cast( - std::chrono::duration_cast( - std::chrono::steady_clock::now() - startTime - ).count() - ); + elapsed = static_cast(std::chrono::duration_cast( + std::chrono::steady_clock::now() - startTime).count()); + if (config.showProgress && elapsed < config.duration) { + int percentage = elapsed * PROGRESS_MAX / config.duration; + EmitProgress(percentage, config.verbose ? "running (verbose)" : "running"); + } } if (config.showProgress) { - EmitProgress(PROGRESS_PERCENTAGE_MAX, "completed"); + EmitProgress(PROGRESS_MAX, "completed"); } - EmitResult("success", config.duration, elapsed); - + if (!config.errMsg.empty()) { + std::cerr << config.errMsg << std::endl; + } + EmitSuccessResult("{\"duration\":" + std::to_string(config.duration) + + ",\"actual_duration\":" + std::to_string(elapsed) + "}"); return 0; } int main(int argc, char* argv[]) { TimerConfig config; - - if (argc < HELP_ARGC) { - ShowHelp(); - return 1; - } - - if (!ParseArguments(argc, argv, config)) { - return 1; + int parseResult = ParseArguments(argc, argv, config); + if (parseResult != 0) { + return parseResult == 2 ? 0 : 1; } if (config.duration == 0) { - ShowHelp(); + EmitError("ERR_MISSING_PARAM", "Missing required parameter 'duration'.", + "Use: ohos-timer --duration [--interval ] [--showProgress] [--verbose]"); return 1; } - - if (!ValidateArguments(config)) { + if (config.duration < MIN_DURATION) { + EmitError("ERR_INVALID_PARAM", "Parameter 'duration' must be greater than or equal to 1.", + "Use a positive integer, for example: --duration 5"); + return 1; + } + if (config.interval < MIN_INTERVAL) { + EmitError("ERR_INVALID_PARAM", "Parameter 'interval' must be greater than or equal to 1.", + "Use a positive integer, for example: --interval 1"); + return 1; + } + if (config.interval > config.duration) { + EmitError("ERR_INVALID_PARAM", "Parameter 'interval' must not be greater than 'duration'.", + "Use values such as: --duration 5 --interval 1"); return 1; } diff --git a/tools/test/BUILD.gn b/tools/test/BUILD.gn index 473a290e81..677d1863f1 100644 --- a/tools/test/BUILD.gn +++ b/tools/test/BUILD.gn @@ -12,6 +12,7 @@ # limitations under the License. import("//build/test.gni") +import("//foundation/ability/ability_runtime/ability_runtime.gni") group("systemtest") { testonly = true @@ -32,4 +33,5 @@ group("unittest") { deps = [ "unittest/aa:unittest" ] deps += [ "unittest/ability_delegator:unittest" ] deps += [ "unittest/system_time:unittest" ] + deps += [ "${ability_runtime_path}/tools/ohos-aa/tests:unittest"] } diff --git a/tools/test/mock/mock_ability_manager_stub.h b/tools/test/mock/mock_ability_manager_stub.h index 70a762afad..8952c0a2c5 100644 --- a/tools/test/mock/mock_ability_manager_stub.h +++ b/tools/test/mock/mock_ability_manager_stub.h @@ -190,7 +190,7 @@ public: virtual int StartAbilityByCall(const Want& want, const sptr& connect, const sptr& callerToken, int32_t userId = DEFAULT_INVAL_VALUE, bool isSilent = false, - bool promotePriority = false, bool isVisible = false) + bool promotePriority = false, bool isVisible = false, uint64_t specifiedFullTokenId = 0) { return 0; } @@ -282,6 +282,14 @@ public: const InsightIntentExecuteParam ¶m)); MOCK_METHOD3(ExecuteInsightIntentDone, int32_t(const sptr &token, uint64_t intentId, const InsightIntentExecuteResult &result)); + MOCK_METHOD3(GetAllInsightIntentInfo, int32_t(AbilityRuntime::GetInsightIntentFlag flag, + std::vector &infos, int32_t userId)); + MOCK_METHOD4(GetInsightIntentInfoByBundleName, int32_t(AbilityRuntime::GetInsightIntentFlag flag, + const std::string &bundleName, std::vector &infos, + int32_t userId)); + MOCK_METHOD6(GetInsightIntentInfoByIntentName, int32_t(AbilityRuntime::GetInsightIntentFlag flag, + const std::string &bundleName, const std::string &moduleName, const std::string &intentName, + AbilityRuntime::InsightIntentInfoForQuery &info, int32_t userId)); MOCK_METHOD5(StartAbilityWithSpecifyTokenId, int(const Want& want, const sptr& callerToken, uint32_t specifyTokenId, int32_t userId, int requestCode)); public: diff --git a/utils/global/constant/global_constant.h b/utils/global/constant/global_constant.h index 0fbe70dc39..c00a276dee 100644 --- a/utils/global/constant/global_constant.h +++ b/utils/global/constant/global_constant.h @@ -35,6 +35,7 @@ constexpr int32_t GAME_SA_UID = 7800; constexpr int32_t COLDSTART_TIMEOUT_MULTIPLE = 15000; constexpr int32_t LOAD_TIMEOUT_MULTIPLE = 15000; constexpr int32_t FOREGROUND_TIMEOUT_MULTIPLE = 7500; +constexpr int32_t FOREGROUND_TIMEOUT_MULTIPLE_BETA = 7500; constexpr int32_t BACKGROUND_TIMEOUT_MULTIPLE = 4500; constexpr int32_t INSIGHT_INTENT_TIMEOUT_MULTIPLE = 15000; constexpr int32_t ACTIVE_TIMEOUT_MULTIPLE = 7500; @@ -43,11 +44,13 @@ constexpr int32_t INACTIVE_TIMEOUT_MULTIPLE = 800; constexpr int32_t INACTIVE_TIMEOUT_MULTIPLE_NEW = 800; constexpr int32_t DUMP_TIMEOUT_MULTIPLE = 1500; constexpr int32_t SHAREDATA_TIMEOUT_MULTIPLE = 7500; +constexpr int32_t SKILL_EXECUTE_TIMEOUT_MULTIPLE = 15000; constexpr int32_t CONCURRENT_START_TIMEOUT = 10; #else constexpr int32_t COLDSTART_TIMEOUT_MULTIPLE = 10; constexpr int32_t LOAD_TIMEOUT_MULTIPLE = 10; constexpr int32_t FOREGROUND_TIMEOUT_MULTIPLE = 5; +constexpr int32_t FOREGROUND_TIMEOUT_MULTIPLE_BETA = 10; constexpr int32_t BACKGROUND_TIMEOUT_MULTIPLE = 3; constexpr int32_t INSIGHT_INTENT_TIMEOUT_MULTIPLE = 10; constexpr int32_t ACTIVE_TIMEOUT_MULTIPLE = 5; @@ -56,6 +59,7 @@ constexpr int32_t INACTIVE_TIMEOUT_MULTIPLE = 1; constexpr int32_t INACTIVE_TIMEOUT_MULTIPLE_NEW = 21; constexpr int32_t DUMP_TIMEOUT_MULTIPLE = 1000; constexpr int32_t SHAREDATA_TIMEOUT_MULTIPLE = 5; +constexpr int32_t SKILL_EXECUTE_TIMEOUT_MULTIPLE = 10; constexpr int32_t CONCURRENT_START_TIMEOUT = 1; constexpr int32_t TYPE_RESERVE = 1; constexpr int32_t TYPE_OTHERS = 2; diff --git a/utils/server/startup/include/param.h b/utils/server/startup/include/param.h index eb1d3ccec7..67dbd565c7 100644 --- a/utils/server/startup/include/param.h +++ b/utils/server/startup/include/param.h @@ -50,6 +50,7 @@ struct LoadParam : public Parcelable { bool isMainElementRunning = false; bool isKeepAliveAppService = false; bool isCallerSetProcess = false; + bool isGamePrelaunch = false; std::string customProcessFlag = ""; uint32_t extensionProcessMode = 0; ExtensionLoadParam extensionLoadParam; diff --git a/utils/server/startup/src/startup_util.cpp b/utils/server/startup/src/startup_util.cpp index b357cf0952..ccb56b3901 100644 --- a/utils/server/startup/src/startup_util.cpp +++ b/utils/server/startup/src/startup_util.cpp @@ -59,7 +59,8 @@ bool StartupUtil::IsSupportAppClone(AppExecFwk::ExtensionAbilityType type) type == AppExecFwk::ExtensionAbilityType::REMOTE_NOTIFICATION || type == AppExecFwk::ExtensionAbilityType::VOIP || type == AppExecFwk::ExtensionAbilityType::FAULT_LOG || - type == AppExecFwk::ExtensionAbilityType::EMBEDDED_UI; + type == AppExecFwk::ExtensionAbilityType::EMBEDDED_UI || + type == AppExecFwk::ExtensionAbilityType::MODULAR_OBJECT; } void StartupUtil::InitAbilityInfoFromExtension(AppExecFwk::ExtensionAbilityInfo &extensionInfo,