• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "util.h"
17 #include <fstream>
18 #include <regex>
19 
20 #include <sys/prctl.h>
21 #include <sys/stat.h>
22 #include <sys/syscall.h>
23 #include <unistd.h>
24 
25 #include "aggregator.h"
26 #include "error_multimodal.h"
27 #include "securec.h"
28 
29 #undef MMI_LOG_TAG
30 #define MMI_LOG_TAG "Util"
31 
32 namespace OHOS {
33 namespace MMI {
34 namespace {
35 constexpr int32_t FILE_SIZE_MAX { 0x6C445 };
36 constexpr int32_t MAX_PRO_FILE_SIZE { 128000 };
37 constexpr int32_t INVALID_FILE_SIZE { -1 };
38 constexpr int32_t MIN_INTERVALTIME { 36 };
39 constexpr int32_t MAX_INTERVALTIME { 100 };
40 constexpr int32_t MIN_DELAYTIME { 300 };
41 constexpr int32_t MAX_DELAYTIME { 1000 };
42 constexpr int32_t COMMENT_SUBSCRIPT { 0 };
43 const std::string CONFIG_ITEM_REPEAT = "Key.autorepeat";
44 const std::string CONFIG_ITEM_DELAY = "Key.autorepeat.delaytime";
45 const std::string CONFIG_ITEM_INTERVAL = "Key.autorepeat.intervaltime";
46 const std::string CONFIG_ITEM_TYPE = "Key.keyboard.type";
47 const std::string CURSORSTYLE_PATH = "/system/etc/multimodalinput/mouse_icon/";
48 const std::string DATA_PATH = "/data";
49 const std::string INPUT_PATH = "/system/";
50 const std::string SYS_PROD_PATH = "/sys_prod/";
51 const std::string KEY_PATH = "/vendor/etc/keymap/";
52 constexpr size_t BUF_TID_SIZE { 10 };
53 constexpr size_t BUF_CMD_SIZE { 512 };
54 constexpr size_t PROGRAM_NAME_SIZE { 256 };
55 constexpr int32_t TIME_CONVERSION_UNIT { 1000 };
56 constexpr int32_t COLOR_FIXEX_WIDTH { 6 };
57 const std::string COLOR_PREFIX = "#";
58 const char COLOR_FILL = '0';
59 constexpr int64_t TIME_ROUND_UP { 999 };
60 } // namespace
61 
GetSysClockTime()62 int64_t GetSysClockTime()
63 {
64     struct timespec ts = { 0, 0 };
65     if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) {
66         MMI_HILOGD("clock_gettime failed:%{public}d", errno);
67         return 0;
68     }
69 
70     // Prevent integer overflow
71     if (static_cast<uint64_t>(ts.tv_sec) > static_cast<uint64_t>(std::numeric_limits<int64_t>::max()) / (
72         TIME_CONVERSION_UNIT * TIME_CONVERSION_UNIT)) {
73         MMI_HILOGE("Integer overflow detected!");
74         return 0;
75     }
76 
77     uint64_t totalMicroSeconds = static_cast<uint64_t>(ts.tv_sec) * TIME_CONVERSION_UNIT *
78     TIME_CONVERSION_UNIT + static_cast<uint64_t>(ts.tv_nsec) / TIME_CONVERSION_UNIT;
79 
80     if (totalMicroSeconds > static_cast<uint64_t>(std::numeric_limits<int64_t>::max())) {
81         MMI_HILOGE("Total time value integer overflow detected!");
82         return 0;
83     }
84     return static_cast<int64_t>(totalMicroSeconds);
85 }
86 
GetMillisTime()87 int64_t GetMillisTime()
88 {
89     auto timeNow = std::chrono::time_point_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now());
90     auto tmp = std::chrono::duration_cast<std::chrono::milliseconds>(timeNow.time_since_epoch());
91     return tmp.count();
92 }
93 
GetTimeToMilli(int64_t timeDT)94 int64_t GetTimeToMilli(int64_t timeDT)
95 {
96     return (timeDT + TIME_ROUND_UP) / TIME_CONVERSION_UNIT;
97 }
98 
GetThisThreadIdOfString()99 static std::string GetThisThreadIdOfString()
100 {
101     thread_local std::string threadLocalId;
102     if (threadLocalId.empty()) {
103         long tid = syscall(SYS_gettid);
104         char buf[BUF_TID_SIZE] = {};
105         const int32_t ret = sprintf_s(buf, BUF_TID_SIZE, "%06d", tid);
106         if (ret < 0) {
107             printf("ERR: in %s, #%d, call sprintf_s failed, ret = %d", __func__, __LINE__, ret);
108             return threadLocalId;
109         }
110         buf[BUF_TID_SIZE - 1] = '\0';
111         threadLocalId = buf;
112     }
113 
114     return threadLocalId;
115 }
116 
GetThisThreadId()117 uint64_t GetThisThreadId()
118 {
119     std::string stid = GetThisThreadIdOfString();
120     auto tid = std::atoll(stid.c_str());
121     return tid;
122 }
123 
StringToken(std::string & str,const std::string & sep,std::string & token)124 static size_t StringToken(std::string &str, const std::string &sep, std::string &token)
125 {
126     token = "";
127     if (str.empty()) {
128         return str.npos;
129     }
130     size_t pos = str.npos;
131     size_t tmp = 0;
132     for (auto &item : sep) {
133         tmp = str.find(item);
134         if (str.npos != tmp) {
135             pos = (std::min)(pos, tmp);
136         }
137     }
138     if (str.npos != pos) {
139         token = str.substr(0, pos);
140         if (str.npos != pos + 1) {
141             str = str.substr(pos + 1, str.npos);
142         }
143         if (pos == 0) {
144             return StringToken(str, sep, token);
145         }
146     } else {
147         token = str;
148         str = "";
149     }
150     return token.size();
151 }
152 
StringSplit(const std::string & str,const std::string & sep,std::vector<std::string> & vecList)153 size_t StringSplit(const std::string &str, const std::string &sep, std::vector<std::string> &vecList)
154 {
155     size_t size;
156     auto strs = str;
157     std::string token;
158     while (str.npos != (size = StringToken(strs, sep, token))) {
159         vecList.push_back(token);
160     }
161     return vecList.size();
162 }
163 
IdsListToString(const std::vector<int32_t> & list,const std::string & sep)164 std::string IdsListToString(const std::vector<int32_t> &list, const std::string &sep)
165 {
166     std::string str;
167     for (const auto &it : list) {
168         str += std::to_string(it) + sep;
169     }
170     if (str.size() > 0) {
171         str.resize(str.size() - sep.size());
172     }
173     return str;
174 }
175 
GetPid()176 int32_t GetPid()
177 {
178     return static_cast<int32_t>(getpid());
179 }
180 
GetFileName(const std::string & strPath)181 static std::string GetFileName(const std::string &strPath)
182 {
183     size_t nPos = strPath.find_last_of('/');
184     if (strPath.npos == nPos) {
185         nPos = strPath.find_last_of('\\');
186     }
187     if (strPath.npos == nPos) {
188         return strPath;
189     }
190 
191     return strPath.substr(nPos + 1, strPath.npos);
192 }
193 
GetProgramName()194 const char *GetProgramName()
195 {
196     static char programName[PROGRAM_NAME_SIZE] = {};
197     if (programName[0] != '\0') {
198         return programName;
199     }
200 
201     char buf[BUF_CMD_SIZE] = { 0 };
202     if (sprintf_s(buf, BUF_CMD_SIZE, "/proc/%d/cmdline", static_cast<int32_t>(getpid())) == -1) {
203         KMSG_LOGE("GetProcessInfo sprintf_s /proc/.../cmdline error");
204         return "";
205     }
206     FILE *fp = fopen(buf, "rb");
207     CHKPS(fp);
208     static constexpr size_t bufLineSize = 512;
209     char bufLine[bufLineSize] = { 0 };
210     if ((fgets(bufLine, bufLineSize, fp) == nullptr)) {
211         KMSG_LOGE("fgets failed");
212         if (fclose(fp) != 0) {
213             KMSG_LOGW("Close file:%s failed", buf);
214         }
215         fp = nullptr;
216         return "";
217     }
218     if (fclose(fp) != 0) {
219         KMSG_LOGW("Close file:%s failed", buf);
220     }
221     fp = nullptr;
222 
223     std::string tempName(bufLine);
224     tempName = GetFileName(tempName);
225     if (tempName.empty()) {
226         KMSG_LOGE("tempName is empty");
227         return "";
228     }
229     const size_t copySize = std::min(tempName.size(), PROGRAM_NAME_SIZE - 1);
230     if (copySize == 0) {
231         KMSG_LOGE("The copySize is 0");
232         return "";
233     }
234     errno_t ret = memcpy_s(programName, PROGRAM_NAME_SIZE, tempName.c_str(), copySize);
235     if (ret != EOK) {
236         return "";
237     }
238     KMSG_LOGI("GetProgramName success. programName = %s", programName);
239 
240     return programName;
241 }
242 
SetThreadName(const std::string & name)243 void SetThreadName(const std::string &name)
244 {
245     prctl(PR_SET_NAME, name.c_str());
246 }
247 
IsFileExists(const std::string & fileName)248 static bool IsFileExists(const std::string &fileName)
249 {
250     return (access(fileName.c_str(), F_OK) == 0);
251 }
252 
CheckFileExtendName(const std::string & filePath,const std::string & checkExtension)253 static bool CheckFileExtendName(const std::string &filePath, const std::string &checkExtension)
254 {
255     std::string::size_type pos = filePath.find_last_of('.');
256     if (pos == std::string::npos) {
257         MMI_HILOGE("File is not find extension");
258         return false;
259     }
260     return (filePath.substr(pos + 1, filePath.npos) == checkExtension);
261 }
262 
GetFileSize(const std::string & filePath)263 static int32_t GetFileSize(const std::string &filePath)
264 {
265     struct stat statbuf = { 0 };
266     if (stat(filePath.c_str(), &statbuf) != 0) {
267         MMI_HILOGE("Get file size error");
268         return INVALID_FILE_SIZE;
269     }
270     return statbuf.st_size;
271 }
272 
ReadFile(const std::string & filePath)273 static std::string ReadFile(const std::string &filePath)
274 {
275     FILE *fp = fopen(filePath.c_str(), "r");
276     CHKPS(fp);
277     std::string dataStr;
278     char buf[256] = {};
279     while (fgets(buf, sizeof(buf), fp) != nullptr) {
280         dataStr += buf;
281     }
282     if (fclose(fp) != 0) {
283         MMI_HILOGW("Close file failed");
284     }
285     return dataStr;
286 }
287 
IsValidPath(const std::string & rootDir,const std::string & filePath)288 static bool IsValidPath(const std::string &rootDir, const std::string &filePath)
289 {
290     return (filePath.compare(0, rootDir.size(), rootDir) == 0);
291 }
292 
IsValidJsonPath(const std::string & filePath)293 bool IsValidJsonPath(const std::string &filePath)
294 {
295     return (IsValidPath(DATA_PATH, filePath) ||
296             IsValidPath(INPUT_PATH, filePath) ||
297             IsValidPath(SYS_PROD_PATH, filePath));
298 }
299 
IsValidProPath(const std::string & filePath)300 static bool IsValidProPath(const std::string &filePath)
301 {
302     return IsValidPath(KEY_PATH, filePath);
303 }
304 
IsValidTomlPath(const std::string & filePath)305 static bool IsValidTomlPath(const std::string &filePath)
306 {
307     return IsValidPath(KEY_PATH, filePath);
308 }
309 
ReadProFile(const std::string & filePath,int32_t deviceId,std::map<int32_t,std::map<int32_t,int32_t>> & configMap)310 void ReadProFile(const std::string &filePath, int32_t deviceId,
311     std::map<int32_t, std::map<int32_t, int32_t>> &configMap)
312 {
313     CALL_DEBUG_ENTER;
314     if (filePath.empty()) {
315         MMI_HILOGE("FilePath is empty");
316         return;
317     }
318     char realPath[PATH_MAX] = {};
319     CHKPV(realpath(filePath.c_str(), realPath));
320     if (!IsValidProPath(realPath)) {
321         MMI_HILOGE("File path is error");
322         return;
323     }
324     if (!IsFileExists(realPath)) {
325         MMI_HILOGE("File is not existent");
326         return;
327     }
328     if (!CheckFileExtendName(realPath, "pro")) {
329         MMI_HILOGE("Unable to parse files other than json format");
330         return;
331     }
332     auto fileSize = GetFileSize(realPath);
333     if ((fileSize == INVALID_FILE_SIZE) || (fileSize >= MAX_PRO_FILE_SIZE)) {
334         MMI_HILOGE("The configuration file size is incorrect");
335         return;
336     }
337     ReadProConfigFile(realPath, deviceId, configMap);
338 }
339 
IsNum(const std::string & str)340 static inline bool IsNum(const std::string &str)
341 {
342     std::istringstream sin(str);
343     double num;
344     return (sin >> num) && sin.eof();
345 }
346 
ReadProConfigFile(const std::string & realPath,int32_t deviceId,std::map<int32_t,std::map<int32_t,int32_t>> & configKey)347 void ReadProConfigFile(const std::string &realPath, int32_t deviceId,
348     std::map<int32_t, std::map<int32_t, int32_t>> &configKey)
349 {
350     CALL_DEBUG_ENTER;
351     std::ifstream reader(realPath);
352     if (!reader.is_open()) {
353         MMI_HILOGE("Failed to open config file");
354         return;
355     }
356     std::string strLine;
357     int32_t sysKeyValue;
358     int32_t nativeKeyValue;
359     int32_t elementKey = 0;
360     int32_t elementValue = 0;
361     std::map<int32_t, int32_t> tmpConfigKey;
362     while (std::getline(reader, strLine)) {
363         const char* line = strLine.c_str();
364         int32_t len = strlen(line);
365         char* realLine = static_cast<char*>(malloc(len + 1));
366         CHKPV(realLine);
367         if (strcpy_s(realLine, len + 1, line) != EOK) {
368             MMI_HILOGE("strcpy_s error");
369             free(realLine);
370             realLine = nullptr;
371             return;
372         }
373         *(realLine + len + 1) = '\0';
374         int32_t ret = ReadConfigInfo(realLine, len, &elementKey, &elementValue);
375         free(realLine);
376         realLine = nullptr;
377         if (ret != RET_OK) {
378             MMI_HILOGE("Failed to read from line of config info");
379             reader.close();
380             return;
381         }
382         nativeKeyValue = elementKey;
383         sysKeyValue = elementValue;
384         MMI_HILOGD("The nativeKeyValue is:%{public}d, sysKeyValue is:%{public}d", nativeKeyValue, sysKeyValue);
385         tmpConfigKey.insert(std::pair<int32_t, int32_t>(nativeKeyValue, sysKeyValue));
386     }
387     reader.close();
388     auto iter = configKey.insert(std::make_pair(deviceId, tmpConfigKey));
389     if (!iter.second) {
390         MMI_HILOGE("The file name is duplicated");
391         return;
392     }
393 }
394 
ReadJsonFile(const std::string & filePath)395 std::string ReadJsonFile(const std::string &filePath)
396 {
397     if (filePath.empty()) {
398         MMI_HILOGE("FilePath is empty");
399         return "";
400     }
401     char realPath[PATH_MAX] = {};
402     CHKPS(realpath(filePath.c_str(), realPath));
403     if (!IsValidJsonPath(realPath)) {
404         MMI_HILOGE("File path is error");
405         return "";
406     }
407     if (!CheckFileExtendName(realPath, "json")) {
408         MMI_HILOGE("Unable to parse files other than json format");
409         return "";
410     }
411     if (!IsFileExists(realPath)) {
412         MMI_HILOGE("File is not existent");
413         return "";
414     }
415     int32_t fileSize = GetFileSize(realPath);
416     if ((fileSize <= 0) || (fileSize > FILE_SIZE_MAX)) {
417         MMI_HILOGE("File size out of read range");
418         return "";
419     }
420     return ReadFile(filePath);
421 }
422 
ConfigItemSwitch(const std::string & configItem,const std::string & value,DeviceConfig & devConf)423 static int32_t ConfigItemSwitch(const std::string &configItem, const std::string &value, DeviceConfig &devConf)
424 {
425     CALL_DEBUG_ENTER;
426     if (configItem.empty() || value.empty()) {
427         MMI_HILOGE("Get key config item is invalid");
428         return RET_ERR;
429     }
430     if (!IsNum(value)) {
431         MMI_HILOGE("Get key config item is invalid");
432         return RET_ERR;
433     }
434     if (configItem == CONFIG_ITEM_REPEAT) {
435         devConf.autoSwitch = atoi(value.c_str());
436     } else if (configItem == CONFIG_ITEM_DELAY) {
437         devConf.delayTime = atoi(value.c_str());
438         if (devConf.delayTime < MIN_DELAYTIME || devConf.delayTime > MAX_DELAYTIME) {
439             MMI_HILOGE("Unusual the delaytime");
440             return RET_ERR;
441         }
442     } else if (configItem == CONFIG_ITEM_INTERVAL) {
443         devConf.intervalTime = atoi(value.c_str());
444         if (devConf.intervalTime < MIN_INTERVALTIME || devConf.intervalTime > MAX_INTERVALTIME) {
445             MMI_HILOGE("Unusual the intervaltime");
446             return RET_ERR;
447         }
448     } else if (configItem == CONFIG_ITEM_TYPE) {
449         devConf.keyboardType = atoi(value.c_str());
450     }
451     return RET_OK;
452 }
453 
ReadConfigFile(const std::string & realPath,DeviceConfig & devConf)454 static int32_t ReadConfigFile(const std::string &realPath, DeviceConfig &devConf)
455 {
456     CALL_DEBUG_ENTER;
457     std::ifstream cfgFile(realPath);
458     if (!cfgFile.is_open()) {
459         MMI_HILOGE("Failed to open config file");
460         return FILE_OPEN_FAIL;
461     }
462     std::string tmp;
463     while (std::getline(cfgFile, tmp)) {
464         RemoveSpace(tmp);
465         size_t pos = tmp.find('#');
466         if (pos != tmp.npos && pos != COMMENT_SUBSCRIPT) {
467             MMI_HILOGE("File format is error");
468             cfgFile.close();
469             return RET_ERR;
470         }
471         if (tmp.empty() || tmp.front() == '#') {
472             continue;
473         }
474         pos = tmp.find('=');
475         if ((pos == std::string::npos) || (tmp.back() == '=')) {
476             MMI_HILOGE("Find config item error");
477             cfgFile.close();
478             return RET_ERR;
479         }
480         std::string configItem = tmp.substr(0, pos);
481         std::string value = tmp.substr(pos + 1);
482         if (ConfigItemSwitch(configItem, value, devConf) == RET_ERR) {
483             MMI_HILOGE("Configuration item error");
484             cfgFile.close();
485             return RET_ERR;
486         }
487     }
488     cfgFile.close();
489     return RET_OK;
490 }
491 
ReadTomlFile(const std::string & filePath,DeviceConfig & devConf)492 int32_t ReadTomlFile(const std::string &filePath, DeviceConfig &devConf)
493 {
494     if (filePath.empty()) {
495         MMI_HILOGE("FilePath is empty");
496         return RET_ERR;
497     }
498     char realPath[PATH_MAX] = {};
499     CHKPR(realpath(filePath.c_str(), realPath), RET_ERR);
500     if (!IsValidTomlPath(realPath)) {
501         MMI_HILOGE("File path is error");
502         return RET_ERR;
503     }
504     if (!IsFileExists(realPath)) {
505         MMI_HILOGE("File is not existent");
506         return RET_ERR;
507     }
508     if (!CheckFileExtendName(realPath, "TOML")) {
509         MMI_HILOGE("Unable to parse files other than json format");
510         return RET_ERR;
511     }
512     int32_t fileSize = GetFileSize(realPath);
513     if ((fileSize <= 0) || (fileSize > FILE_SIZE_MAX)) {
514         MMI_HILOGE("File size out of read range");
515         return RET_ERR;
516     }
517     if (ReadConfigFile(realPath, devConf) == RET_ERR) {
518         MMI_HILOGE("Read device config file failed");
519         return RET_ERR;
520     }
521     return RET_OK;
522 }
523 
ReadCursorStyleFile(const std::string & filePath)524 int32_t ReadCursorStyleFile(const std::string &filePath)
525 {
526     CALL_DEBUG_ENTER;
527     if (filePath.empty()) {
528         MMI_HILOGE("FilePath is empty");
529         return RET_ERR;
530     }
531     if (!IsFileExists(filePath)) {
532         MMI_HILOGE("File is not existent");
533         return RET_ERR;
534     }
535     char realPath[PATH_MAX] = {};
536     CHKPR(realpath(filePath.c_str(), realPath), RET_ERR);
537     int32_t fileSize = GetFileSize(realPath);
538     if ((fileSize <= 0) || (fileSize > FILE_SIZE_MAX)) {
539         MMI_HILOGE("File size out of read range");
540         return RET_ERR;
541     }
542     return RET_OK;
543 }
544 
StringPrintf(const char * format,...)545 std::string StringPrintf(const char *format, ...)
546 {
547     char space[1024];
548 
549     va_list ap;
550     va_start(ap, format);
551     std::string result;
552     int32_t ret = vsnprintf_s(space, sizeof(space), sizeof(space) - 1, format, ap);
553     if (ret >= RET_OK && (size_t)ret < sizeof(space)) {
554         result = space;
555     } else {
556         MMI_HILOGE("The buffer is overflow");
557     }
558     va_end(ap);
559     return result;
560 }
561 
FileVerification(std::string & filePath,const std::string & checkExtension)562 std::string FileVerification(std::string &filePath, const std::string &checkExtension)
563 {
564     if (filePath.empty()) {
565         MMI_HILOGE("FilePath is empty");
566         return "";
567     }
568     char realPath[PATH_MAX] = {};
569     CHKPS(realpath(filePath.c_str(), realPath));
570     if (!IsFileExists(realPath)) {
571         MMI_HILOGE("File is not existent");
572         return "";
573     }
574     if (!CheckFileExtendName(realPath, checkExtension)) {
575         MMI_HILOGE("Unable to parse files other than json format");
576         return "";
577     }
578     int32_t fileSize = GetFileSize(realPath);
579     if ((fileSize <= 0) || (fileSize > FILE_SIZE_MAX)) {
580         MMI_HILOGE("File size out of read range");
581         return "";
582     }
583     return realPath;
584 }
585 
ReadFile(const std::string & path,std::string & content)586 bool ReadFile(const std::string &path, std::string &content)
587 {
588     if (path.empty()) {
589         MMI_HILOGE("path is empty");
590         return false;
591     }
592     char realPath[PATH_MAX] = {};
593     CHKPF(realpath(path.c_str(), realPath));
594     std::ifstream file(realPath);
595     if (!file) {
596         return false;
597     }
598     std::stringstream buffer;
599     buffer << file.rdbuf();
600     content = buffer.str();
601     return true;
602 }
603 
IntToHexRGB(int32_t color)604 std::string IntToHexRGB(int32_t color)
605 {
606     std::ostringstream oss;
607     oss << COLOR_PREFIX << std::setfill(COLOR_FILL) << std::setw(COLOR_FIXEX_WIDTH) << std::hex << std::uppercase
608         << color;
609     return oss.str();
610 }
611 
StringReplace(std::string & str,const std::string & oldStr,const std::string & newStr)612 void StringReplace(std::string &str, const std::string &oldStr, const std::string &newStr)
613 {
614     std::regex re(oldStr);
615     str = std::regex_replace(str, re, newStr);
616 }
617 
IsInteger(const char * target)618 bool IsInteger(const char *target)
619 {
620     CHKPF(target);
621     std::regex pattern("^\\s*-?(0|([1-9]\\d*))\\s*$");
622     return std::regex_match(target, pattern);
623 }
624 
Record(const LogHeader & lh,const std::string & key,const std::string & record)625 bool Aggregator::Record(const LogHeader &lh, const std::string &key, const std::string &record)
626 {
627     constexpr int32_t oneSecond = 1000;
628     if (timerId_ != -1) {
629         resetTimer_(timerId_);
630     } else {
631         timerId_ = addTimer_(oneSecond, 1, [this, lh]() {
632             FlushRecords(lh);
633             timerId_ = -1;
634         });
635     }
636     if (key == key_) {
637         auto now = std::chrono::system_clock::now();
638         records_.push_back({record, now});
639         if (records_.size() >= maxRecordCount_) {
640             FlushRecords(lh);
641         }
642         return true;
643     } else {
644         FlushRecords(lh, key, record);
645         key_ = key;
646         return false;
647     }
648 }
649 
FlushRecords(const LogHeader & lh,const std::string & key,const std::string & extraRecord)650 void Aggregator::FlushRecords(const LogHeader &lh, const std::string &key, const std::string &extraRecord)
651 {
652     constexpr uint32_t milliSecondWidth = 3;
653     constexpr uint32_t microToMilli = 1000;
654     size_t recordCount = records_.size();
655     std::ostringstream oss;
656     if (!records_.empty()) {
657         oss << key_;
658         oss << ", first: " << records_.front().record << "-(";
659         auto firstTime = records_.front().timestamp;
660         time_t firstTimeT = std::chrono::system_clock::to_time_t(firstTime);
661         std::tm *bt = std::localtime(&firstTimeT);
662         if (bt == nullptr) {
663             MMI_HILOGE("The bt is nullptr, this is a invalid time");
664             return;
665         }
666         oss << std::put_time(bt, "%Y-%m-%d %H:%M:%S");
667         auto since_epoch = firstTime.time_since_epoch();
668         auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(since_epoch).count() % microToMilli;
669         oss << '.' << std::setfill('0') << std::setw(milliSecondWidth) << millis << "ms)";
670 
671         if (records_.size() > 1) {
672             size_t i = records_.size() - 1;
673             const auto &recordInfo = records_[i];
674             oss << ", " << recordInfo.record;
675         }
676         records_.clear();
677         oss << ", count: " << recordCount;
678     }
679     if (!extraRecord.empty()) {
680         if (!oss.str().empty()) {
681             oss << ", last: ";
682         }
683         oss << key << ": " << extraRecord;
684     }
685     if (!oss.str().empty()) {
686         MMI_HILOG_HEADER(LOG_INFO, lh, "%{public}s", oss.str().c_str());
687     }
688 }
689 
~Aggregator()690 Aggregator::~Aggregator()
691 {
692     FlushRecords(MMI_LOG_HEADER);
693 }
694 } // namespace MMI
695 } // namespace OHOS
696