blob: 897e5679447514a966ec3642e0a951a1690aece6 [file] [log] [blame]
/*
* Copyright (C) 2020 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.
*/
#pragma once
#include <android-base/thread_annotations.h>
#include <media/MediaMetricsItem.h>
#include <mutex>
namespace android::mediametrics {
/**
* AnalyticsActions consists of a map of pairs <trigger, action> which
* are evaluated for a given incoming MediaMetrics item.
*
* A vector of Actions are returned from getActionsForItem() which
* should be executed outside of any locks.
*
* Mediametrics assumes weak consistency, which is fine as the analytics database
* is generally strictly increasing in size (until gc removes values that are
* supposedly no longer needed).
*/
class AnalyticsActions {
public:
using Elem = mediametrics::Item::Prop::Elem;
/**
* Trigger: a pair consisting of
* std::string: A wildcard url specifying a property in the item,
* where '*' indicates 0 or more arbitrary characters
* for the item key match.
* Elem: A value that needs to match exactly.
*
* Trigger is used in a map sort; default less with std::string as primary key.
* The wildcard accepts a string with '*' as being 0 or more arbitrary
* characters for the item key match. A wildcard is preferred over general
* regexp for simple fast lookup.
*
* TODO: incorporate a regexp option.
*/
using Trigger = std::pair<std::string, Elem>;
/**
* Function: The function to be executed.
*/
using Function = std::function<
void(const std::shared_ptr<const mediametrics::Item>& item)>;
/**
* Action: An action to execute. This is a shared pointer to Function.
*/
using Action = std::shared_ptr<Function>;
/**
* Adds a new action.
*
* \param url references a property in the item with wildcards
* \param value references a value (cast to Elem automatically)
* so be careful of the type. It must be one of
* the types acceptable to Elem.
* \param action is a function or lambda to execute if the url matches value
* in the item.
*/
template <typename T, typename U, typename A>
void addAction(T&& url, U&& value, A&& action) {
std::lock_guard l(mLock);
mFilters.emplace(Trigger{ std::forward<T>(url), std::forward<U>(value) },
std::forward<A>(action));
}
// TODO: remove an action.
/**
* Get all the actions triggered for a particular item.
*
* \param item to be analyzed for actions.
*/
std::vector<Action>
getActionsForItem(const std::shared_ptr<const mediametrics::Item>& item) {
std::vector<Action> actions;
std::lock_guard l(mLock);
for (const auto &[trigger, action] : mFilters) {
if (isWildcardMatch(trigger, item) ==
mediametrics::Item::RECURSIVE_WILDCARD_CHECK_MATCH_FOUND) {
actions.push_back(action);
}
}
// TODO: Optimize for prefix search and wildcarding.
return actions;
}
private:
static inline bool isMatch(const Trigger& trigger,
const std::shared_ptr<const mediametrics::Item>& item) {
const auto& [key, elem] = trigger;
if (!startsWith(key, item->getKey())) return false;
// The trigger key is in format (item key).propName, so + 1 skips '.' delimeter.
const char *propName = key.c_str() + item->getKey().size() + 1;
return item->hasPropElem(propName, elem);
}
static inline int isWildcardMatch(const Trigger& trigger,
const std::shared_ptr<const mediametrics::Item>& item) {
const auto& [key, elem] = trigger;
return item->recursiveWildcardCheckElem(key.c_str(), elem);
}
mutable std::mutex mLock;
using FilterType = std::multimap<Trigger, Action>;
FilterType mFilters GUARDED_BY(mLock);
};
} // namespace android::mediametrics