* 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 "call_manager_config.h"
#include <securec.h>
#include <unistd.h>
#include <fstream>
#include "telephony_errors.h"
#include "telephony_log_wrapper.h"
#include "telephony_permission.h"
#include "os_account_manager.h"
namespace OHOS {
namespace Telephony {
constexpr const char PATH[] = "/system/etc/telephony/call_manager_config.json";
constexpr const char ITEM_HANG_UP_CELLULAR_CALL_BEFORE_ANSWER_LIST[] = "HangUpCellularCallBeforeAnswer";
constexpr const char ITEM_CONVERT_USERNAME_TO_PHONE_NUM_LIST[] = "ConvertUsernameToPhoneNum";
constexpr int MAX_BYTE_LEN = 10 * 1024;
constexpr int MAX_FEATURE_LIST_SIZE = 100;
std::unordered_set<std::string> CallManagerConfig::hangUpCellularCallBeforeAnswerList_{};
std::unordered_set<std::string> CallManagerConfig::convertUsernameToPhoneNumList_{};
std::atomic<bool> CallManagerConfig::isInit_(false);
ffrt::mutex CallManagerConfig::mutex_;
void CallManagerConfig::Init()
{
if (isInit_.load(std::memory_order_acquire)) {
TELEPHONY_LOGI("CallManagerConfig has init");
return;
}
std::lock_guard<ffrt::mutex> lock(mutex_);
if (isInit_.load(std::memory_order_acquire)) {
return;
}
hangUpCellularCallBeforeAnswerList_.clear();
convertUsernameToPhoneNumList_.clear();
if (ParserConfigJson() != TELEPHONY_SUCCESS) {
return;
}
TELEPHONY_LOGI("CallManagerConfig init success");
isInit_.store(true, std::memory_order_release);
}
std::string CallManagerConfig::BuildMatchKey(int32_t uid, const std::string &methodName)
{
std::string bundleName = "";
int32_t userId = 0;
if (!TelephonyPermission::GetBundleNameByUid(uid, bundleName)) {
TELEPHONY_LOGW("%{public}s: GetBundleNameByUid failed, uid=%{public}d", methodName.c_str(), uid);
return "";
}
if (AccountSA::OsAccountManager::GetOsAccountLocalIdFromUid(uid, userId) != 0) {
TELEPHONY_LOGW("%{public}s: GetOsAccountLocalIdFromUid failed, uid=%{public}d", methodName.c_str(), uid);
return "";
}
std::string appIdentifier = "";
TelephonyPermission::GetAppIdentifier(bundleName, appIdentifier, userId);
std::string matchKey = bundleName + ":" + appIdentifier;
return matchKey;
}
bool CallManagerConfig::ShouldHangUpCellularCallBeforeAnswer(int32_t uid)
{
Init();
std::string matchKey = BuildMatchKey(uid, std::string("HangUpCellularCallBeforeAnswer"));
if (matchKey.empty()) {
return false;
}
return hangUpCellularCallBeforeAnswerList_.find(matchKey) != hangUpCellularCallBeforeAnswerList_.end();
}
bool CallManagerConfig::ShouldConvertUsernameToPhoneNum(int32_t uid)
{
Init();
std::string matchKey = BuildMatchKey(uid, std::string("ConvertUsernameToPhoneNum"));
if (matchKey.empty()) {
return false;
}
return convertUsernameToPhoneNumList_.find(matchKey) != convertUsernameToPhoneNumList_.end();
}
int32_t CallManagerConfig::ParserConfigJson()
{
std::unique_ptr<char[]> content;
int32_t ret = LoaderJsonFile(content, PATH);
if (ret != TELEPHONY_SUCCESS) {
TELEPHONY_LOGW("CallManagerConfig: load fail");
return ret;
}
if (content == nullptr) {
TELEPHONY_LOGW("CallManagerConfig: content is nullptr");
return TELEPHONY_ERR_READ_DATA_FAIL;
}
cJSON *root = cJSON_Parse(content.get());
if (root == nullptr) {
TELEPHONY_LOGW("CallManagerConfig: json root is error");
return TELEPHONY_ERR_READ_DATA_FAIL;
}
ParseFeatureList(root, ITEM_HANG_UP_CELLULAR_CALL_BEFORE_ANSWER_LIST, hangUpCellularCallBeforeAnswerList_);
ParseFeatureList(root, ITEM_CONVERT_USERNAME_TO_PHONE_NUM_LIST, convertUsernameToPhoneNumList_);
cJSON_Delete(root);
root = nullptr;
return TELEPHONY_SUCCESS;
}
void CallManagerConfig::ParseFeatureList(const cJSON *root, const std::string &itemName,
std::unordered_set<std::string> &featureList)
{
cJSON *featureListItem = cJSON_GetObjectItem(root, itemName.c_str());
if (featureListItem != nullptr && cJSON_IsArray(featureListItem)) {
int size = cJSON_GetArraySize(featureListItem);
if (size <= 0 || size > MAX_FEATURE_LIST_SIZE) {
TELEPHONY_LOGW("CallManagerConfig: %{public}s size invalid, size=%{public}d", itemName.c_str(), size);
return;
}
for (int i = 0; i < size; i++) {
cJSON *item = cJSON_GetArrayItem(featureListItem, i);
std::string bundleName = ParseString(item);
if (!bundleName.empty()) {
featureList.insert(bundleName);
}
}
TELEPHONY_LOGI("CallManagerConfig loaded %{public}s with %{public}zu items",
itemName.c_str(), featureList.size());
} else {
TELEPHONY_LOGW("CallManagerConfig: %{public}s is invalid", itemName.c_str());
}
}
int32_t CallManagerConfig::LoaderJsonFile(std::unique_ptr<char[]> &content, const std::string &path)
{
char realPath[PATH_MAX] = { 0x00 };
if (realpath(path.c_str(), realPath) == nullptr) {
TELEPHONY_LOGW("CallManagerConfig: realpath fail");
return TELEPHONY_ERR_READ_DATA_FAIL;
}
std::ifstream file(realPath, std::ios::ate);
if (!file.is_open()) {
TELEPHONY_LOGW("CallManagerConfig: file open fail");
return TELEPHONY_ERR_READ_DATA_FAIL;
}
std::streamsize len = file.tellg();
if (len <= 0 || len > static_cast<std::streamsize>(MAX_BYTE_LEN)) {
TELEPHONY_LOGW("CallManagerConfig: invalid file size");
file.close();
return TELEPHONY_ERR_READ_DATA_FAIL;
}
file.seekg(0, std::ios::beg);
content = std::make_unique<char[]>(len + 1);
if (content == nullptr) {
TELEPHONY_LOGW("CallManagerConfig: malloc fail");
file.close();
return TELEPHONY_ERR_READ_DATA_FAIL;
}
if (memset_s(content.get(), len + 1, 0, len + 1) != EOK) {
TELEPHONY_LOGW("CallManagerConfig: memset_s fail");
content.reset();
file.close();
return TELEPHONY_ERR_READ_DATA_FAIL;
}
if (!file.read(content.get(), len)) {
TELEPHONY_LOGW("CallManagerConfig: file read fail");
content.reset();
file.close();
return TELEPHONY_ERR_READ_DATA_FAIL;
}
file.close();
return TELEPHONY_SUCCESS;
}
std::string CallManagerConfig::ParseString(const cJSON *value)
{
if (value != nullptr && value->type == cJSON_String && value->valuestring != nullptr) {
return value->valuestring;
}
return "";
}
}
}