1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "Log.h"
18 
19 #include "CombinationAtomMatchingTracker.h"
20 
21 #include "matchers/matcher_util.h"
22 
23 namespace android {
24 namespace os {
25 namespace statsd {
26 
27 using std::set;
28 using std::shared_ptr;
29 using std::unordered_map;
30 using std::vector;
31 
CombinationAtomMatchingTracker(const int64_t id,const uint64_t protoHash)32 CombinationAtomMatchingTracker::CombinationAtomMatchingTracker(const int64_t id,
33                                                                const uint64_t protoHash)
34     : AtomMatchingTracker(id, protoHash) {
35 }
36 
~CombinationAtomMatchingTracker()37 CombinationAtomMatchingTracker::~CombinationAtomMatchingTracker() {
38 }
39 
init(int matcherIndex,const vector<AtomMatcher> & allAtomMatchers,const vector<sp<AtomMatchingTracker>> & allAtomMatchingTrackers,const unordered_map<int64_t,int> & matcherMap,vector<uint8_t> & stack)40 MatcherInitResult CombinationAtomMatchingTracker::init(
41         int matcherIndex, const vector<AtomMatcher>& allAtomMatchers,
42         const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
43         const unordered_map<int64_t, int>& matcherMap, vector<uint8_t>& stack) {
44     MatcherInitResult result{nullopt /* invalidConfigReason */,
45                              false /* hasStringTransformation */};
46     if (mInitialized) {
47         // CombinationMatchers do not support string transformations so if mInitialized = true,
48         // we know that there is no string transformation and we do not need to check for it again.
49         return result;
50     }
51 
52     // mark this node as visited in the recursion stack.
53     stack[matcherIndex] = true;
54 
55     AtomMatcher_Combination matcher = allAtomMatchers[matcherIndex].combination();
56 
57     // LogicalOperation is missing in the config
58     if (!matcher.has_operation()) {
59         result.invalidConfigReason = createInvalidConfigReasonWithMatcher(
60                 INVALID_CONFIG_REASON_MATCHER_NO_OPERATION, mId);
61         return result;
62     }
63 
64     mLogicalOperation = matcher.operation();
65 
66     if (mLogicalOperation == LogicalOperation::NOT && matcher.matcher_size() != 1) {
67         result.invalidConfigReason = createInvalidConfigReasonWithMatcher(
68                 INVALID_CONFIG_REASON_MATCHER_NOT_OPERATION_IS_NOT_UNARY, mId);
69         return result;
70     }
71 
72     for (const auto& child : matcher.matcher()) {
73         auto pair = matcherMap.find(child);
74         if (pair == matcherMap.end()) {
75             ALOGW("Matcher %lld not found in the config", (long long)child);
76             result.invalidConfigReason = createInvalidConfigReasonWithMatcher(
77                     INVALID_CONFIG_REASON_MATCHER_CHILD_NOT_FOUND, mId);
78             result.invalidConfigReason->matcherIds.push_back(child);
79             return result;
80         }
81 
82         int childIndex = pair->second;
83 
84         // if the child is a visited node in the recursion -> circle detected.
85         if (stack[childIndex]) {
86             ALOGE("Circle detected in matcher config");
87             result.invalidConfigReason =
88                     createInvalidConfigReasonWithMatcher(INVALID_CONFIG_REASON_MATCHER_CYCLE, mId);
89             result.invalidConfigReason->matcherIds.push_back(child);
90             return result;
91         }
92         auto [invalidConfigReason, hasStringTransformation] =
93                 allAtomMatchingTrackers[childIndex]->init(
94                         childIndex, allAtomMatchers, allAtomMatchingTrackers, matcherMap, stack);
95         if (hasStringTransformation) {
96             ALOGE("String transformation detected in CombinationMatcher");
97             result.invalidConfigReason = createInvalidConfigReasonWithMatcher(
98                     INVALID_CONFIG_REASON_MATCHER_COMBINATION_WITH_STRING_REPLACE, mId);
99             result.hasStringTransformation = true;
100             return result;
101         }
102 
103         if (invalidConfigReason.has_value()) {
104             ALOGW("child matcher init failed %lld", (long long)child);
105             invalidConfigReason->matcherIds.push_back(mId);
106             result.invalidConfigReason = invalidConfigReason;
107             return result;
108         }
109 
110         mChildren.push_back(childIndex);
111 
112         const set<int>& childTagIds = allAtomMatchingTrackers[childIndex]->getAtomIds();
113         mAtomIds.insert(childTagIds.begin(), childTagIds.end());
114     }
115 
116     mInitialized = true;
117     // unmark this node in the recursion stack.
118     stack[matcherIndex] = false;
119     return result;
120 }
121 
onConfigUpdated(const AtomMatcher & matcher,const unordered_map<int64_t,int> & atomMatchingTrackerMap)122 optional<InvalidConfigReason> CombinationAtomMatchingTracker::onConfigUpdated(
123         const AtomMatcher& matcher, const unordered_map<int64_t, int>& atomMatchingTrackerMap) {
124     mChildren.clear();
125     const AtomMatcher_Combination& combinationMatcher = matcher.combination();
126     for (const int64_t child : combinationMatcher.matcher()) {
127         const auto& pair = atomMatchingTrackerMap.find(child);
128         if (pair == atomMatchingTrackerMap.end()) {
129             ALOGW("Matcher %lld not found in the config", (long long)child);
130             optional<InvalidConfigReason> invalidConfigReason =
131                     createInvalidConfigReasonWithMatcher(
132                             INVALID_CONFIG_REASON_MATCHER_CHILD_NOT_FOUND, matcher.id());
133             invalidConfigReason->matcherIds.push_back(child);
134             return invalidConfigReason;
135         }
136         mChildren.push_back(pair->second);
137     }
138     return nullopt;
139 }
140 
onLogEvent(const LogEvent & event,int matcherIndex,const vector<sp<AtomMatchingTracker>> & allAtomMatchingTrackers,vector<MatchingState> & matcherResults,vector<shared_ptr<LogEvent>> & matcherTransformations)141 void CombinationAtomMatchingTracker::onLogEvent(
142         const LogEvent& event, int matcherIndex,
143         const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
144         vector<MatchingState>& matcherResults,
145         vector<shared_ptr<LogEvent>>& matcherTransformations) {
146     // this event has been processed.
147     if (matcherResults[matcherIndex] != MatchingState::kNotComputed) {
148         return;
149     }
150 
151     if (mAtomIds.find(event.GetTagId()) == mAtomIds.end()) {
152         matcherResults[matcherIndex] = MatchingState::kNotMatched;
153         return;
154     }
155 
156     // evaluate children matchers if they haven't been evaluated.
157     for (const int childIndex : mChildren) {
158         if (matcherResults[childIndex] == MatchingState::kNotComputed) {
159             const sp<AtomMatchingTracker>& child = allAtomMatchingTrackers[childIndex];
160             child->onLogEvent(event, childIndex, allAtomMatchingTrackers, matcherResults,
161                               matcherTransformations);
162         }
163     }
164 
165     bool matched = combinationMatch(mChildren, mLogicalOperation, matcherResults);
166     matcherResults[matcherIndex] = matched ? MatchingState::kMatched : MatchingState::kNotMatched;
167 }
168 
169 }  // namespace statsd
170 }  // namespace os
171 }  // namespace android
172