* Copyright (c) 2022-2023 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 "daudio_utils.h"
#include <ctime>
#include <map>
#include <ostream>
#include <sstream>
#include "daudio_constants.h"
#include "daudio_errcode.h"
#include "daudio_log.h"
#include "parameter.h"
#undef DH_LOG_TAG
#define DH_LOG_TAG "DAudioUtils"
namespace OHOS {
namespace DistributedHardware {
constexpr size_t INT32_SHORT_ID_LENGTH = 20;
constexpr size_t INT32_PLAINTEXT_LENGTH = 4;
constexpr size_t INT32_MIN_ID_LENGTH = 3;
constexpr uint8_t MAX_KEY_DH_ID_LEN = 20;
int32_t SetThreadPriority(int32_t priority)
{
struct sched_param threadParam;
threadParam.sched_priority = priority;
if (sched_setscheduler(0, SCHED_FIFO, &threadParam) != 0) {
DHLOGE("Set thread priority failed");
return ERR_DH_AUDIO_HDF_INVALID_OPERATION;
}
return DH_SUCCESS;
}
std::string GetAnonyString(const std::string &value)
{
std::string res;
std::string tmpStr("******");
size_t strLen = value.length();
if (strLen < INT32_MIN_ID_LENGTH) {
return tmpStr;
}
if (strLen <= INT32_SHORT_ID_LENGTH) {
res += value[0];
res += tmpStr;
res += value[strLen - 1];
} else {
res.append(value, 0, INT32_PLAINTEXT_LENGTH);
res += tmpStr;
res.append(value, strLen - INT32_PLAINTEXT_LENGTH, INT32_PLAINTEXT_LENGTH);
}
return res;
}
std::string GetChangeDevIdMap(int32_t devId)
{
std::string result = PRINT_NONE;
switch (devId) {
case DEFAULT_CAPTURE_ID:
result = PRINT_MIC;
break;
case LOW_LATENCY_RENDER_ID:
result = PRINT_SPK;
break;
case DEFAULT_RENDER_ID:
result = PRINT_SPK;
break;
case OFFLOAD_RENDER_ID:
result = PRINT_SPK;
break;
default:
break;
}
return result;
}
int32_t GetAudioParamStr(const std::string ¶ms, const std::string &key, std::string &value)
{
size_t step = key.size();
if (step >= params.size()) {
return ERR_DH_AUDIO_HDF_FAIL;
}
size_t pos = params.find(key);
if (pos == params.npos || params.at(pos + step) != '=') {
return ERR_DH_AUDIO_COMMON_NOT_FOUND_KEY;
}
size_t splitPosEnd = params.find(';', pos);
if (splitPosEnd != params.npos) {
if (pos + step + 1 > splitPosEnd) {
return ERR_DH_AUDIO_HDF_FAIL;
}
value = params.substr(pos + step + 1, splitPosEnd - pos - step - 1);
} else {
value = params.substr(pos + step + 1);
}
return DH_SUCCESS;
}
int32_t GetAudioParamInt(const std::string ¶ms, const std::string &key, int32_t &value)
{
std::string val = "-1";
int32_t ret = GetAudioParamStr(params, key, val);
if (!CheckIsNum(val)) {
DHLOGE("String is not number. str:%{public}s.", val.c_str());
return -1;
}
value = std::atoi(val.c_str());
return ret;
}
bool CheckIsNum(const std::string &jsonString)
{
if (jsonString.empty() || jsonString.size() > MAX_KEY_DH_ID_LEN) {
DHLOGE("Json string size is zero or too long.");
return false;
}
for (char const &c : jsonString) {
if (!std::isdigit(c)) {
DHLOGE("Json string is not number.");
return false;
}
}
return true;
}
int32_t GetAudioParamUInt(const std::string ¶ms, const std::string &key, uint32_t &value)
{
value = 0;
return DH_SUCCESS;
}
int32_t GetAudioParamBool(const std::string ¶ms, const std::string &key, bool &value)
{
std::string val;
GetAudioParamStr(params, key, val);
value = (val != "0");
return DH_SUCCESS;
}
int32_t SetAudioParamStr(std::string ¶ms, const std::string &key, const std::string &value)
{
params = params + key + '=' + value + ';';
return DH_SUCCESS;
}
int32_t GetDevTypeByDHId(int32_t dhId)
{
if (static_cast<uint32_t>(dhId) & 0x8000000) {
return AUDIO_DEVICE_TYPE_MIC;
} else if (static_cast<uint32_t>(dhId) & 0x7ffffff) {
return AUDIO_DEVICE_TYPE_SPEAKER;
}
return AUDIO_DEVICE_TYPE_UNKNOWN;
}
int64_t GetNowTimeUs()
{
std::chrono::microseconds nowUs =
std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now().time_since_epoch());
return nowUs.count();
}
uint32_t CalculateFrameSize(uint32_t sampleRate, uint32_t channelCount,
int32_t format, uint32_t timeInterval, bool isMMAP)
{
return isMMAP ? (sampleRate * channelCount * static_cast<uint32_t>(format) * timeInterval) /
AUDIO_MS_PER_SECOND : DEFAULT_AUDIO_DATA_SIZE;
}
int32_t CalculateSampleNum(uint32_t sampleRate, uint32_t timems)
{
return (sampleRate * timems) / AUDIO_MS_PER_SECOND;
}
int64_t GetCurNano()
{
int64_t result = -1;
struct timespec time;
clockid_t clockId = CLOCK_MONOTONIC;
int ret = clock_gettime(clockId, &time);
if (ret < 0) {
return result;
}
result = (time.tv_sec * AUDIO_NS_PER_SECOND) + time.tv_nsec;
return result;
}
int32_t AbsoluteSleep(int64_t nanoTime)
{
int32_t ret = -1;
if (nanoTime <= 0) {
return ret;
}
struct timespec time;
time.tv_sec = nanoTime / AUDIO_NS_PER_SECOND;
time.tv_nsec = nanoTime - (time.tv_sec * AUDIO_NS_PER_SECOND);
clockid_t clockId = CLOCK_MONOTONIC;
ret = clock_nanosleep(clockId, TIMER_ABSTIME, &time, nullptr);
return ret;
}
int64_t CalculateOffset(const int64_t frameIndex, const int64_t framePeriodNs, const int64_t startTime)
{
int64_t totalOffset = GetCurNano() - startTime;
return totalOffset - frameIndex * framePeriodNs;
}
int64_t UpdateTimeOffset(const int64_t frameIndex, const int64_t framePeriodNs, int64_t &startTime)
{
int64_t timeOffset = 0;
if (frameIndex == 0) {
startTime = GetCurNano();
} else if (frameIndex % AUDIO_OFFSET_FRAME_NUM == 0) {
timeOffset = CalculateOffset(frameIndex, framePeriodNs, startTime);
}
return timeOffset;
}
bool IsOutDurationRange(int64_t startTime, int64_t endTime, int64_t lastStartTime)
{
int64_t currentInterval = endTime - startTime;
int64_t twiceInterval = startTime - lastStartTime;
return (currentInterval > MAX_TIME_INTERVAL_US || twiceInterval > MAX_TIME_INTERVAL_US) ? true : false;
}
void SaveFile(const std::string fileName, uint8_t *audioData, int32_t size)
{
char path[PATH_MAX + 1] = {0x00};
if (fileName.length() > PATH_MAX || realpath(fileName.c_str(), path) == nullptr) {
DHLOGE("The file path is invalid.");
return;
}
std::ofstream ofs(path, std::ios::binary | std::ios::out | std::ios::app);
if (!ofs.is_open()) {
return;
}
ofs.write(reinterpret_cast<char *>(audioData), size);
ofs.close();
}
int32_t WrapCJsonItem(const std::initializer_list<std::pair<std::string, std::string>> &keys, std::string &content)
{
cJSON *jParam = cJSON_CreateObject();
if (jParam == nullptr) {
return ERR_DH_AUDIO_HDF_FAIL;
}
for (auto item : keys) {
cJSON_AddStringToObject(jParam, item.first.c_str(), item.second.c_str());
}
char *jsonData = cJSON_PrintUnformatted(jParam);
if (jsonData == nullptr) {
cJSON_Delete(jParam);
return ERR_DH_AUDIO_HDF_FAIL;
}
content = std::string(jsonData);
cJSON_Delete(jParam);
cJSON_free(jsonData);
return DH_SUCCESS;
}
bool CJsonParamCheck(const cJSON *jsonObj, const std::initializer_list<std::string> &keys)
{
if (jsonObj == nullptr || !cJSON_IsObject(jsonObj)) {
DHLOGE("JSON parameter is invalid.");
return false;
}
for (auto it = keys.begin(); it != keys.end(); it++) {
cJSON *paramValue = cJSON_GetObjectItemCaseSensitive(jsonObj, (*it).c_str());
if (paramValue == nullptr) {
DHLOGE("JSON parameter does not contain key: %{public}s", (*it).c_str());
return false;
}
}
return true;
}
std::string ParseStringFromArgs(const std::string &args, const char *key)
{
DHLOGD("ParseStringFrom Args : %{public}s", args.c_str());
cJSON *jParam = cJSON_Parse(args.c_str());
if (jParam == nullptr) {
DHLOGE("Failed to parse JSON: %{public}s", cJSON_GetErrorPtr());
cJSON_Delete(jParam);
return "Failed to parse JSON";
}
if (!CJsonParamCheck(jParam, { key })) {
DHLOGE("Not found the key : %{public}s.", key);
cJSON_Delete(jParam);
return "Not found the key.";
}
cJSON *dhIdItem = cJSON_GetObjectItem(jParam, key);
if (dhIdItem == NULL || !cJSON_IsString(dhIdItem)) {
DHLOGE("Not found the value of the key : %{public}s.", key);
cJSON_Delete(jParam);
return "Not found the value.";
}
std::string content(dhIdItem->valuestring);
cJSON_Delete(jParam);
DHLOGD("Parsed string is: %{public}s.", content.c_str());
return content;
}
template <typename T>
bool GetSysPara(const char *key, T &value)
{
CHECK_AND_RETURN_RET_LOG(key == nullptr, false, "key is nullptr");
char paraValue[30] = {0};
auto res = GetParameter(key, "-1", paraValue, sizeof(paraValue));
CHECK_AND_RETURN_RET_LOG(res <= 0, false, "GetParameter fail, key:%{public}s res:%{public}d", key, res);
DHLOGI("GetSysPara key:%{public}s value:%{public}s", key, paraValue);
std::stringstream valueStr;
valueStr << paraValue;
valueStr >> value;
return true;
}
template bool GetSysPara(const char *key, int32_t &value);
template bool GetSysPara(const char *key, uint32_t &value);
template bool GetSysPara(const char *key, int64_t &value);
template bool GetSysPara(const char *key, std::string &value);
bool IsParamEnabled(const std::string &key, bool &isEnabled)
{
int32_t policyFlag = 0;
if (GetSysPara(key.c_str(), policyFlag) && policyFlag == 1) {
isEnabled = true;
return true;
}
isEnabled = false;
return false;
}
std::map<std::string, std::string> DumpFileUtil::g_lastPara = {};
FILE *DumpFileUtil::OpenDumpFileInner(const std::string ¶, const std::string &fileName)
{
std::string filePath = DUMP_SERVICE_DIR + fileName;
std::string dumpPara;
FILE *dumpFile = nullptr;
bool res = GetSysPara(para.c_str(), dumpPara);
if (!res || dumpPara.empty()) {
DHLOGI("%{public}s is not set, dump dcamera is not required", para.c_str());
g_lastPara[para] = dumpPara;
return dumpFile;
}
DHLOGI("%{public}s = %{public}s, filePath: %{public}s", para.c_str(), dumpPara.c_str(), filePath.c_str());
if (dumpPara == "w") {
dumpFile = fopen(filePath.c_str(), "wb+");
CHECK_AND_RETURN_RET_LOG(dumpFile == nullptr, dumpFile, "Error opening dump file!");
} else if (dumpPara == "a") {
dumpFile = fopen(filePath.c_str(), "ab+");
CHECK_AND_RETURN_RET_LOG(dumpFile == nullptr, dumpFile, "Error opening dump file!");
}
g_lastPara[para] = dumpPara;
return dumpFile;
}
void DumpFileUtil::WriteDumpFile(FILE *dumpFile, void *buffer, size_t bufferSize)
{
if (dumpFile == nullptr) {
return;
}
CHECK_AND_RETURN_LOG(buffer == nullptr, "Invalid write param");
size_t writeResult = fwrite(buffer, 1, bufferSize, dumpFile);
CHECK_AND_RETURN_LOG(writeResult != bufferSize, "Failed to write the file.");
}
void DumpFileUtil::CloseDumpFile(FILE **dumpFile)
{
CHECK_AND_RETURN_LOG(dumpFile == nullptr, "Invalid file para");
if (*dumpFile) {
fclose(*dumpFile);
*dumpFile = nullptr;
}
}
void DumpFileUtil::ChangeDumpFileState(const std::string ¶, FILE **dumpFile, const std::string &filePath)
{
CHECK_AND_RETURN_LOG(dumpFile == nullptr || *dumpFile == nullptr, "Invalid file para");
CHECK_AND_RETURN_LOG(g_lastPara[para] != "w" || g_lastPara[para] != "a", "Invalid input para");
std::string dumpPara;
bool res = GetSysPara(para.c_str(), dumpPara);
if (!res || dumpPara.empty()) {
DHLOGE("get %{public}s fail", para.c_str());
}
if (g_lastPara[para] == "w" && dumpPara == "w") {
return;
}
CloseDumpFile(dumpFile);
OpenDumpFile(para, filePath, dumpFile);
}
void DumpFileUtil::OpenDumpFile(const std::string ¶, const std::string &fileName, FILE **file)
{
CHECK_AND_RETURN_LOG(file == nullptr, "Invalid file para");
if (*file != nullptr) {
DumpFileUtil::ChangeDumpFileState(para, file, fileName);
return;
}
if (para == DUMP_SERVER_PARA) {
*file = DumpFileUtil::OpenDumpFileInner(para, fileName);
}
}
}
}