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