* 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 <string>
#include <variant>
#include "extra_config_ani.h"
#include "global.h"
#include "input_attribute.h"
namespace OHOS {
namespace MiscServices {
std::string AniExtraConfig::ConvertString(ani_env* env, ani_object in)
{
if (env == nullptr) {
return "";
}
ani_boolean res;
ani_class stringCls {};
ani_status ret = env->FindClass("std.core.String", &stringCls);
if (ret != ANI_OK || stringCls == nullptr) {
IMSA_HILOGE("ConvertString, FindClass failed!");
return "";
}
ret = env->Object_InstanceOf(in, stringCls, &res);
if (ret != ANI_OK || !res) {
IMSA_HILOGE("ConvertString, obj is not string!");
return "";
}
ani_size sz {};
ani_string str = static_cast<ani_string>(in);
ani_status status;
if ((status = env->String_GetUTF8Size(str, &sz)) != ANI_OK) {
IMSA_HILOGE("Get string size failed, status: %{public}d.", status);
return "";
}
std::string result(sz + 1, 0);
if ((status = env->String_GetUTF8(str, result.data(), result.size(), &sz)) != ANI_OK) {
IMSA_HILOGE("Get string size failed, status: %{public}d.", status);
return "";
}
result.resize(sz);
return result;
}
ani_field AniExtraConfig::AniFindClassField(ani_env* env, ani_class cls, const char* name)
{
ani_field fld;
if (cls == nullptr || env == nullptr) {
return nullptr;
}
if (ANI_OK != env->Class_FindField(cls, name, &fld)) {
return nullptr;
}
return fld;
}
ani_class AniExtraConfig::AniGetClass(ani_env* env, const char* className)
{
if (env == nullptr) {
return nullptr;
}
ani_class cls {};
if (ANI_OK != env->FindClass(className, &cls)) {
return nullptr;
}
return cls;
}
ani_method AniExtraConfig::AniGetClassMethod(ani_env* env, const char* className,
const char* methodName, const char* signature)
{
if (env == nullptr) {
return nullptr;
}
ani_class retClass = AniGetClass(env, className);
if (retClass == nullptr) {
return nullptr;
}
ani_method retMethod {};
if (ANI_OK != env->Class_FindMethod(retClass, methodName, signature, &retMethod)) {
return nullptr;
}
return retMethod;
}
bool AniExtraConfig::ParseValue(ani_env* env, ani_object aniValue, CustomValueType &valueObj, uint32_t& valueSize)
{
if (env == nullptr || aniValue == nullptr) {
IMSA_HILOGE("ParseValue, param is invalid!");
return false;
}
ani_boolean res;
ani_class optionClass {};
env->FindClass("std.core.Boolean", &optionClass);
if (ANI_OK != env->Object_InstanceOf(aniValue, optionClass, &res)) {
IMSA_HILOGE("to boolean failed");
return false;
}
if (res) {
ani_boolean value;
if (ANI_OK != env->Object_CallMethodByName_Boolean(aniValue, "toBoolean", ":z", &value)) {
IMSA_HILOGE("ParseValue, Object_CallMethodByName_Boolean failed!");
return false;
}
valueObj = static_cast<bool>(value);
valueSize = sizeof(bool);
return true;
}
auto status = env->FindClass("std.core.Int", &optionClass);
if (status != ANI_OK|| env->Object_InstanceOf(aniValue, optionClass, &res) != ANI_OK) {
IMSA_HILOGE("to int failed");
return false;
}
if (res) {
ani_int value = 0;
if (ANI_OK != env->Object_CallMethodByName_Int(aniValue, "toInt", ":i", &value)) {
IMSA_HILOGE("ParseValue, Object_CallMethodByName_Int failed!");
return false;
}
valueObj = static_cast<int32_t>(value);
valueSize = sizeof(int32_t);
return true;
}
env->FindClass("std.core.String", &optionClass);
if (ANI_OK != env->Object_InstanceOf(aniValue, optionClass, &res)) {
IMSA_HILOGE("to str failed");
return false;
}
if (res) {
auto tmpString = ConvertString(env, aniValue);
valueObj = tmpString;
valueSize = tmpString.size();
return true;
}
return true;
}
bool AniExtraConfig::ParseRecordItem(ani_env* env, ani_object entryIterator,
ani_method iterNextMethod, ani_class iterResultClass, CustomSettings& out)
{
if (env == nullptr) {
IMSA_HILOGE("env is null");
return false;
}
ani_boolean iter_done = false;
uint32_t totalSize = 0;
while (!iter_done) {
ani_object iteratorNextResult = {};
env->Object_CallMethod_Ref(entryIterator, iterNextMethod, reinterpret_cast<ani_ref *>(&iteratorNextResult));
ani_field fieldTmp = AniFindClassField(env, iterResultClass, "done");
ani_status ret = env->Object_GetField_Boolean(iteratorNextResult, fieldTmp, &iter_done);
if (ret != ANI_OK || iter_done) {
break;
}
ani_object temp_ani_item = {};
fieldTmp = AniFindClassField(env, iterResultClass, "value");
env->Object_GetField_Ref(iteratorNextResult, fieldTmp, reinterpret_cast<ani_ref *>(&temp_ani_item));
ani_ref temp_ani_key = {};
env->Object_GetFieldByName_Ref(temp_ani_item, "$0", &temp_ani_key);
ani_object keyObj = reinterpret_cast<ani_object>(temp_ani_key);
std::string stdKey = ConvertString(env, keyObj);
if (stdKey.size() == 0) {
break;
}
uint32_t keySize = stdKey.size();
ani_ref temp_ani_val = {};
env->Object_GetFieldByName_Ref(temp_ani_item, "$1", &temp_ani_val);
CustomValueType parseResult {};
uint32_t valueSize = 0;
ParseValue(env, static_cast<ani_object>(temp_ani_val), parseResult, valueSize);
if ((keySize > UINT32_MAX - valueSize) || (totalSize > UINT32_MAX - keySize - valueSize)) {
out.clear();
IMSA_HILOGE("integer overflow detected in size calculation");
return false;
}
totalSize = totalSize + keySize + valueSize;
out[stdKey] = parseResult;
}
ExtraConfigInner configInner;
configInner.customSettings = out;
if (totalSize > DEFAULT_MAX_EXTRA_CONFIG_SIZE || !configInner.IsMarshallingPass()) {
out.clear();
IMSA_HILOGE("input data exceed limit. totalSize: %{public}d", totalSize);
return false;
}
return true;
}
bool AniExtraConfig::ParseRecord(ani_env* env, ani_object recordObj, CustomSettings& out, uint32_t maxLen)
{
if (env == nullptr || recordObj == nullptr) {
IMSA_HILOGE("param is invalid");
return false;
}
ani_method entryMethod = AniGetClassMethod(env, "std.core.Record", "entries", ":C{std.core.IterableIterator}");
ani_method iterNextMethod = AniGetClassMethod(env, "std.core.Iterator", "next", ":C{std.core.IteratorResult}");
ani_class iterResultClass = AniGetClass(env, "std.core.IteratorResult");
if (entryMethod == nullptr || iterNextMethod == nullptr || iterResultClass == nullptr) {
IMSA_HILOGE("ParseRecord, get iter failed!");
return false;
}
ani_object entryIterator = {};
if (ANI_OK != env->Object_CallMethod_Ref(recordObj, entryMethod, reinterpret_cast<ani_ref *>(&entryIterator))) {
IMSA_HILOGE("ParseRecord, Object_CallMethod_Ref failed!");
return false;
}
if (!ParseRecordItem(env, entryIterator, iterNextMethod, iterResultClass, out)) {
IMSA_HILOGE("ParseRecord, ParseRecordItem!");
return false;
}
return true;
}
bool AniExtraConfig::GetRecordOrUndefined(ani_env* env, ani_object in,
const char* name, CustomSettings& out, uint32_t maxLen)
{
if (maxLen > DEFAULT_MAX_EXTRA_CONFIG_SIZE) {
maxLen = DEFAULT_MAX_EXTRA_CONFIG_SIZE;
}
if (env == nullptr || in == nullptr) {
return false;
}
ani_ref ref = nullptr;
ani_boolean isUndefined = false;
ani_boolean isNull = false;
ani_status ret = env->Object_GetPropertyByName_Ref(in, name, &ref);
if (ret != ANI_OK) {
IMSA_HILOGE("Object_GetPropertyByName_Ref failed, ret: %{public}d", ret);
return false;
}
ret = env->Reference_IsUndefined(ref, &isUndefined);
if (ret != ANI_OK || isUndefined) {
IMSA_HILOGE("customSettings is undefined, ret: %{public}d", ret);
return false;
}
ret = env->Reference_IsNull(ref, &isNull);
if (ret != ANI_OK || isNull) {
IMSA_HILOGE("customSettings is null, ret: %{public}d", ret);
return false;
}
ani_class cls;
ret = env->FindClass("std.core.Record", &cls);
if (ret != ANI_OK || cls == nullptr) {
IMSA_HILOGE("customSettings FindClass failed, ret: %{public}d", ret);
return false;
}
ani_boolean result = false;
auto recordObj = static_cast<ani_object>(ref);
ret = env->Object_InstanceOf(recordObj, cls, &result);
if (ret != ANI_OK || !result) {
IMSA_HILOGE("customSettings is not record, ret: %{public}d", ret);
return false;
}
if (!ParseRecord(env, recordObj, out, maxLen)) {
IMSA_HILOGE("ParseRecord failed");
return false;
}
return true;
}
bool AniExtraConfig::GetExtraConfig([[maybe_unused]]ani_env* env, ani_object in, ExtraConfig &out, uint32_t maxLen)
{
if (maxLen > DEFAULT_MAX_EXTRA_CONFIG_SIZE) {
maxLen = DEFAULT_MAX_EXTRA_CONFIG_SIZE;
}
if (env == nullptr) {
IMSA_HILOGE("env is null");
return false;
}
ani_boolean isUndefined = false;
ani_status ret = env->Reference_IsUndefined(in, &isUndefined);
if (ret != ANI_OK || isUndefined) {
IMSA_HILOGE("ExtraConfig is undefined, ret: %{public}d", ret);
return false;
}
ani_boolean isNull = false;
ret = env->Reference_IsNull(in, &isNull);
if (ret != ANI_OK || isNull) {
IMSA_HILOGE("ExtraConfig is null, ret: %{public}d", ret);
return false;
}
if (!GetRecordOrUndefined(env, in, "customSettings", out.customSettings, maxLen)) {
IMSA_HILOGE("GetRecordOrUndefined failed");
return false;
}
IMSA_HILOGI("parse extraConfig success!");
return true;
}
}
}