Add serialization and parsing capabilities for wantParams

Signed-off-by: xhz-sz <xiehuandong@huawei.com>
This commit is contained in:
xhz-sz
2026-07-06 14:51:22 +08:00
parent 52e7d1e513
commit 83faeeb42e
3 changed files with 324 additions and 0 deletions
@@ -205,6 +205,58 @@ public:
*/
static std::shared_ptr<WantAgent> FromString(const std::string &jsonString, int32_t uid = -1);
/**
* Convert WantAgentInfo object to string with WantParams envelope for extraInfo.
* This function is the upgrade path for ToString when extraInfo must round
* trip safely. ToString stores extraInfo through WantParamWrapper::ToString,
* which uses manual string concatenation and inherits the legacy
* WantParamsWrapper escaping and parsing limitations. This function stores
* extraInfo through WantParamWrapperJson::Serialize so the WantParams payload
* is wrapped by the envelope format and JSON escaping is applied.
*
* The output keeps the same outer WantAgent string format as ToString, but
* extraInfo.extraInfoValue is generated by WantParamWrapperJson::Serialize:
* {"extraInfo":{"extraInfoValue":"{\"ohos.want.paramsStringEnvelope\":{...}}"}}
*
* @param agent WantAgent object.
* @return WantAgentInfo object's serialized string.
*/
static std::string ToStringWithEnvelope(const std::shared_ptr<WantAgent> &agent);
/**
* @brief Parses a WantAgent string generated by ToStringWithEnvelope.
*
* Convert string with WantParams envelope extraInfo to WantAgentInfo object.
* This function is the upgrade path for FromString when the input was
* generated by ToStringWithEnvelope. FromString parses extraInfo with
* WantParamWrapper::Parse and therefore has the same legacy limitations:
* unescaped quotes/backslashes/control characters, delimiter-like content,
* manually embedded typeId text, unsupported types, and some truncated or
* trailing inputs may fail or restore incorrectly.
*
* Before calling this function, callers must use HasWantParamsEnvelope
* to check that the input string is generated by ToStringWithEnvelope,
* unless the data source is already guaranteed to be generated by
* ToStringWithEnvelope.
*
* @param jsonString String to parse.
* @return WantAgentInfo object.
*/
static std::shared_ptr<WantAgent> FromStringWithEnvelope(
const std::string &jsonString, int32_t uid = -1);
/**
* Check whether the input string contains WantParams envelope in extraInfo.
*
* This function is used to distinguish data generated by
* ToStringWithEnvelope from legacy data generated by ToString.
*
* @param jsonString String to check.
* @return Returns true if the WantParamWrapperJson envelope is detected;
* returns false otherwise.
*/
static bool HasWantParamsEnvelope(const std::string &jsonString);
private:
WantAgentHelper();
virtual ~WantAgentHelper() = default;
@@ -21,6 +21,7 @@
#include "local_pending_want.h"
#include "multi_app_utils.h"
#include "want_params_wrapper.h"
#include "want_params_wrapper_json.h"
#include "pending_want.h"
#include "permission_verification.h"
#include "want_agent_client.h"
@@ -574,6 +575,129 @@ std::shared_ptr<WantAgent> WantAgentHelper::FromString(const std::string &jsonSt
return GetWantAgent(info, userId, uid);
}
std::string WantAgentHelper::ToStringWithEnvelope(const std::shared_ptr<WantAgent> &agent)
{
if (agent == nullptr) {
TAG_LOGE(AAFwkTag::WANTAGENT, "invalid param");
return "";
}
std::shared_ptr<PendingWant> pendingWant = agent->GetPendingWant();
if (pendingWant == nullptr) {
TAG_LOGE(AAFwkTag::WANTAGENT, "invalid param");
return "";
}
std::shared_ptr<WantSenderInfo> info = pendingWant->GetWantSenderInfo(pendingWant->GetTarget());
if (info == nullptr) {
TAG_LOGE(AAFwkTag::WANTAGENT, "invalid param");
return "";
}
nlohmann::json jsonObject;
jsonObject["requestCode"] = (*info.get()).requestCode;
jsonObject["operationType"] = (*info.get()).type;
jsonObject["flags"] = (*info.get()).flags;
jsonObject["userId"] = (*info.get()).userId;
jsonObject["appIndex"] = (*info.get()).appIndex;
nlohmann::json wants = nlohmann::json::array();
for (auto &wantInfo : (*info.get()).allWants) {
wants.emplace_back(wantInfo.want.ToString());
}
jsonObject["wants"] = wants;
if ((*info.get()).allWants.size() > 0) {
nlohmann::json paramsObj;
std::string paramsString;
if (!AAFwk::WantParamWrapperJson::Serialize((*info.get()).allWants[0].want.GetParams(), paramsString)) {
TAG_LOGE(AAFwkTag::WANTAGENT, "serialize want params json failed");
}
paramsObj["extraInfoValue"] = paramsString;
jsonObject["extraInfo"] = paramsObj;
}
return jsonObject.dump();
}
std::shared_ptr<WantAgent> WantAgentHelper::FromStringWithEnvelope(
const std::string &jsonString, int32_t uid)
{
if (jsonString.empty()) {
return nullptr;
}
nlohmann::json jsonObject = nlohmann::json::parse(jsonString, nullptr, false);
if (jsonObject.is_discarded()) {
TAG_LOGE(AAFwkTag::WANTAGENT, "Failed to parse json string");
return nullptr;
}
int requestCode = -1;
if (jsonObject.contains("requestCode") && jsonObject["requestCode"].is_number_integer()) {
requestCode = jsonObject.at("requestCode").get<int>();
}
int userId = -1;
if (jsonObject.contains("userId") && jsonObject["userId"].is_number_integer()) {
userId = jsonObject.at("userId").get<int>();
}
WantAgentConstant::OperationType operationType = WantAgentConstant::OperationType::UNKNOWN_TYPE;
if (jsonObject.contains("operationType") && jsonObject["operationType"].is_number_integer()) {
operationType = static_cast<WantAgentConstant::OperationType>(jsonObject.at("operationType").get<int>());
}
std::vector<WantAgentConstant::Flags> flagsVec = ParseFlags(jsonObject);
std::vector<std::shared_ptr<AAFwk::Want>> wants = {};
if (jsonObject.contains("wants") && jsonObject["wants"].is_array()) {
for (auto &wantObj : jsonObject.at("wants")) {
if (wantObj.is_string()) {
auto wantString = wantObj.get<std::string>();
wants.emplace_back(std::make_shared<AAFwk::Want>(*Want::FromString(wantString)));
}
}
}
std::shared_ptr<AAFwk::WantParams> extraInfo = nullptr;
if (jsonObject.contains("extraInfo") && jsonObject["extraInfo"].is_object()) {
auto extraInfoObj = jsonObject.at("extraInfo");
if (extraInfoObj.contains("extraInfoValue") && extraInfoObj["extraInfoValue"].is_string()) {
AAFwk::WantParams params;
if (!AAFwk::WantParamWrapperJson::Parse(extraInfoObj.at("extraInfoValue").get<std::string>(), params)) {
TAG_LOGE(AAFwkTag::WANTAGENT, "parse want params json failed");
}
extraInfo = std::make_shared<AAFwk::WantParams>(params);
}
}
int32_t appIndex = 0;
if (jsonObject.contains("appIndex") && jsonObject["appIndex"].is_number_integer()) {
appIndex = jsonObject.at("appIndex").get<int>();
}
WantAgentInfo info(requestCode, appIndex, operationType, flagsVec, wants, extraInfo);
return GetWantAgent(info, userId, uid);
}
bool WantAgentHelper::HasWantParamsEnvelope(const std::string &jsonString)
{
if (jsonString.empty()) {
return false;
}
nlohmann::json jsonObject = nlohmann::json::parse(jsonString, nullptr, false);
if (jsonObject.is_discarded() || !jsonObject.is_object()) {
return false;
}
if (!jsonObject.contains("extraInfo") || !jsonObject["extraInfo"].is_object()) {
return false;
}
const auto &extraInfoObj = jsonObject["extraInfo"];
if (!extraInfoObj.contains("extraInfoValue") || !extraInfoObj["extraInfoValue"].is_string()) {
return false;
}
return AAFwk::WantParamWrapperJson::HasEnvelope(extraInfoObj["extraInfoValue"].get<std::string>());
}
std::vector<WantAgentConstant::Flags> WantAgentHelper::ParseFlags(nlohmann::json jsonObject)
{
int flags = -1;
@@ -1533,4 +1533,152 @@ HWTEST_F(WantAgentHelperTest, GetWantFromProxy_0100, TestSize.Level1)
auto want = WantAgentHelper::GetWantFromProxy(wantAgent);
EXPECT_EQ(want, nullptr);
}
/*
* @tc.number : WantAgentHelper_6700
* @tc.name : WantAgentHelper ToStringWithEnvelope
* @tc.desc : Test ToStringWithEnvelope uses WantParamWrapperJson without changing ToString format.
*/
HWTEST_F(WantAgentHelperTest, WantAgentHelper_6700, Function | MediumTest | Level1)
{
std::shared_ptr<Want> want = std::make_shared<Want>();
ElementName element("device", "bundleName", "abilityNameToJson");
want->SetElement(element);
WantAgentInfo wantAgentInfo;
wantAgentInfo.wants_.emplace_back(want);
wantAgentInfo.operationType_ = WantAgentConstant::OperationType::START_ABILITY;
wantAgentInfo.requestCode_ = 10;
std::shared_ptr<WantParams> wParams = std::make_shared<WantParams>();
wParams->SetParam("key", Boolean::Box(true));
wantAgentInfo.extraInfo_ = wParams;
auto wantAgent = WantAgentHelper::GetWantAgent(wantAgentInfo);
ASSERT_NE(wantAgent, nullptr);
auto legacyString = WantAgentHelper::ToString(wantAgent);
auto jsonString = WantAgentHelper::ToStringWithEnvelope(wantAgent);
ASSERT_FALSE(legacyString.empty());
ASSERT_FALSE(jsonString.empty());
auto legacyObject = nlohmann::json::parse(legacyString, nullptr, false);
auto jsonObject = nlohmann::json::parse(jsonString, nullptr, false);
ASSERT_FALSE(legacyObject.is_discarded());
ASSERT_FALSE(jsonObject.is_discarded());
auto legacyExtraInfo = legacyObject.at("extraInfo").at("extraInfoValue").get<std::string>();
auto jsonExtraInfo = jsonObject.at("extraInfo").at("extraInfoValue").get<std::string>();
EXPECT_EQ(legacyExtraInfo.find("ohos.want.paramsStringEnvelope"), std::string::npos);
EXPECT_NE(jsonExtraInfo.find("ohos.want.paramsStringEnvelope"), std::string::npos);
EXPECT_FALSE(WantAgentHelper::HasWantParamsEnvelope(legacyString));
EXPECT_TRUE(WantAgentHelper::HasWantParamsEnvelope(jsonString));
}
/*
* @tc.number : WantAgentHelper_6710
* @tc.name : WantAgentHelper FromStringWithEnvelope
* @tc.desc : Test FromStringWithEnvelope restores WantAgent from WantParamWrapperJson extraInfo.
*/
HWTEST_F(WantAgentHelperTest, WantAgentHelper_6710, Function | MediumTest | Level1)
{
std::shared_ptr<Want> want = std::make_shared<Want>();
ElementName element("device", "bundleName", "abilityNameFromJson");
want->SetElement(element);
WantAgentInfo wantAgentInfo;
wantAgentInfo.wants_.emplace_back(want);
wantAgentInfo.operationType_ = WantAgentConstant::OperationType::START_ABILITY;
wantAgentInfo.requestCode_ = 10;
std::shared_ptr<WantParams> wParams = std::make_shared<WantParams>();
wParams->SetParam("key", Boolean::Box(true));
wantAgentInfo.extraInfo_ = wParams;
auto wantAgent = WantAgentHelper::GetWantAgent(wantAgentInfo);
ASSERT_NE(wantAgent, nullptr);
auto jsonString = WantAgentHelper::ToStringWithEnvelope(wantAgent);
auto restoredWantAgent = WantAgentHelper::FromStringWithEnvelope(jsonString);
ASSERT_NE(restoredWantAgent, nullptr);
auto restoredWant = WantAgentHelper::GetWant(restoredWantAgent);
ASSERT_NE(restoredWant, nullptr);
auto value = restoredWant->GetParams().GetParam("key");
auto boolValue = IBoolean::Query(value);
ASSERT_NE(boolValue, nullptr);
EXPECT_TRUE(Boolean::Unbox(boolValue));
}
/*
* @tc.number : WantAgentHelper_6720
* @tc.name : WantAgentHelper ToStringWithEnvelope invalid input
* @tc.desc : Test ToStringWithEnvelope invalid agent branches.
*/
HWTEST_F(WantAgentHelperTest, WantAgentHelper_6720, Function | MediumTest | Level1)
{
EXPECT_TRUE(WantAgentHelper::ToStringWithEnvelope(nullptr).empty());
auto wantAgentWithoutPendingWant = std::make_shared<WantAgent>(std::shared_ptr<PendingWant>(nullptr));
EXPECT_TRUE(WantAgentHelper::ToStringWithEnvelope(wantAgentWithoutPendingWant).empty());
auto pendingWantWithoutTarget = std::make_shared<PendingWant>(sptr<IWantSender>(nullptr));
auto wantAgentWithoutInfo = std::make_shared<WantAgent>(pendingWantWithoutTarget);
EXPECT_TRUE(WantAgentHelper::ToStringWithEnvelope(wantAgentWithoutInfo).empty());
}
/*
* @tc.number : WantAgentHelper_6730
* @tc.name : WantAgentHelper FromStringWithEnvelope invalid input
* @tc.desc : Test FromStringWithEnvelope invalid string and malformed field branches.
*/
HWTEST_F(WantAgentHelperTest, WantAgentHelper_6730, Function | MediumTest | Level1)
{
EXPECT_EQ(WantAgentHelper::FromStringWithEnvelope(""), nullptr);
EXPECT_EQ(WantAgentHelper::FromStringWithEnvelope("{"), nullptr);
EXPECT_EQ(WantAgentHelper::FromStringWithEnvelope(R"({"requestCode":"bad","wants":[]})"),
nullptr);
EXPECT_EQ(WantAgentHelper::FromStringWithEnvelope(R"({"wants":[100]})"), nullptr);
Want want;
ElementName element("device", "bundleName", "abilityNameFromJsonInvalid");
want.SetElement(element);
const auto originParamSize = want.GetParams().Size();
nlohmann::json jsonObject;
jsonObject["requestCode"] = 6730;
jsonObject["operationType"] = static_cast<int32_t>(WantAgentConstant::OperationType::START_ABILITY);
jsonObject["flags"] = static_cast<int32_t>(FLAG_UPDATE_CURRENT);
jsonObject["userId"] = -1;
jsonObject["appIndex"] = "bad";
jsonObject["wants"] = nlohmann::json::array({ want.ToString() });
jsonObject["extraInfo"] = { { "extraInfoValue", "invalid want params json" } };
auto wantAgent = WantAgentHelper::FromStringWithEnvelope(jsonObject.dump());
ASSERT_NE(wantAgent, nullptr);
auto restoredWant = WantAgentHelper::GetWant(wantAgent);
ASSERT_NE(restoredWant, nullptr);
EXPECT_EQ(restoredWant->GetParams().Size(), originParamSize);
jsonObject["requestCode"] = 6731;
jsonObject["extraInfo"] = "bad";
EXPECT_NE(WantAgentHelper::FromStringWithEnvelope(jsonObject.dump()), nullptr);
jsonObject["requestCode"] = 6732;
jsonObject["extraInfo"] = { { "extraInfoValue", 100 } };
EXPECT_NE(WantAgentHelper::FromStringWithEnvelope(jsonObject.dump()), nullptr);
}
/*
* @tc.number : WantAgentHelper_6740
* @tc.name : WantAgentHelper HasWantParamsEnvelope
* @tc.desc : Test HasWantParamsEnvelope dispatches only when extraInfoValue uses WantParamWrapperJson envelope.
*/
HWTEST_F(WantAgentHelperTest, WantAgentHelper_6740, Function | MediumTest | Level1)
{
nlohmann::json jsonObject;
jsonObject["extraInfo"] = { { "extraInfoValue", R"( {"ohos.want.paramsStringEnvelope":{}} )" } };
EXPECT_TRUE(WantAgentHelper::HasWantParamsEnvelope(jsonObject.dump()));
jsonObject["extraInfo"] = { { "extraInfoValue", "legacy want params" } };
EXPECT_FALSE(WantAgentHelper::HasWantParamsEnvelope(jsonObject.dump()));
EXPECT_FALSE(WantAgentHelper::HasWantParamsEnvelope(""));
EXPECT_FALSE(WantAgentHelper::HasWantParamsEnvelope("{"));
EXPECT_FALSE(WantAgentHelper::HasWantParamsEnvelope(R"({"extraInfo":"bad"})"));
EXPECT_FALSE(WantAgentHelper::HasWantParamsEnvelope(R"({"extraInfo":{"extraInfoValue":100}})"));
EXPECT_FALSE(WantAgentHelper::HasWantParamsEnvelope(R"({"wants":[]})"));
}
} // namespace OHOS::AbilityRuntime::WantAgent