1 /* 2 * Copyright (C) 2020 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 #pragma once 18 19 #include <android-base/thread_annotations.h> 20 #include <media/MediaMetricsItem.h> 21 #include <mutex> 22 23 namespace android::mediametrics { 24 25 /** 26 * AnalyticsActions consists of a map of pairs <trigger, action> which 27 * are evaluated for a given incoming MediaMetrics item. 28 * 29 * A vector of Actions are returned from getActionsForItem() which 30 * should be executed outside of any locks. 31 * 32 * Mediametrics assumes weak consistency, which is fine as the analytics database 33 * is generally strictly increasing in size (until gc removes values that are 34 * supposedly no longer needed). 35 */ 36 37 class AnalyticsActions { 38 public: 39 40 using Elem = mediametrics::Item::Prop::Elem; 41 /** 42 * Trigger: a pair consisting of 43 * std::string: A wildcard url specifying a property in the item, 44 * where '*' indicates 0 or more arbitrary characters 45 * for the item key match. 46 * Elem: A value that needs to match exactly. 47 * 48 * Trigger is used in a map sort; default less with std::string as primary key. 49 * The wildcard accepts a string with '*' as being 0 or more arbitrary 50 * characters for the item key match. A wildcard is preferred over general 51 * regexp for simple fast lookup. 52 * 53 * TODO: incorporate a regexp option. 54 */ 55 using Trigger = std::pair<std::string, Elem>; 56 57 /** 58 * Function: The function to be executed. 59 */ 60 using Function = std::function< 61 void(const std::shared_ptr<const mediametrics::Item>& item)>; 62 63 /** 64 * Action: An action to execute. This is a shared pointer to Function. 65 */ 66 using Action = std::shared_ptr<Function>; 67 68 /** 69 * Adds a new action. 70 * 71 * \param url references a property in the item with wildcards 72 * \param value references a value (cast to Elem automatically) 73 * so be careful of the type. It must be one of 74 * the types acceptable to Elem. 75 * \param action is a function or lambda to execute if the url matches value 76 * in the item. 77 */ 78 template <typename T, typename U, typename A> addAction(T && url,U && value,A && action)79 void addAction(T&& url, U&& value, A&& action) { 80 std::lock_guard l(mLock); 81 mFilters.emplace(Trigger{ std::forward<T>(url), std::forward<U>(value) }, 82 std::forward<A>(action)); 83 } 84 85 // TODO: remove an action. 86 87 /** 88 * Get all the actions triggered for a particular item. 89 * 90 * \param item to be analyzed for actions. 91 */ 92 std::vector<Action> getActionsForItem(const std::shared_ptr<const mediametrics::Item> & item)93 getActionsForItem(const std::shared_ptr<const mediametrics::Item>& item) { 94 std::vector<Action> actions; 95 std::lock_guard l(mLock); 96 97 for (const auto &[trigger, action] : mFilters) { 98 if (isWildcardMatch(trigger, item) == 99 mediametrics::Item::RECURSIVE_WILDCARD_CHECK_MATCH_FOUND) { 100 actions.push_back(action); 101 } 102 } 103 104 // TODO: Optimize for prefix search and wildcarding. 105 106 return actions; 107 } 108 109 private: 110 isMatch(const Trigger & trigger,const std::shared_ptr<const mediametrics::Item> & item)111 static inline bool isMatch(const Trigger& trigger, 112 const std::shared_ptr<const mediametrics::Item>& item) { 113 const auto& [key, elem] = trigger; 114 if (!startsWith(key, item->getKey())) return false; 115 // The trigger key is in format (item key).propName, so + 1 skips '.' delimeter. 116 const char *propName = key.c_str() + item->getKey().size() + 1; 117 return item->hasPropElem(propName, elem); 118 } 119 isWildcardMatch(const Trigger & trigger,const std::shared_ptr<const mediametrics::Item> & item)120 static inline int isWildcardMatch(const Trigger& trigger, 121 const std::shared_ptr<const mediametrics::Item>& item) { 122 const auto& [key, elem] = trigger; 123 return item->recursiveWildcardCheckElem(key.c_str(), elem); 124 } 125 126 mutable std::mutex mLock; 127 128 using FilterType = std::multimap<Trigger, Action>; 129 FilterType mFilters GUARDED_BY(mLock); 130 }; 131 132 } // namespace android::mediametrics 133