1 /*
2 * Copyright (c) 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 <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 "drm_log.h"
28 #include "drm_error_code.h"
29 #include "drm_api_operation.h"
30 
31 namespace OHOS {
32 namespace DrmStandard {
33 // Global variables for caching file content and mutex
34 std::string ConfigParser::g_fileContent = "";
35 int64_t ConfigParser::g_processorId = -1;
36 std::mutex ConfigParser::g_apiOperationMutex;
37 const int32_t APP_FLAG = -200;
38 
LoadConfigurationFile(const std::string & configFile)39 bool ConfigParser::LoadConfigurationFile(const std::string &configFile)
40 {
41     std::ifstream file(configFile);
42     if (!file.is_open()) {
43         perror("Unable to open api operation config file!");
44         return false;
45     }
46 
47     std::ostringstream oss;
48     std::string line;
49     while (std::getline(file, line)) {
50         line = Trim(line);
51         if (!line.empty() && line[0] != '#') {
52             oss << line << "\n";
53         }
54     }
55     g_fileContent = oss.str();
56     file.close();
57     return true;
58 }
59 
GetConfigurationParams(ApiReportConfig & reportConfig,ApiEventConfig & eventConfig)60 void ConfigParser::GetConfigurationParams(ApiReportConfig &reportConfig, ApiEventConfig &eventConfig)
61 {
62     std::istringstream stream(g_fileContent);
63     ParseApiOperationManagement(stream, reportConfig, eventConfig);
64 }
65 
Trim(const std::string & str)66 std::string ConfigParser::Trim(const std::string &str)
67 {
68     const char* whitespace = " \t\n\r";
69     size_t first = str.find_first_not_of(whitespace);
70     size_t last = str.find_last_not_of(whitespace);
71     return (first == std::string::npos) ? "" : str.substr(first, last - first + 1);
72 }
73 
ParseKeyValue(const std::string & line)74 std::pair<std::string, std::string> ConfigParser::ParseKeyValue(const std::string &line)
75 {
76     size_t colonPos = line.find(':');
77     if (colonPos != std::string::npos) {
78         std::string key = Trim(line.substr(0, colonPos));
79         std::string value = Trim(line.substr(colonPos + 1));
80         key.erase(std::remove(key.begin(), key.end(), '"'), key.end());
81         value.erase(std::remove(value.begin(), value.end(), '"'), value.end());
82         if (!value.empty() && value.back() == ',') {
83             value.pop_back(); // Remove trailing comma if present
84         }
85         return std::make_pair(key, value);
86     }
87     return std::make_pair("", "");
88 }
89 
TryParseInt(const std::string & str,int & out)90 bool ConfigParser::TryParseInt(const std::string& str, int& out)
91 {
92     char* end;
93     long val = strtol(str.c_str(), &end, 10);
94     if (*end == '\0' && end != str.c_str() && val >= INT_MIN && val <= INT_MAX) {
95         out = static_cast<int>(val);
96         return true;
97     } else {
98         DRM_ERR_LOG("Invalid integer: %{public}s", str.c_str());
99         return false;
100     }
101 }
102 
ParseReportConfig(std::istringstream & stream,ApiReportConfig & reportConfig)103 void ConfigParser::ParseReportConfig(std::istringstream &stream, ApiReportConfig &reportConfig)
104 {
105     std::unordered_map<std::string, std::function<void(const std::string&)>> configMap = {
106         {"config_name", [&](const std::string &value) { reportConfig.config_name = value; }},
107         {"config_appId", [&](const std::string &value) { reportConfig.config_appId = value; }},
108         {"config_routeInfo", [&](const std::string &value) { reportConfig.config_routeInfo = value; }},
109         {"config_TriggerCond.timeout", [&](const std::string &value) {
110             int temp;
111             if (TryParseInt(value, temp)) {
112                 reportConfig.config_timeout = temp;
113             } else {
114                 DRM_ERR_LOG("Invalid integer for config_timeout: %{public}s", value.c_str());
115             }
116         }},
117         {"config_TriggerCond.row", [&](const std::string &value) {
118             int temp;
119             if (TryParseInt(value, temp)) {
120                 reportConfig.config_row = temp;
121             } else {
122                 DRM_ERR_LOG("Invalid integer for config_row: %{public}s", value.c_str());
123             }
124         }}
125     };
126 
127     std::string line;
128     while (std::getline(stream, line)) {
129         line = Trim(line);
130         if (line == "},") {
131             break;
132         }
133         auto keyValue = ParseKeyValue(line);
134         auto it = configMap.find(keyValue.first);
135         if (it != configMap.end()) {
136             it->second(keyValue.second);
137         }
138     }
139 }
140 
ParseEvent(std::istringstream & stream,ApiEvent & event)141 void ConfigParser::ParseEvent(std::istringstream &stream, ApiEvent &event)
142 {
143     std::unordered_map<std::string, std::function<void(const std::string&)>> eventMap = {
144         {"domain", [&](const std::string &value) { event.domain = value; }},
145         {"name", [&](const std::string &value) { event.name = value; }},
146         {"isRealTime", [&](const std::string &value) { event.isRealTime = (value == "true"); }}
147     };
148 
149     std::string line;
150     while (std::getline(stream, line)) {
151         line = Trim(line);
152         if (line == "},") {
153             break;
154         }
155         auto keyValue = ParseKeyValue(line);
156         auto it = eventMap.find(keyValue.first);
157         if (it != eventMap.end()) {
158             it->second(keyValue.second);
159         }
160     }
161 }
162 
ParseEventConfig(std::istringstream & stream,ApiEventConfig & eventConfig)163 void ConfigParser::ParseEventConfig(std::istringstream &stream, ApiEventConfig &eventConfig)
164 {
165     std::unordered_map<std::string, std::function<void(std::istringstream&)>> eventConfigMap = {
166         {"\"event1\": {", [&](std::istringstream &stream) { ParseEvent(stream, eventConfig.event1); }},
167         {"\"event2\": {", [&](std::istringstream &stream) { ParseEvent(stream, eventConfig.event2); }},
168         {"\"event3\": {", [&](std::istringstream &stream) { ParseEvent(stream, eventConfig.event3); }}
169     };
170 
171     std::string line;
172     while (std::getline(stream, line)) {
173         line = Trim(line);
174         if (line == "},") {
175             break;
176         }
177         auto it = eventConfigMap.find(line);
178         if (it != eventConfigMap.end()) {
179             it->second(stream);
180         }
181     }
182 }
183 
ParseApiOperationManagement(std::istringstream & stream,ApiReportConfig & reportConfig,ApiEventConfig & eventConfig)184 void ConfigParser::ParseApiOperationManagement(std::istringstream &stream, ApiReportConfig &reportConfig,
185     ApiEventConfig &eventConfig)
186 {
187     std::unordered_map<std::string, std::function<void(std::istringstream&)>> apiOpMgmtMap = {
188         {"\"report_config\": {", [&](std::istringstream &stream) { ParseReportConfig(stream, reportConfig); }},
189         {"\"event_config\": {", [&](std::istringstream &stream) { ParseEventConfig(stream, eventConfig); }}
190     };
191 
192     std::string line;
193     while (std::getline(stream, line)) {
194         line = Trim(line);
195         if (line == "}") {
196             break;
197         }
198         auto it = apiOpMgmtMap.find(line);
199         if (it != apiOpMgmtMap.end()) {
200             it->second(stream);
201         }
202     }
203 }
204 
AddProcessor()205 int64_t ConfigParser::AddProcessor()
206 {
207     DRM_INFO_LOG("AddProcessor enter.");
208     ApiReportConfig reportConfig;
209     ApiEventConfig eventConfig;
210     std::lock_guard<std::mutex> lock(g_apiOperationMutex);
211     if (g_processorId != -1) {
212         DRM_DEBUG_LOG("AddProcessor exit");
213         return g_processorId;
214     }
215     if (g_processorId == APP_FLAG) {
216         DRM_ERR_LOG("dotting is not supported for non-apps");
217         return g_processorId;
218     }
219     if (LoadConfigurationFile(DRM_API_OPERATION_CONFIG_PATH) != true) {
220         DRM_ERR_LOG("AddProcessor LoadConfigurationFile error!");
221         return DRM_OPERATION_NOT_ALLOWED;
222     }
223     GetConfigurationParams(reportConfig, eventConfig);
224     HiviewDFX::HiAppEvent::ReportConfig config;
225     config.name = reportConfig.config_name;
226     config.appId = reportConfig.config_appId;
227     config.routeInfo = reportConfig.config_routeInfo;
228     config.triggerCond.timeout = reportConfig.config_timeout;
229     config.triggerCond.row = reportConfig.config_row;
230     config.eventConfigs.clear();
231     HiviewDFX::HiAppEvent::EventConfig event1;
232     event1.domain = eventConfig.event1.domain;
233     event1.name = eventConfig.event1.name;
234     event1.isRealTime = eventConfig.event1.isRealTime;
235     config.eventConfigs.push_back(event1);
236     HiviewDFX::HiAppEvent::EventConfig event2;
237     event2.domain = eventConfig.event2.domain;
238     event2.name = eventConfig.event2.name;
239     event2.isRealTime = eventConfig.event2.isRealTime;
240     config.eventConfigs.push_back(event2);
241     HiviewDFX::HiAppEvent::EventConfig event3;
242     event3.domain = eventConfig.event3.domain;
243     event3.name = eventConfig.event3.name;
244     event3.isRealTime = eventConfig.event3.isRealTime;
245     config.eventConfigs.push_back(event3);
246     g_processorId = HiviewDFX::HiAppEvent::AppEventProcessorMgr::AddProcessor(config);
247     return g_processorId;
248 }
249 
WriteEndEvent(const int result,const int errCode,std::string apiName,int64_t beginTime)250 void ConfigParser::WriteEndEvent(const int result, const int errCode, std::string apiName, int64_t beginTime)
251 {
252     DRM_INFO_LOG("WriteEndEvent enter.");
253     (void)AddProcessor();
254     std::string transId = std::string("traceId_") + std::to_string(std::rand());
255     int64_t endTime = std::chrono::duration_cast<std::chrono::milliseconds>(
256         std::chrono::system_clock::now().time_since_epoch()).count();
257 
258     HiviewDFX::HiAppEvent::Event event("api_diagnostic", "api_exec_end", OHOS::HiviewDFX::HiAppEvent::BEHAVIOR);
259     event.AddParam("trans_id", transId);
260     event.AddParam("api_name", apiName);
261     event.AddParam("sdk_name", std::string("DrmKit"));
262     event.AddParam("begin_time", beginTime);
263     event.AddParam("end_time", endTime);
264     event.AddParam("result", result);
265     event.AddParam("error_code", errCode);
266     HiviewDFX::HiAppEvent::Write(event);
267 }
268 }  // namespace DrmStandard
269 }  // namespace OHOS