1/*
2 * Copyright (c) 2021 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 "rule_cluster.h"
17
18#include <sstream>
19#include <sys/stat.h>
20#include <unistd.h>
21
22#include "hiview_logger.h"
23
24namespace OHOS {
25namespace HiviewDFX {
26DEFINE_LOG_LABEL(0xD002D01, "FreezeDetector");
27namespace {
28    static constexpr const char* const DEFAULT_RULE_FILE = "/system/etc/hiview/freeze_rules.xml";
29    static constexpr const char* const TAG_FREEZE = "freeze";
30    static constexpr const char* const TAG_RULES = "rules";
31    static constexpr const char* const TAG_RULE = "rule";
32    static constexpr const char* const TAG_LINKS = "links";
33    static constexpr const char* const TAG_EVENT = "event";
34    static constexpr const char* const TAG_RESULT = "result";
35    static constexpr const char* const TAG_RELEVANCE = "relevance";
36    static constexpr const char* const ATTRIBUTE_ID = "id";
37    static constexpr const char* const ATTRIBUTE_WINDOW = "window";
38    static constexpr const char* const ATTRIBUTE_DOMAIN = "domain";
39    static constexpr const char* const ATTRIBUTE_STRINGID = "stringid";
40    static constexpr const char* const ATTRIBUTE_TYPE = "type";
41    static constexpr const char* const ATTRIBUTE_USER = "user";
42    static constexpr const char* const ATTRIBUTE_WATCHPOINT = "watchpoint";
43    static constexpr const char* const ATTRIBUTE_CODE = "code";
44    static constexpr const char* const ATTRIBUTE_SCOPE = "scope";
45    static constexpr const char* const ATTRIBUTE_SAME_PACKAGE = "samePackage";
46    static constexpr const char* const ATTRIBUTE_ACTION = "action";
47    static constexpr const char* const ATTRIBUTE_APPLICATION = "application";
48    static constexpr const char* const ATTRIBUTE_SYSTEM = "system";
49    static const int MAX_FILE_SIZE = 512 * 1024;
50}
51
52FreezeRuleCluster::FreezeRuleCluster()
53{
54    rules_.clear();
55}
56
57FreezeRuleCluster::~FreezeRuleCluster()
58{
59    rules_.clear();
60}
61
62bool FreezeRuleCluster::Init()
63{
64    if (access(DEFAULT_RULE_FILE, R_OK) != 0) {
65        HIVIEW_LOGE("cannot access rule file.");
66        return false;
67    }
68
69    if (CheckFileSize(DEFAULT_RULE_FILE) == false) {
70        HIVIEW_LOGE("bad rule file size.");
71        return false;
72    }
73
74    if (ParseRuleFile(DEFAULT_RULE_FILE) == false) {
75        HIVIEW_LOGE("failed to parse rule file.");
76        return false;
77    }
78
79    if (rules_.size() == 0) {
80        HIVIEW_LOGE("no rule in rule file.");
81        return false;
82    }
83
84    return true;
85}
86
87bool FreezeRuleCluster::CheckFileSize(const std::string& path)
88{
89    struct stat st;
90    if (stat(path.c_str(), &st) != 0) {
91        return false;
92    }
93    if (st.st_size > MAX_FILE_SIZE) {
94        return false;
95    }
96    return true;
97}
98
99bool FreezeRuleCluster::ParseRuleFile(const std::string& file)
100{
101    xmlDoc* doc = xmlReadFile(file.c_str(), nullptr, 0);
102    if (doc == nullptr) {
103        HIVIEW_LOGE("failed to read rule file.");
104        return false;
105    }
106
107    xmlNode* root = xmlDocGetRootElement(doc);
108    if (root == nullptr) {
109        HIVIEW_LOGE("failed to get root element in rule file.");
110        xmlFreeDoc(doc);
111        doc = nullptr;
112        return false;
113    }
114
115    for (xmlNode* node = root; node; node = node->next) {
116        if (node->type != XML_ELEMENT_NODE) {
117            continue;
118        }
119        if (TAG_FREEZE == std::string((char*)(node->name))) {
120            ParseTagFreeze(node);
121            break;
122        }
123    }
124
125    xmlFreeDoc(doc);
126    doc = nullptr;
127    return true;
128}
129
130void FreezeRuleCluster::ParseTagFreeze(xmlNode* tag)
131{
132    for (xmlNode* node = tag->children; node; node = node->next) {
133        if (TAG_RULES == std::string((char*)(node->name))) {
134            ParseTagRules(node);
135        }
136    }
137}
138
139void FreezeRuleCluster::ParseTagRules(xmlNode* tag)
140{
141    for (xmlNode* node = tag->children; node; node = node->next) {
142        if (TAG_RULE == std::string((char*)(node->name))) {
143            ParseTagRule(node);
144        }
145    }
146}
147
148void FreezeRuleCluster::ParseTagRule(xmlNode* tag)
149{
150    std::string domain = GetAttributeValue<std::string>(tag, ATTRIBUTE_DOMAIN);
151    if (domain == "") {
152        HIVIEW_LOGE("null rule attribute:domain.");
153        return;
154    }
155    std::string stringId = GetAttributeValue<std::string>(tag, ATTRIBUTE_STRINGID);
156    if (stringId == "") {
157        HIVIEW_LOGE("null rule attribute:stringid.");
158        return;
159    }
160
161    FreezeRule rule = FreezeRule(domain, stringId);
162
163    for (xmlNode* node = tag->children; node; node = node->next) {
164        if (TAG_LINKS == std::string((char*)(node->name))) {
165            ParseTagLinks(node, rule);
166        }
167    }
168
169    if (rules_.find(domain + stringId) != rules_.end()) {
170        HIVIEW_LOGE("skip duplicated rule, stringid:%{public}s.", stringId.c_str());
171        return;
172    }
173
174    rules_[domain + stringId] = rule;
175}
176
177void FreezeRuleCluster::ParseTagLinks(xmlNode* tag, FreezeRule& rule)
178{
179    for (xmlNode* node = tag->children; node; node = node->next) {
180        if (TAG_EVENT == std::string((char*)(node->name))) {
181            std::string domain = GetAttributeValue<std::string>(node, ATTRIBUTE_DOMAIN);
182            if (domain == "") {
183                HIVIEW_LOGE("null event attribute:domain.");
184                return;
185            }
186            std::string stringId = GetAttributeValue<std::string>(node, ATTRIBUTE_STRINGID);
187            if (stringId == "") {
188                HIVIEW_LOGE("null event attribute:stringid.");
189                return;
190            }
191
192            long window = GetAttributeValue<long>(node, ATTRIBUTE_WINDOW);
193
194            FreezeResult result = FreezeResult(window, domain, stringId);
195            ParseTagEvent(node, result);
196            rule.AddResult(domain, stringId, result);
197
198            bool principalPoint = false;
199            if (rule.GetDomain() == domain && rule.GetStringId() == stringId) {
200                principalPoint = true;
201            }
202            if (result.GetScope() == "app") {
203                applicationPairs_[stringId] = std::pair<std::string, bool>(domain, principalPoint);
204            } else if (result.GetScope() == "sys") {
205                systemPairs_[stringId] = std::pair<std::string, bool>(domain, principalPoint);
206            } else {
207                sysWarningPairs_[stringId] = std::pair<std::string, bool>(domain, principalPoint);
208            }
209        }
210    }
211}
212
213void FreezeRuleCluster::ParseTagEvent(xmlNode* tag, FreezeResult& result)
214{
215    for (xmlNode* node = tag->children; node; node = node->next) {
216        if (TAG_RESULT == std::string((char*)(node->name))) {
217            ParseTagResult(node, result);
218            break;
219        }
220    }
221}
222
223void FreezeRuleCluster::ParseTagResult(xmlNode* tag, FreezeResult& result)
224{
225    unsigned long code = GetAttributeValue<unsigned long>(tag, ATTRIBUTE_CODE);
226    std::string scope = GetAttributeValue<std::string>(tag, ATTRIBUTE_SCOPE);
227    std::string samePackage = GetAttributeValue<std::string>(tag, ATTRIBUTE_SAME_PACKAGE);
228    std::string action = GetAttributeValue<std::string>(tag, ATTRIBUTE_ACTION);
229
230    result.SetId(code);
231    result.SetScope(scope);
232    result.SetSamePackage(samePackage);
233    result.SetAction(action);
234}
235
236template<typename T>
237T FreezeRuleCluster::GetAttributeValue(xmlNode* node, const std::string& name)
238{
239    xmlChar* prop = xmlGetProp(node, (xmlChar*)(name.c_str()));
240    std::string propa = "";
241    if (prop != nullptr) {
242        propa = (char*)prop;
243    }
244    std::istringstream istr(propa);
245    T value;
246    istr >> value;
247    xmlFree(prop);
248    return value;
249}
250
251bool FreezeRuleCluster::GetResult(const WatchPoint& watchPoint, std::vector<FreezeResult>& list)
252{
253    std::string domain = watchPoint.GetDomain();
254    std::string stringId = watchPoint.GetStringId();
255    if (rules_.find(domain + stringId) == rules_.end()) {
256        return false;
257    }
258    auto map = rules_[domain + stringId].GetMap();
259    for (auto& i : map) {
260        list.push_back(i.second);
261    }
262
263    if (list.empty()) {
264        return false;
265    }
266    return true;
267}
268
269std::map<std::string, std::pair<std::string, bool>> FreezeRuleCluster::GetApplicationPairs() const
270{
271    return applicationPairs_;
272}
273
274std::map<std::string, std::pair<std::string, bool>> FreezeRuleCluster::GetSystemPairs() const
275{
276    return systemPairs_;
277}
278
279std::map<std::string, std::pair<std::string, bool>> FreezeRuleCluster::GetSysWarningPairs() const
280{
281    return sysWarningPairs_;
282}
283
284std::string FreezeResult::GetDomain() const
285{
286    return domain_;
287}
288
289std::string FreezeResult::GetStringId() const
290{
291    return stringId_;
292}
293
294unsigned long FreezeResult::GetId() const
295{
296    return code_;
297}
298
299void FreezeResult::SetId(unsigned long code)
300{
301    code_ = code;
302}
303
304std::string FreezeResult::GetScope() const
305{
306    return scope_;
307}
308
309void FreezeResult::SetScope(const std::string& scope)
310{
311    scope_ = scope;
312}
313
314long FreezeResult::GetWindow() const
315{
316    return window_;
317}
318
319std::string FreezeResult::GetSamePackage() const
320{
321    return samePackage_;
322}
323
324void FreezeResult::SetSamePackage(const std::string& samePackage)
325{
326    samePackage_ = samePackage;
327}
328
329std::string FreezeResult::GetAction() const
330{
331    return action_;
332}
333
334void FreezeResult::SetAction(const std::string& action)
335{
336    action_ = action;
337}
338
339std::string FreezeRule::GetDomain() const
340{
341    return domain_;
342}
343
344void FreezeRule::SetDomain(const std::string& domain)
345{
346    domain_ = domain;
347}
348
349std::string FreezeRule::GetStringId() const
350{
351    return stringId_;
352}
353
354void FreezeRule::SetStringId(const std::string& stringId)
355{
356    stringId_ = stringId;
357}
358
359std::map<std::string, FreezeResult> FreezeRule::GetMap() const
360{
361    return results_;
362}
363
364void FreezeRule::AddResult(const std::string& domain, const std::string& stringId, const FreezeResult& result)
365{
366    if (results_.find(domain + stringId) != results_.end()) {
367        HIVIEW_LOGE("skip duplicated event tag, stringid:%{public}s.", stringId.c_str());
368        return;
369    }
370
371    results_[domain + stringId] = result;
372}
373
374bool FreezeRule::GetResult(const std::string& domain, const std::string& stringId, FreezeResult& result)
375{
376    if (results_.find(domain + stringId) == results_.end()) {
377        HIVIEW_LOGE("failed to find rule result, domain:%{public}s stringid:%{public}s.",
378            domain.c_str(), stringId.c_str());
379        return false;
380    }
381
382    result = results_[domain + stringId]; // take result back
383    return true;
384}
385} // namespace HiviewDFX
386} // namespace OHOS
387