• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2 * Copyright (c) 2025 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 <iostream>
17 #include <fstream>
18 #include <sstream>
19 #include <algorithm>
20 #include <cstdlib>
21 #include <climits>
22 #include <unordered_map>
23 #include <functional>
24 #include <mutex>
25 #include "app_event.h"
26 #include "app_event_processor_mgr.h"
27 #include "hilog_wrapper.h"
28 #include "api_event_reporter.h"
29 #include "uuid.h"
30 #include <sys/stat.h>
31 
32 namespace OHOS {
33 namespace Accessibility {
34 // Global variables for caching file content and mutex
35 std::string ApiEventReporter::g_fileContent = "";
36 int64_t ApiEventReporter::g_processorId = -1;
37 std::mutex ApiEventReporter::g_apiOperationMutex;
38 constexpr size_t UUID_CHAR_ARRAY_LENGTH = 37;
39 
ApiEventReporter()40 ApiEventReporter::ApiEventReporter()
41 {
42 }
43 
~ApiEventReporter()44 ApiEventReporter::~ApiEventReporter()
45 {
46     m_thresholdData.clear();
47 }
48 
IsAppEventProccessorValid()49 bool ApiEventReporter::IsAppEventProccessorValid()
50 {
51     if (g_processorId <= NULLPTR_PROCCESSORID) {
52         g_processorId = AddProcessor();
53     }
54     if (g_processorId <= NULLPTR_PROCCESSORID) {
55         return false;
56     }
57     return true;
58 }
59 
LoadConfigurationFile(const std::string & configFile)60 bool ApiEventReporter::LoadConfigurationFile(const std::string &configFile)
61 {
62     if (!IsValidPath(configFile)) {
63         return false;
64     }
65     std::ifstream file(configFile);
66     if (!file.is_open()) {
67         HILOG_ERROR("Unable to open api operation config file!");
68         return false;
69     }
70 
71     std::ostringstream oss;
72     std::string line;
73     while (std::getline(file, line)) {
74         line = Trim(line);
75         if (!line.empty() && line[0] != '#') {
76             oss << line << "\n";
77         }
78     }
79     g_fileContent = oss.str();
80     file.close();
81     return true;
82 }
83 
GetConfigurationParams(ApiReportConfig & reportConfig,ApiEventConfig & eventConfig)84 void ApiEventReporter::GetConfigurationParams(ApiReportConfig &reportConfig, ApiEventConfig &eventConfig)
85 {
86     std::istringstream stream(g_fileContent);
87     ParseApiOperationManagement(stream, reportConfig, eventConfig);
88 }
89 
Trim(const std::string & str)90 std::string ApiEventReporter::Trim(const std::string &str)
91 {
92     const char* whitespace = " \t\n\r";
93     size_t first = str.find_first_not_of(whitespace);
94     size_t last = str.find_last_not_of(whitespace);
95     return (first == std::string::npos) ? "" : str.substr(first, last - first + 1);
96 }
97 
ParseKeyValue(const std::string & line)98 std::pair<std::string, std::string> ApiEventReporter::ParseKeyValue(const std::string &line)
99 {
100     size_t colonPos = line.find(':');
101     if (colonPos != std::string::npos) {
102         std::string key = Trim(line.substr(0, colonPos));
103         std::string value = Trim(line.substr(colonPos + 1));
104         key.erase(std::remove(key.begin(), key.end(), '"'), key.end());
105         value.erase(std::remove(value.begin(), value.end(), '"'), value.end());
106         if (!value.empty() && value.back() == ',') {
107             value.pop_back(); // Remove trailing comma if present
108         }
109         return std::make_pair(key, value);
110     }
111     return std::make_pair("", "");
112 }
113 
TryParseInt(const std::string & str,int & out)114 bool ApiEventReporter::TryParseInt(const std::string &str, int &out)
115 {
116     char* end;
117     long val = strtol(str.c_str(), &end, 10);
118     if (*end == '\0' && end != str.c_str() && val >= INT_MIN && val <= INT_MAX) {
119         out = static_cast<int>(val);
120         return true;
121     } else {
122         HILOG_ERROR("Invalid integer: %{public}s", str.c_str());
123         return false;
124     }
125 }
126 
ParseReportConfig(std::istringstream & stream,ApiReportConfig & reportConfig)127 void ApiEventReporter::ParseReportConfig(std::istringstream &stream, ApiReportConfig &reportConfig)
128 {
129     std::unordered_map<std::string, std::function<void(const std::string&)>> configMap = {
130         {"config_name", [&](const std::string &value) { reportConfig.config_name = value; }},
131         {"config_appId", [&](const std::string &value) { reportConfig.config_appId = value; }},
132         {"config_routeInfo", [&](const std::string &value) { reportConfig.config_routeInfo = value; }},
133         {"config_TriggerCond.timeout", [&](const std::string &value) {
134             int temp;
135             if (TryParseInt(value, temp)) {
136                 reportConfig.config_timeout = temp;
137             } else {
138                 HILOG_ERROR("Invalid integer for config_timeout: %{public}s", value.c_str());
139             }
140         }},
141         {"config_TriggerCond.row", [&](const std::string &value) {
142             int temp;
143             if (TryParseInt(value, temp)) {
144                 reportConfig.config_row = temp;
145             } else {
146                 HILOG_ERROR("Invalid integer for config_row: %{public}s", value.c_str());
147             }
148         }}
149     };
150 
151     std::string line;
152     while (std::getline(stream, line)) {
153         line = Trim(line);
154         if (line == "},") {
155             break;
156         }
157         auto keyValue = ParseKeyValue(line);
158         auto it = configMap.find(keyValue.first);
159         if (it != configMap.end()) {
160             it->second(keyValue.second);
161         }
162     }
163 }
164 
ParseEvent(std::istringstream & stream,ApiEvent & event)165 void ApiEventReporter::ParseEvent(std::istringstream &stream, ApiEvent &event)
166 {
167     std::unordered_map<std::string, std::function<void(const std::string&)>> eventMap = {
168         {"domain", [&](const std::string &value) { event.domain = value; }},
169         {"name", [&](const std::string &value) { event.name = value; }},
170         {"isRealTime", [&](const std::string &value) { event.isRealTime = (value == "true"); }}
171     };
172 
173     std::string line;
174     while (std::getline(stream, line)) {
175         line = Trim(line);
176         if (line == "},") {
177             break;
178         }
179         auto keyValue = ParseKeyValue(line);
180         auto it = eventMap.find(keyValue.first);
181         if (it != eventMap.end()) {
182             it->second(keyValue.second);
183         }
184     }
185 }
186 
ParseEventConfig(std::istringstream & stream,ApiEventConfig & eventConfig)187 void ApiEventReporter::ParseEventConfig(std::istringstream &stream, ApiEventConfig &eventConfig)
188 {
189     std::unordered_map<std::string, std::function<void(std::istringstream&)>> eventConfigMap = {
190         {"\"event1\": {", [&](std::istringstream &stream) { ParseEvent(stream, eventConfig.event1); }},
191         {"\"event2\": {", [&](std::istringstream &stream) { ParseEvent(stream, eventConfig.event2); }},
192         {"\"event3\": {", [&](std::istringstream &stream) { ParseEvent(stream, eventConfig.event3); }}
193     };
194 
195     std::string line;
196     while (std::getline(stream, line)) {
197         line = Trim(line);
198         if (line == "},") {
199             break;
200         }
201         auto it = eventConfigMap.find(line);
202         if (it != eventConfigMap.end()) {
203             it->second(stream);
204         }
205     }
206 }
207 
ParseApiOperationManagement(std::istringstream & stream,ApiReportConfig & reportConfig,ApiEventConfig & eventConfig)208 void ApiEventReporter::ParseApiOperationManagement(std::istringstream &stream, ApiReportConfig &reportConfig,
209     ApiEventConfig &eventConfig)
210 {
211     std::unordered_map<std::string, std::function<void(std::istringstream&)>> apiOpMgmtMap = {
212         {"\"report_config\": {", [&](std::istringstream &stream) { ParseReportConfig(stream, reportConfig); }},
213         {"\"event_config\": {", [&](std::istringstream &stream) { ParseEventConfig(stream, eventConfig); }}
214     };
215 
216     std::string line;
217     while (std::getline(stream, line)) {
218         line = Trim(line);
219         if (line == "}") {
220             break;
221         }
222         auto it = apiOpMgmtMap.find(line);
223         if (it != apiOpMgmtMap.end()) {
224             it->second(stream);
225         }
226     }
227 }
228 
AddProcessor()229 int64_t ApiEventReporter::AddProcessor()
230 {
231     HILOG_INFO("AddProcessor enter.");
232     std::lock_guard<std::mutex> lock(g_apiOperationMutex);
233     ApiReportConfig reportConfig;
234     ApiEventConfig eventConfig;
235     if (g_fileContent.empty()) {
236         if (LoadConfigurationFile(ACCESSIBILITY_API_OPERATION_CONFIG_PATH) != true) {
237             HILOG_ERROR("AddProcessor LoadConfigurationFile error!");
238             return -1;
239         }
240     }
241     GetConfigurationParams(reportConfig, eventConfig);
242     HiviewDFX::HiAppEvent::ReportConfig config;
243     config.name = reportConfig.config_name;
244     config.appId = reportConfig.config_appId;
245     config.routeInfo = reportConfig.config_routeInfo;
246     config.triggerCond.timeout = reportConfig.config_timeout;
247     config.triggerCond.row = reportConfig.config_row;
248     config.eventConfigs.clear();
249     HiviewDFX::HiAppEvent::EventConfig event1;
250     event1.domain = eventConfig.event1.domain;
251     event1.name = eventConfig.event1.name;
252     event1.isRealTime = eventConfig.event1.isRealTime;
253     config.eventConfigs.push_back(event1);
254     HiviewDFX::HiAppEvent::EventConfig event2;
255     event2.domain = eventConfig.event2.domain;
256     event2.name = eventConfig.event2.name;
257     event2.isRealTime = eventConfig.event2.isRealTime;
258     config.eventConfigs.push_back(event2);
259     HiviewDFX::HiAppEvent::EventConfig event3;
260     event3.domain = eventConfig.event3.domain;
261     event3.name = eventConfig.event3.name;
262     event3.isRealTime = eventConfig.event3.isRealTime;
263     config.eventConfigs.push_back(event3);
264     g_processorId = HiviewDFX::HiAppEvent::AppEventProcessorMgr::AddProcessor(config);
265     return g_processorId;
266 }
267 
RandomUuid()268 std::string RandomUuid()
269 {
270     uuid_t uuid;
271     uuid_generate_random(uuid);
272     char uuidChars[UUID_CHAR_ARRAY_LENGTH] = {'\0'};
273     uuid_unparse(uuid, uuidChars);
274     return std::string(uuidChars);
275 }
276 
WriteEndEvent(int result,int errCode,std::string apiName,int64_t beginTime)277 void ApiEventReporter::WriteEndEvent(int result, int errCode, std::string apiName, int64_t beginTime)
278 {
279     HILOG_DEBUG("WriteEndEvent enter.");
280     std::string transId = std::string("traceId_") + RandomUuid();
281     int64_t endTime = GetCurrentTime();
282 
283     HiviewDFX::HiAppEvent::Event event("api_diagnostic", "api_exec_end", OHOS::HiviewDFX::HiAppEvent::BEHAVIOR);
284     event.AddParam("trans_id", transId);
285     event.AddParam("api_name", apiName);
286     event.AddParam("sdk_name", std::string("AccessibilityKit"));
287     event.AddParam("begin_time", beginTime);
288     event.AddParam("end_time", endTime);
289     event.AddParam("result", result);
290     event.AddParam("error_code", errCode);
291     if (!IsAppEventProccessorValid()) {
292         HILOG_ERROR("WriteEndEvent processorid invalid!");
293         return;
294     }
295     HiviewDFX::HiAppEvent::Write(event);
296 }
297 
ClearCacheData()298 void ApiEventReporter::ClearCacheData()
299 {
300     for (auto it : m_thresholdData) {
301         std::string apiName = it.first;
302         auto expandableData = it.second;
303         int32_t dataCount = static_cast<int32_t>(expandableData->runTime.size());
304         if (dataCount < 1) {
305             continue;
306         }
307         ExecuteThresholdWriteEndEvent(apiName, expandableData, dataCount);
308     }
309 }
310 
GetCurrentTime()311 int64_t ApiEventReporter::GetCurrentTime()
312 {
313     int64_t time = std::chrono::duration_cast<std::chrono::milliseconds>(
314         std::chrono::system_clock::now().time_since_epoch()).count();
315     return time;
316 }
317 
ThresholdWriteEndEvent(int result,std::string apiName,int64_t beginTime,int32_t thresholdValue)318 void ApiEventReporter::ThresholdWriteEndEvent(int result, std::string apiName, int64_t beginTime,
319     int32_t thresholdValue)
320 {
321     auto expandableData = CacheEventInfo(apiName, beginTime, result);
322     if (expandableData == nullptr) {
323         return;
324     }
325     int32_t dataCount = static_cast<int32_t>(expandableData->runTime.size());
326     if (dataCount < thresholdValue) {
327         return;
328     }
329     HILOG_INFO("ThresholdWriteEndEvent begin, apiname: %{public}s, dataCount: %{public}d", apiName.c_str(), dataCount);
330     ExecuteThresholdWriteEndEvent(apiName, expandableData, dataCount);
331 }
332 
CacheEventInfo(std::string apiName,int64_t beginTime,int result)333 std::shared_ptr<EventPeriodExpandableData> ApiEventReporter::CacheEventInfo(std::string apiName,
334     int64_t beginTime, int result)
335 {
336     std::lock_guard<std::mutex> lock(g_apiOperationMutex);
337     if (m_thresholdData.find(apiName) == m_thresholdData.end()) {
338         std::shared_ptr<EventPeriodExpandableData> expandableData = std::make_shared<EventPeriodExpandableData>();
339         if (expandableData == nullptr) {
340             HILOG_ERROR("ApiEventReporter CacheEventInfo expandableData make_shared failed");
341             return nullptr;
342         }
343         m_thresholdData.insert(std::pair<std::string, std::shared_ptr<EventPeriodExpandableData>>(apiName,
344             expandableData));
345     }
346     auto expandableData = m_thresholdData[apiName];
347     int64_t costTime = GetCurrentTime() - beginTime;
348     expandableData->runTime.push_back(costTime);
349     expandableData->sumTime += costTime;
350     if (result == 0) {
351         expandableData->successCount++;
352     }
353     return expandableData;
354 }
355 
ExecuteThresholdWriteEndEvent(std::string apiName,std::shared_ptr<EventPeriodExpandableData> expandableData,int32_t dataCount)356 void ApiEventReporter::ExecuteThresholdWriteEndEvent(std::string apiName,
357     std::shared_ptr<EventPeriodExpandableData> expandableData, int32_t dataCount)
358 {
359     HILOG_DEBUG("ExecuteThresholdWriteEndEvent enter.");
360     HiviewDFX::HiAppEvent::Event event("api_diagnostic", "api_called_stat_cnt", OHOS::HiviewDFX::HiAppEvent::BEHAVIOR);
361     event.AddParam("trans_id", std::string(""));
362     event.AddParam("api_name", apiName);
363     event.AddParam("sdk_name", std::string("AccessibilityKit"));
364     event.AddParam("success_times", expandableData->successCount);
365     int64_t maxElement = 0;
366     int64_t minElement = 0;
367     if (!expandableData->runTime.empty()) {
368         maxElement = *max_element(expandableData->runTime.begin(), expandableData->runTime.end());
369         minElement = *min_element(expandableData->runTime.begin(), expandableData->runTime.end());
370     }
371     event.AddParam("max_cost_time", maxElement);
372     event.AddParam("min_cost_time", minElement);
373     event.AddParam("total_cost_time", expandableData->sumTime);
374     if (!IsAppEventProccessorValid()) {
375         HILOG_ERROR("ExecuteThresholdWriteEndEvent processorid invalid!");
376         return;
377     }
378     HiviewDFX::HiAppEvent::Write(event);
379 
380     //重置数据
381     expandableData->runTime.clear();
382     expandableData->successCount = 0;
383     expandableData->sumTime = 0;
384 }
385 
IsValidPath(const std::string & filePath)386 bool ApiEventReporter::IsValidPath(const std::string& filePath)
387 {
388     std::string realFilePath;
389     if (!IsReal(filePath, realFilePath)) {
390         return false;
391     }
392     struct stat st;
393     if (stat(realFilePath.c_str(), &st)) {
394         return false;
395     }
396     return true;
397 }
398 
IsReal(const std::string & file,std::string & realFile)399 bool ApiEventReporter::IsReal(const std::string& file, std::string& realFile)
400 {
401     char realPath[PATH_MAX];
402     if (realpath(file.c_str(), realPath) == nullptr) {
403         return false;
404     }
405     realFile = std::string(realPath);
406     return true;
407 }
408 }  // namespace Accessibility
409 }  // namespace OHOS