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