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