* Copyright (C) 2024 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 <charconv>
#include <sys/stat.h>
#include "cjson_helper.h"
#include "timer_manager_interface.h"
namespace OHOS {
namespace MiscServices {
namespace {
constexpr const char* DB_PATH = "/data/service/el1/public/database/time/time.json";
constexpr size_t INDEX_TWO = 2;
}
std::mutex CjsonHelper::mutex_;
CjsonHelper &CjsonHelper::GetInstance()
{
static CjsonHelper cjsonHelper;
return cjsonHelper;
}
CjsonHelper::CjsonHelper()
{
std::ifstream file(DB_PATH);
if (file.is_open()) {
file.close();
return;
}
std::ofstream newFile(DB_PATH);
if (!newFile.is_open()) {
TIME_HILOGE(TIME_MODULE_SERVICE, "Create new file fail");
return;
}
cJSON* root = cJSON_CreateObject();
cJSON* holdTable = cJSON_CreateArray();
cJSON* dropTable = cJSON_CreateArray();
cJSON_AddItemToObject(root, HOLD_ON_REBOOT, holdTable);
cJSON_AddItemToObject(root, DROP_ON_REBOOT, dropTable);
char* jsonString = cJSON_Print(root);
if (jsonString != nullptr) {
newFile << jsonString;
cJSON_free(jsonString);
}
cJSON_Delete(root);
newFile.close();
}
bool CjsonHelper::LoadAndParseJsonFile(const std::string& tableName, cJSON* &db, cJSON* &table)
{
db = nullptr;
table = nullptr;
std::ifstream file(DB_PATH);
if (!file.good()) {
TIME_HILOGE(TIME_MODULE_SERVICE, "open json file fail!");
return false;
}
std::string fileContent((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
db = cJSON_Parse(fileContent.c_str());
table = cJSON_GetObjectItem(db, tableName.c_str());
if (table == NULL) {
TIME_HILOGE(TIME_MODULE_SERVICE, "%{public}s get fail!", tableName.c_str());
cJSON_Delete(db);
db = nullptr;
return false;
}
return true;
}
std::string CjsonHelper::QueryWant(std::string tableName, uint64_t timerId)
{
std::string data = "";
cJSON* db = NULL;
cJSON* table = QueryTable(tableName, &db);
if (table == NULL) {
TIME_HILOGE(TIME_MODULE_SERVICE, "QueryTable fail!");
cJSON_Delete(db);
return data;
}
int size = cJSON_GetArraySize(table);
for (int i = 0; i < size; ++i) {
cJSON* obj = cJSON_GetArrayItem(table, i);
auto item = cJSON_GetObjectItem(obj, "timerId");
if (!IsString(item)) {
continue;
}
if (item->valuestring == std::to_string(timerId)) {
item = cJSON_GetObjectItem(obj, "wantAgent");
if (IsString(item)) {
data = item->valuestring;
}
break;
}
}
cJSON_Delete(db);
return data;
}
cJSON* CjsonHelper::QueryTable(std::string tableName, cJSON** db)
{
cJSON* data = NULL;
std::lock_guard<std::mutex> lock(mutex_);
std::ifstream file(DB_PATH);
if (!file.good()) {
TIME_HILOGE(TIME_MODULE_SERVICE, "open json file fail!");
return data;
}
std::string fileContent((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
*db = cJSON_Parse(fileContent.c_str());
cJSON* table = cJSON_GetObjectItem(*db, tableName.c_str());
if (table == NULL) {
TIME_HILOGE(TIME_MODULE_SERVICE, "%{public}s get fail!", tableName.c_str());
return data;
}
data = table;
return data;
}
bool CjsonHelper::StrToI64(std::string str, int64_t& value)
{
auto [ptr, ec] = std::from_chars(str.data(), str.data() + str.size(), value);
return ec == std::errc{} && ptr == str.data() + str.size();
}
bool Compare(const std::tuple<std::string, std::string, int64_t>& a,
const std::tuple<std::string, std::string, int64_t>& b)
{
return std::get<INDEX_TWO>(a) < std::get<INDEX_TWO>(b);
}
std::vector<std::tuple<std::string, std::string, int64_t>> CjsonHelper::QueryAutoReboot()
{
std::vector<std::tuple<std::string, std::string, int64_t>> result;
cJSON* db = NULL;
cJSON* table = QueryTable(HOLD_ON_REBOOT, &db);
if (table == NULL) {
TIME_HILOGE(TIME_MODULE_SERVICE, "QueryTable fail!");
cJSON_Delete(db);
return result;
}
int size = cJSON_GetArraySize(table);
for (int i = 0; i < size; ++i) {
cJSON* obj = cJSON_GetArrayItem(table, i);
auto state = cJSON_GetObjectItem(obj, "state");
if (!IsNumber(state)) {
continue;
}
if (state->valueint == 1) {
auto item = cJSON_GetObjectItem(obj, "bundleName");
if (!IsString(item)) {
continue;
}
std::string bundleName = item->valuestring;
item = cJSON_GetObjectItem(obj, "name");
if (!IsString(item)) {
continue;
}
std::string name = item->valuestring;
item = cJSON_GetObjectItem(obj, "triggerTime");
if (!IsString(item)) {
continue;
}
int64_t triggerTime;
if (!StrToI64(item->valuestring, triggerTime)) {
continue;
}
std::tuple<std::string, std::string, int64_t> tuple(bundleName, name, triggerTime);
result.push_back(tuple);
}
}
cJSON_Delete(db);
std::sort(result.begin(), result.end(), Compare);
return result;
}
bool CjsonHelper::Insert(std::string tableName, std::shared_ptr<TimerEntry> timerInfo)
{
cJSON* newLine = cJSON_CreateObject();
cJSON_AddStringToObject(newLine, "name", timerInfo->name.c_str());
cJSON_AddStringToObject(newLine, "timerId", std::to_string(timerInfo->id).c_str());
cJSON_AddNumberToObject(newLine, "type", timerInfo->type);
cJSON_AddNumberToObject(newLine, "flag", timerInfo->flag);
cJSON_AddNumberToObject(newLine, "windowLength", timerInfo->windowLength);
cJSON_AddNumberToObject(newLine, "interval", timerInfo->interval);
cJSON_AddNumberToObject(newLine, "uid", timerInfo->uid);
cJSON_AddNumberToObject(newLine, "pid", timerInfo->pid);
cJSON_AddStringToObject(newLine, "bundleName", timerInfo->bundleName.c_str());
cJSON_AddStringToObject(newLine, "wantAgent",
OHOS::AbilityRuntime::WantAgent::WantAgentHelper::ToString(timerInfo->wantAgent).c_str());
cJSON_AddNumberToObject(newLine, "state", 0);
cJSON_AddStringToObject(newLine, "triggerTime", "0");
std::lock_guard<std::mutex> lock(mutex_);
std::ifstream file(DB_PATH);
if (!file.good()) {
TIME_HILOGE(TIME_MODULE_SERVICE, "open json file fail!");
cJSON_Delete(newLine);
return false;
}
std::string fileContent((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
cJSON* db = cJSON_Parse(fileContent.c_str());
cJSON* table = cJSON_GetObjectItem(db, tableName.c_str());
if (table == NULL) {
TIME_HILOGE(TIME_MODULE_SERVICE, "%{public}s get fail!", tableName.c_str());
cJSON_Delete(db);
cJSON_Delete(newLine);
return false;
}
cJSON_AddItemToArray(table, newLine);
SaveJson(db);
cJSON_Delete(db);
return true;
}
bool CjsonHelper::UpdateTrigger(std::string tableName, int64_t timerId, int64_t triggerTime)
{
std::lock_guard<std::mutex> lock(mutex_);
cJSON* db = nullptr;
cJSON* table = nullptr;
if (!LoadAndParseJsonFile(tableName, db, table)) {
return false;
}
int size = cJSON_GetArraySize(table);
for (int i = 0; i < size; ++i) {
cJSON* obj = cJSON_GetArrayItem(table, i);
auto timerIdObj = cJSON_GetObjectItem(obj, "timerId");
if (!IsString(timerIdObj)) {
continue;
}
if (timerIdObj->valuestring == std::to_string(timerId)) {
cJSON_ReplaceItemInObject(obj, "state", cJSON_CreateNumber(1));
cJSON_ReplaceItemInObject(obj, "triggerTime", cJSON_CreateString(std::to_string(triggerTime).c_str()));
SaveJson(db);
break;
}
}
cJSON_Delete(db);
return true;
}
bool CjsonHelper::UpdateTriggerGroup(std::string tableName, std::vector<std::pair<uint64_t, uint64_t>> timerVec)
{
if (timerVec.empty()) {
return false;
}
std::lock_guard<std::mutex> lock(mutex_);
cJSON* db = nullptr;
cJSON* table = nullptr;
if (!LoadAndParseJsonFile(tableName, db, table)) {
return false;
}
int size = cJSON_GetArraySize(table);
for (int i = 0; i < size; ++i) {
cJSON* obj = cJSON_GetArrayItem(table, i);
auto timerIdObj = cJSON_GetObjectItem(obj, "timerId");
if (!IsString(timerIdObj)) {
continue;
}
for (auto it = timerVec.begin(); it != timerVec.end(); ++it) {
std::string timerId = std::to_string(it->first);
if (timerIdObj->valuestring == timerId) {
cJSON_ReplaceItemInObject(obj, "state", cJSON_CreateNumber(1));
std::string triggerTime = std::to_string(it->second);
cJSON_ReplaceItemInObject(obj, "triggerTime", cJSON_CreateString(triggerTime.c_str()));
}
}
}
SaveJson(db);
cJSON_Delete(db);
return true;
}
bool CjsonHelper::UpdateState(std::string tableName, int64_t timerId)
{
std::lock_guard<std::mutex> lock(mutex_);
cJSON* db = nullptr;
cJSON* table = nullptr;
if (!LoadAndParseJsonFile(tableName, db, table)) {
return false;
}
int size = cJSON_GetArraySize(table);
for (int i = 0; i < size; ++i) {
cJSON* obj = cJSON_GetArrayItem(table, i);
auto timerIdObj = cJSON_GetObjectItem(obj, "timerId");
auto stateObj = cJSON_GetObjectItem(obj, "state");
if (!IsString(timerIdObj) || !IsNumber(stateObj)) {
continue;
}
if (timerIdObj->valuestring == std::to_string(timerId) && stateObj->valueint == 1) {
cJSON_ReplaceItemInObject(obj, "state", cJSON_CreateNumber(0));
SaveJson(db);
break;
}
}
cJSON_Delete(db);
return true;
}
bool CjsonHelper::Delete(std::string tableName, int64_t timerId)
{
std::lock_guard<std::mutex> lock(mutex_);
cJSON* db = nullptr;
cJSON* table = nullptr;
if (!LoadAndParseJsonFile(tableName, db, table)) {
return false;
}
int size = cJSON_GetArraySize(table);
for (int i = 0; i < size; ++i) {
cJSON* obj = cJSON_GetArrayItem(table, i);
auto timerIdObj = cJSON_GetObjectItem(obj, "timerId");
if (!IsString(timerIdObj)) {
continue;
}
if (timerIdObj->valuestring == std::to_string(timerId)) {
cJSON_DeleteItemFromArray(table, i);
SaveJson(db);
break;
}
}
cJSON_Delete(db);
return true;
}
bool CjsonHelper::ClearInvaildDataInHoldOnReboot()
{
std::lock_guard<std::mutex> lock(mutex_);
std::ifstream file(DB_PATH);
if (!file.good()) {
TIME_HILOGE(TIME_MODULE_SERVICE, "open json file fail!");
return false;
}
std::string fileContent((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
cJSON* db = cJSON_Parse(fileContent.c_str());
cJSON* table = cJSON_GetObjectItem(db, HOLD_ON_REBOOT);
if (table == NULL) {
TIME_HILOGE(TIME_MODULE_SERVICE, "HOLD_ON_REBOOT get fail!");
cJSON_Delete(db);
return false;
}
int size = cJSON_GetArraySize(table);
for (int i = size - 1; i >= 0; --i) {
cJSON* obj = cJSON_GetArrayItem(table, i);
auto stateObj = cJSON_GetObjectItem(obj, "state");
auto typeObj = cJSON_GetObjectItem(obj, "type");
if (!IsNumber(stateObj) || !IsNumber(typeObj)) {
continue;
}
if (stateObj->valueint == 0
|| typeObj->valueint == ITimerManager::ELAPSED_REALTIME_WAKEUP
|| typeObj->valueint == ITimerManager::ELAPSED_REALTIME) {
cJSON_DeleteItemFromArray(table, i);
}
}
SaveJson(db);
cJSON_Delete(db);
return true;
}
void CjsonHelper::Clear(std::string tableName)
{
std::lock_guard<std::mutex> lock(mutex_);
std::ifstream file(DB_PATH);
if (!file.good()) {
TIME_HILOGE(TIME_MODULE_SERVICE, "open json file fail!");
return;
}
std::string fileContent((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
cJSON* db = cJSON_Parse(fileContent.c_str());
cJSON* table = cJSON_GetObjectItem(db, tableName.c_str());
if (table == NULL) {
TIME_HILOGE(TIME_MODULE_SERVICE, "%{public}s get json fail!", tableName.c_str());
cJSON_Delete(db);
return;
}
int size = cJSON_GetArraySize(table);
for (int i = size - 1; i >= 0; --i) {
cJSON_DeleteItemFromArray(table, i);
}
SaveJson(db);
cJSON_Delete(db);
}
void CjsonHelper::SaveJson(cJSON* data)
{
std::ofstream outFile(DB_PATH);
char* jsonString = cJSON_Print(data);
if (jsonString != nullptr) {
outFile << jsonString;
cJSON_free(jsonString);
}
outFile.close();
}
TimerDbSizeInfo CjsonHelper::GetDatabaseSizeDetail()
{
TimerDbSizeInfo sizeInfo;
struct stat st;
if (stat(DB_PATH, &st) == 0) {
sizeInfo.dbSize = st.st_size;
}
return sizeInfo;
}
int32_t CjsonHelper::GetTotalRecordCount()
{
int32_t totalCount = 0;
cJSON* db = nullptr;
cJSON* holdData = QueryTable(HOLD_ON_REBOOT, &db);
if (holdData != nullptr) {
totalCount += cJSON_GetArraySize(holdData);
}
cJSON_Delete(db);
db = nullptr;
cJSON* dropData = QueryTable(DROP_ON_REBOOT, &db);
if (dropData != nullptr) {
totalCount += cJSON_GetArraySize(dropData);
}
cJSON_Delete(db);
return totalCount;
}
void CjsonHelper::CountTimersFromTable(const std::string &tableName,
std::map<std::pair<std::string, std::string>, int32_t> &countMap)
{
cJSON* db = nullptr;
cJSON* tableData = QueryTable(tableName, &db);
if (tableData == nullptr) {
cJSON_Delete(db);
return;
}
int32_t size = cJSON_GetArraySize(tableData);
for (int32_t i = 0; i < size; ++i) {
cJSON* obj = cJSON_GetArrayItem(tableData, i);
cJSON* bundleItem = cJSON_GetObjectItem(obj, "bundleName");
cJSON* nameItem = cJSON_GetObjectItem(obj, "name");
if (bundleItem == nullptr || nameItem == nullptr) {
continue;
}
std::string bundleName = cJSON_IsString(bundleItem) ?
(bundleItem->valuestring ? bundleItem->valuestring : "") : "";
std::string timerName = cJSON_IsString(nameItem) ?
(nameItem->valuestring ? nameItem->valuestring : "") : "";
countMap[{bundleName, timerName}]++;
}
cJSON_Delete(db);
}
std::vector<TimerDbTopAppInfo> CjsonHelper::GetTopApps(int topN)
{
std::vector<TimerDbTopAppInfo> result;
if (topN <= 0) {
TIME_HILOGW(TIME_MODULE_SERVICE, "GetTopApps invalid topN:%{public}d", topN);
return result;
}
std::map<std::pair<std::string, std::string>, int32_t> countMap;
CountTimersFromTable(HOLD_ON_REBOOT, countMap);
CountTimersFromTable(DROP_ON_REBOOT, countMap);
for (const auto &entry : countMap) {
TimerDbTopAppInfo info;
info.bundleName = entry.first.first;
info.timerName = entry.first.second;
info.count = entry.second;
result.push_back(info);
}
std::sort(result.begin(), result.end(),
[](const TimerDbTopAppInfo &a, const TimerDbTopAppInfo &b) { return a.count > b.count; });
if (result.size() > static_cast<size_t>(topN)) {
result.resize(topN);
}
return result;
}
bool CjsonHelper::IsNumber(cJSON* item)
{
return (item != NULL && cJSON_IsNumber(item));
}
bool CjsonHelper::IsString(cJSON* item)
{
return (item != NULL && cJSON_IsString(item));
}
}
}