blob: 7b5bcb077a7211785442f5a9a27ed928a58a1922 [file] [log] [blame]
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define STATSD_DEBUG false // STOPSHIP if true
#include "Log.h"
#include "matchers/matcher_util.h"
#include <fnmatch.h>
#include "matchers/AtomMatchingTracker.h"
#include "src/statsd_config.pb.h"
#include "stats_util.h"
using std::set;
using std::string;
using std::vector;
namespace android {
namespace os {
namespace statsd {
bool combinationMatch(const vector<int>& children, const LogicalOperation& operation,
const vector<MatchingState>& matcherResults) {
bool matched;
switch (operation) {
case LogicalOperation::AND: {
matched = true;
for (const int childIndex : children) {
if (matcherResults[childIndex] != MatchingState::kMatched) {
matched = false;
break;
}
}
break;
}
case LogicalOperation::OR: {
matched = false;
for (const int childIndex : children) {
if (matcherResults[childIndex] == MatchingState::kMatched) {
matched = true;
break;
}
}
break;
}
case LogicalOperation::NOT:
matched = matcherResults[children[0]] == MatchingState::kNotMatched;
break;
case LogicalOperation::NAND:
matched = false;
for (const int childIndex : children) {
if (matcherResults[childIndex] != MatchingState::kMatched) {
matched = true;
break;
}
}
break;
case LogicalOperation::NOR:
matched = true;
for (const int childIndex : children) {
if (matcherResults[childIndex] == MatchingState::kMatched) {
matched = false;
break;
}
}
break;
case LogicalOperation::LOGICAL_OPERATION_UNSPECIFIED:
matched = false;
break;
}
return matched;
}
static bool tryMatchString(const sp<UidMap>& uidMap, const FieldValue& fieldValue,
const string& str_match) {
if (isAttributionUidField(fieldValue) || isUidField(fieldValue)) {
int uid = fieldValue.mValue.int_value;
auto aidIt = UidMap::sAidToUidMapping.find(str_match);
if (aidIt != UidMap::sAidToUidMapping.end()) {
return ((int)aidIt->second) == uid;
}
std::set<string> packageNames = uidMap->getAppNamesFromUid(uid, true /* normalize*/);
return packageNames.find(str_match) != packageNames.end();
} else if (fieldValue.mValue.getType() == STRING) {
return fieldValue.mValue.str_value == str_match;
}
return false;
}
static bool tryMatchWildcardString(const sp<UidMap>& uidMap, const FieldValue& fieldValue,
const string& wildcardPattern) {
if (isAttributionUidField(fieldValue) || isUidField(fieldValue)) {
int uid = fieldValue.mValue.int_value;
// TODO(b/236886985): replace aid/uid mapping with efficient bidirectional container
// AidToUidMapping will never have uids above 10000
if (uid < 10000) {
for (auto aidIt = UidMap::sAidToUidMapping.begin();
aidIt != UidMap::sAidToUidMapping.end(); ++aidIt) {
if ((int)aidIt->second == uid) {
// Assumes there is only one aid mapping for each uid
return fnmatch(wildcardPattern.c_str(), aidIt->first.c_str(), 0) == 0;
}
}
}
std::set<string> packageNames = uidMap->getAppNamesFromUid(uid, true /* normalize*/);
for (const auto& packageName : packageNames) {
if (fnmatch(wildcardPattern.c_str(), packageName.c_str(), 0) == 0) {
return true;
}
}
} else if (fieldValue.mValue.getType() == STRING) {
return fnmatch(wildcardPattern.c_str(), fieldValue.mValue.str_value.c_str(), 0) == 0;
}
return false;
}
static pair<int, int> getStartEndAtDepth(int targetField, int start, int end, int depth,
const vector<FieldValue>& values) {
// Filter by entry field first
int newStart = -1;
int newEnd = end;
// because the fields are naturally sorted in the DFS order. we can safely
// break when pos is larger than the one we are searching for.
for (int i = start; i < end; i++) {
int pos = values[i].mField.getPosAtDepth(depth);
if (pos == targetField) {
if (newStart == -1) {
newStart = i;
}
newEnd = i + 1;
} else if (pos > targetField) {
break;
}
}
return {newStart, newEnd};
}
/*
* Returns pairs of start-end indices in vector<FieldValue> that pariticipate in matching.
* The returned vector is empty if an error was encountered.
* If Position is ANY and value_matcher is matches_tuple, the vector contains a start/end pair
* corresponding for each child FieldValueMatcher in matches_tuple. For all other cases, the
* returned vector is of size 1.
*
* Also updates the depth reference parameter if matcher has Position specified.
*/
static vector<pair<int, int>> computeRanges(const FieldValueMatcher& matcher,
const vector<FieldValue>& values, int start, int end,
int& depth) {
// Now we have zoomed in to a new range
std::tie(start, end) = getStartEndAtDepth(matcher.field(), start, end, depth, values);
if (start == -1) {
// No such field found.
return {};
}
vector<pair<int, int>> ranges;
if (matcher.has_position()) {
// Repeated fields position is stored as a node in the path.
depth++;
if (depth > 2) {
return ranges;
}
switch (matcher.position()) {
case Position::FIRST: {
for (int i = start; i < end; i++) {
int pos = values[i].mField.getPosAtDepth(depth);
if (pos != 1) {
// Again, the log elements are stored in sorted order. so
// once the position is > 1, we break;
end = i;
break;
}
}
ranges.push_back(std::make_pair(start, end));
break;
}
case Position::LAST: {
// move the starting index to the first LAST field at the depth.
for (int i = start; i < end; i++) {
if (values[i].mField.isLastPos(depth)) {
start = i;
break;
}
}
ranges.push_back(std::make_pair(start, end));
break;
}
case Position::ANY: {
if (matcher.value_matcher_case() == FieldValueMatcher::kMatchesTuple) {
// For ANY with matches_tuple, if all the children matchers match in any of the
// sub trees, it's a match.
// Here start is guaranteed to be a valid index.
int currentPos = values[start].mField.getPosAtDepth(depth);
// Now find all sub trees ranges.
for (int i = start; i < end; i++) {
int newPos = values[i].mField.getPosAtDepth(depth);
if (newPos != currentPos) {
ranges.push_back(std::make_pair(start, i));
start = i;
currentPos = newPos;
}
}
}
ranges.push_back(std::make_pair(start, end));
break;
}
case Position::ALL:
ALOGE("Not supported: field matcher with ALL position.");
break;
case Position::POSITION_UNKNOWN:
break;
}
} else {
// No position
ranges.push_back(std::make_pair(start, end));
}
return ranges;
}
static bool matchesSimple(const sp<UidMap>& uidMap, const FieldValueMatcher& matcher,
const vector<FieldValue>& values, int start, int end, int depth) {
if (depth > 2) {
ALOGE("Depth > 3 not supported");
return false;
}
if (start >= end) {
return false;
}
const vector<pair<int, int>> ranges = computeRanges(matcher, values, start, end, depth);
if (ranges.empty()) {
// No such field found.
return false;
}
// ranges should have exactly one start/end pair at this point unless position is ANY and
// value_matcher is matches_tuple.
std::tie(start, end) = ranges[0];
switch (matcher.value_matcher_case()) {
case FieldValueMatcher::kMatchesTuple: {
++depth;
// If any range matches all matchers, good.
for (const auto& [rangeStart, rangeEnd] : ranges) {
bool matched = true;
for (const auto& subMatcher : matcher.matches_tuple().field_value_matcher()) {
if (!matchesSimple(uidMap, subMatcher, values, rangeStart, rangeEnd, depth)) {
matched = false;
break;
}
}
if (matched) return true;
}
return false;
}
// Finally, we get to the point of real value matching.
// If the field matcher ends with ANY, then we have [start, end) range > 1.
// In the following, we should return true, when ANY of the values matches.
case FieldValueMatcher::ValueMatcherCase::kEqBool: {
for (int i = start; i < end; i++) {
if ((values[i].mValue.getType() == INT &&
(values[i].mValue.int_value != 0) == matcher.eq_bool()) ||
(values[i].mValue.getType() == LONG &&
(values[i].mValue.long_value != 0) == matcher.eq_bool())) {
return true;
}
}
return false;
}
case FieldValueMatcher::ValueMatcherCase::kEqString: {
for (int i = start; i < end; i++) {
if (tryMatchString(uidMap, values[i], matcher.eq_string())) {
return true;
}
}
return false;
}
case FieldValueMatcher::ValueMatcherCase::kNeqAnyString: {
const auto& str_list = matcher.neq_any_string();
for (int i = start; i < end; i++) {
bool notEqAll = true;
for (const auto& str : str_list.str_value()) {
if (tryMatchString(uidMap, values[i], str)) {
notEqAll = false;
break;
}
}
if (notEqAll) {
return true;
}
}
return false;
}
case FieldValueMatcher::ValueMatcherCase::kEqAnyString: {
const auto& str_list = matcher.eq_any_string();
for (int i = start; i < end; i++) {
for (const auto& str : str_list.str_value()) {
if (tryMatchString(uidMap, values[i], str)) {
return true;
}
}
}
return false;
}
case FieldValueMatcher::ValueMatcherCase::kEqWildcardString: {
for (int i = start; i < end; i++) {
if (tryMatchWildcardString(uidMap, values[i], matcher.eq_wildcard_string())) {
return true;
}
}
return false;
}
case FieldValueMatcher::ValueMatcherCase::kEqAnyWildcardString: {
const auto& str_list = matcher.eq_any_wildcard_string();
for (int i = start; i < end; i++) {
for (const auto& str : str_list.str_value()) {
if (tryMatchWildcardString(uidMap, values[i], str)) {
return true;
}
}
}
return false;
}
case FieldValueMatcher::ValueMatcherCase::kNeqAnyWildcardString: {
const auto& str_list = matcher.neq_any_wildcard_string();
for (int i = start; i < end; i++) {
bool notEqAll = true;
for (const auto& str : str_list.str_value()) {
if (tryMatchWildcardString(uidMap, values[i], str)) {
notEqAll = false;
break;
}
}
if (notEqAll) {
return true;
}
}
return false;
}
case FieldValueMatcher::ValueMatcherCase::kEqInt: {
for (int i = start; i < end; i++) {
if (values[i].mValue.getType() == INT &&
(matcher.eq_int() == values[i].mValue.int_value)) {
return true;
}
// eq_int covers both int and long.
if (values[i].mValue.getType() == LONG &&
(matcher.eq_int() == values[i].mValue.long_value)) {
return true;
}
}
return false;
}
case FieldValueMatcher::ValueMatcherCase::kEqAnyInt: {
const auto& int_list = matcher.eq_any_int();
for (int i = start; i < end; i++) {
for (const int int_value : int_list.int_value()) {
if (values[i].mValue.getType() == INT &&
(int_value == values[i].mValue.int_value)) {
return true;
}
// eq_any_int covers both int and long.
if (values[i].mValue.getType() == LONG &&
(int_value == values[i].mValue.long_value)) {
return true;
}
}
}
return false;
}
case FieldValueMatcher::ValueMatcherCase::kNeqAnyInt: {
const auto& int_list = matcher.neq_any_int();
for (int i = start; i < end; i++) {
bool notEqAll = true;
for (const int int_value : int_list.int_value()) {
if (values[i].mValue.getType() == INT &&
(int_value == values[i].mValue.int_value)) {
notEqAll = false;
break;
}
// neq_any_int covers both int and long.
if (values[i].mValue.getType() == LONG &&
(int_value == values[i].mValue.long_value)) {
notEqAll = false;
break;
}
}
if (notEqAll) {
return true;
}
}
return false;
}
case FieldValueMatcher::ValueMatcherCase::kLtInt: {
for (int i = start; i < end; i++) {
if (values[i].mValue.getType() == INT &&
(values[i].mValue.int_value < matcher.lt_int())) {
return true;
}
// lt_int covers both int and long.
if (values[i].mValue.getType() == LONG &&
(values[i].mValue.long_value < matcher.lt_int())) {
return true;
}
}
return false;
}
case FieldValueMatcher::ValueMatcherCase::kGtInt: {
for (int i = start; i < end; i++) {
if (values[i].mValue.getType() == INT &&
(values[i].mValue.int_value > matcher.gt_int())) {
return true;
}
// gt_int covers both int and long.
if (values[i].mValue.getType() == LONG &&
(values[i].mValue.long_value > matcher.gt_int())) {
return true;
}
}
return false;
}
case FieldValueMatcher::ValueMatcherCase::kLtFloat: {
for (int i = start; i < end; i++) {
if (values[i].mValue.getType() == FLOAT &&
(values[i].mValue.float_value < matcher.lt_float())) {
return true;
}
}
return false;
}
case FieldValueMatcher::ValueMatcherCase::kGtFloat: {
for (int i = start; i < end; i++) {
if (values[i].mValue.getType() == FLOAT &&
(values[i].mValue.float_value > matcher.gt_float())) {
return true;
}
}
return false;
}
case FieldValueMatcher::ValueMatcherCase::kLteInt: {
for (int i = start; i < end; i++) {
if (values[i].mValue.getType() == INT &&
(values[i].mValue.int_value <= matcher.lte_int())) {
return true;
}
// lte_int covers both int and long.
if (values[i].mValue.getType() == LONG &&
(values[i].mValue.long_value <= matcher.lte_int())) {
return true;
}
}
return false;
}
case FieldValueMatcher::ValueMatcherCase::kGteInt: {
for (int i = start; i < end; i++) {
if (values[i].mValue.getType() == INT &&
(values[i].mValue.int_value >= matcher.gte_int())) {
return true;
}
// gte_int covers both int and long.
if (values[i].mValue.getType() == LONG &&
(values[i].mValue.long_value >= matcher.gte_int())) {
return true;
}
}
return false;
}
default:
return false;
}
}
bool matchesSimple(const sp<UidMap>& uidMap, const SimpleAtomMatcher& simpleMatcher,
const LogEvent& event) {
if (event.GetTagId() != simpleMatcher.atom_id()) {
return false;
}
for (const auto& matcher : simpleMatcher.field_value_matcher()) {
if (!matchesSimple(uidMap, matcher, event.getValues(), 0, event.getValues().size(), 0)) {
return false;
}
}
return true;
}
} // namespace statsd
} // namespace os
} // namespace android