/* * 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. */ #define DEBUG false // STOPSHIP if true #include "Log.h" #include "config_update_utils.h" #include "external/StatsPullerManager.h" #include "hash.h" #include "matchers/EventMatcherWizard.h" #include "metrics_manager_util.h" using google::protobuf::MessageLite; namespace android { namespace os { namespace statsd { // Recursive function to determine if a matcher needs to be updated. Populates matcherToUpdate. // Returns whether the function was successful or not. bool determineMatcherUpdateStatus(const StatsdConfig& config, const int matcherIdx, const unordered_map& oldAtomMatchingTrackerMap, const vector>& oldAtomMatchingTrackers, const unordered_map& newAtomMatchingTrackerMap, vector& matchersToUpdate, vector& cycleTracker) { // Have already examined this matcher. if (matchersToUpdate[matcherIdx] != UPDATE_UNKNOWN) { return true; } const AtomMatcher& matcher = config.atom_matcher(matcherIdx); int64_t id = matcher.id(); // Check if new matcher. const auto& oldAtomMatchingTrackerIt = oldAtomMatchingTrackerMap.find(id); if (oldAtomMatchingTrackerIt == oldAtomMatchingTrackerMap.end()) { matchersToUpdate[matcherIdx] = UPDATE_NEW; return true; } // This is an existing matcher. Check if it has changed. string serializedMatcher; if (!matcher.SerializeToString(&serializedMatcher)) { ALOGE("Unable to serialize matcher %lld", (long long)id); return false; } uint64_t newProtoHash = Hash64(serializedMatcher); if (newProtoHash != oldAtomMatchingTrackers[oldAtomMatchingTrackerIt->second]->getProtoHash()) { matchersToUpdate[matcherIdx] = UPDATE_REPLACE; return true; } switch (matcher.contents_case()) { case AtomMatcher::ContentsCase::kSimpleAtomMatcher: { matchersToUpdate[matcherIdx] = UPDATE_PRESERVE; return true; } case AtomMatcher::ContentsCase::kCombination: { // Recurse to check if children have changed. cycleTracker[matcherIdx] = true; UpdateStatus status = UPDATE_PRESERVE; for (const int64_t childMatcherId : matcher.combination().matcher()) { const auto& childIt = newAtomMatchingTrackerMap.find(childMatcherId); if (childIt == newAtomMatchingTrackerMap.end()) { ALOGW("Matcher %lld not found in the config", (long long)childMatcherId); return false; } const int childIdx = childIt->second; if (cycleTracker[childIdx]) { ALOGE("Cycle detected in matcher config"); return false; } if (!determineMatcherUpdateStatus( config, childIdx, oldAtomMatchingTrackerMap, oldAtomMatchingTrackers, newAtomMatchingTrackerMap, matchersToUpdate, cycleTracker)) { return false; } if (matchersToUpdate[childIdx] == UPDATE_REPLACE) { status = UPDATE_REPLACE; break; } } matchersToUpdate[matcherIdx] = status; cycleTracker[matcherIdx] = false; return true; } default: { ALOGE("Matcher \"%lld\" malformed", (long long)id); return false; } } return true; } bool updateAtomMatchingTrackers(const StatsdConfig& config, const sp& uidMap, const unordered_map& oldAtomMatchingTrackerMap, const vector>& oldAtomMatchingTrackers, set& allTagIds, unordered_map& newAtomMatchingTrackerMap, vector>& newAtomMatchingTrackers, set& replacedMatchers) { const int atomMatcherCount = config.atom_matcher_size(); vector matcherProtos; matcherProtos.reserve(atomMatcherCount); newAtomMatchingTrackers.reserve(atomMatcherCount); // Maps matcher id to their position in the config. For fast lookup of dependencies. for (int i = 0; i < atomMatcherCount; i++) { const AtomMatcher& matcher = config.atom_matcher(i); if (newAtomMatchingTrackerMap.find(matcher.id()) != newAtomMatchingTrackerMap.end()) { ALOGE("Duplicate atom matcher found for id %lld", (long long)matcher.id()); return false; } newAtomMatchingTrackerMap[matcher.id()] = i; matcherProtos.push_back(matcher); } // For combination matchers, we need to determine if any children need to be updated. vector matchersToUpdate(atomMatcherCount, UPDATE_UNKNOWN); vector cycleTracker(atomMatcherCount, false); for (int i = 0; i < atomMatcherCount; i++) { if (!determineMatcherUpdateStatus(config, i, oldAtomMatchingTrackerMap, oldAtomMatchingTrackers, newAtomMatchingTrackerMap, matchersToUpdate, cycleTracker)) { return false; } } for (int i = 0; i < atomMatcherCount; i++) { const AtomMatcher& matcher = config.atom_matcher(i); const int64_t id = matcher.id(); switch (matchersToUpdate[i]) { case UPDATE_PRESERVE: { const auto& oldAtomMatchingTrackerIt = oldAtomMatchingTrackerMap.find(id); if (oldAtomMatchingTrackerIt == oldAtomMatchingTrackerMap.end()) { ALOGE("Could not find AtomMatcher %lld in the previous config, but expected it " "to be there", (long long)id); return false; } const sp& tracker = oldAtomMatchingTrackers[oldAtomMatchingTrackerIt->second]; if (!tracker->onConfigUpdated(matcherProtos[i], i, newAtomMatchingTrackerMap)) { ALOGW("Config update failed for matcher %lld", (long long)id); return false; } newAtomMatchingTrackers.push_back(tracker); break; } case UPDATE_REPLACE: replacedMatchers.insert(id); [[fallthrough]]; // Intentionally fallthrough to create the new matcher. case UPDATE_NEW: { sp tracker = createAtomMatchingTracker(matcher, i, uidMap); if (tracker == nullptr) { return false; } newAtomMatchingTrackers.push_back(tracker); break; } default: { ALOGE("Matcher \"%lld\" update state is unknown. This should never happen", (long long)id); return false; } } } std::fill(cycleTracker.begin(), cycleTracker.end(), false); for (auto& matcher : newAtomMatchingTrackers) { if (!matcher->init(matcherProtos, newAtomMatchingTrackers, newAtomMatchingTrackerMap, cycleTracker)) { return false; } // Collect all the tag ids that are interesting. TagIds exist in leaf nodes only. const set& tagIds = matcher->getAtomIds(); allTagIds.insert(tagIds.begin(), tagIds.end()); } return true; } // Recursive function to determine if a condition needs to be updated. Populates conditionsToUpdate. // Returns whether the function was successful or not. bool determineConditionUpdateStatus(const StatsdConfig& config, const int conditionIdx, const unordered_map& oldConditionTrackerMap, const vector>& oldConditionTrackers, const unordered_map& newConditionTrackerMap, const set& replacedMatchers, vector& conditionsToUpdate, vector& cycleTracker) { // Have already examined this condition. if (conditionsToUpdate[conditionIdx] != UPDATE_UNKNOWN) { return true; } const Predicate& predicate = config.predicate(conditionIdx); int64_t id = predicate.id(); // Check if new condition. const auto& oldConditionTrackerIt = oldConditionTrackerMap.find(id); if (oldConditionTrackerIt == oldConditionTrackerMap.end()) { conditionsToUpdate[conditionIdx] = UPDATE_NEW; return true; } // This is an existing condition. Check if it has changed. string serializedCondition; if (!predicate.SerializeToString(&serializedCondition)) { ALOGE("Unable to serialize matcher %lld", (long long)id); return false; } uint64_t newProtoHash = Hash64(serializedCondition); if (newProtoHash != oldConditionTrackers[oldConditionTrackerIt->second]->getProtoHash()) { conditionsToUpdate[conditionIdx] = UPDATE_REPLACE; return true; } switch (predicate.contents_case()) { case Predicate::ContentsCase::kSimplePredicate: { // Need to check if any of the underlying matchers changed. const SimplePredicate& simplePredicate = predicate.simple_predicate(); if (simplePredicate.has_start()) { if (replacedMatchers.find(simplePredicate.start()) != replacedMatchers.end()) { conditionsToUpdate[conditionIdx] = UPDATE_REPLACE; return true; } } if (simplePredicate.has_stop()) { if (replacedMatchers.find(simplePredicate.stop()) != replacedMatchers.end()) { conditionsToUpdate[conditionIdx] = UPDATE_REPLACE; return true; } } if (simplePredicate.has_stop_all()) { if (replacedMatchers.find(simplePredicate.stop_all()) != replacedMatchers.end()) { conditionsToUpdate[conditionIdx] = UPDATE_REPLACE; return true; } } conditionsToUpdate[conditionIdx] = UPDATE_PRESERVE; return true; } case Predicate::ContentsCase::kCombination: { // Need to recurse on the children to see if any of the child predicates changed. cycleTracker[conditionIdx] = true; UpdateStatus status = UPDATE_PRESERVE; for (const int64_t childPredicateId : predicate.combination().predicate()) { const auto& childIt = newConditionTrackerMap.find(childPredicateId); if (childIt == newConditionTrackerMap.end()) { ALOGW("Predicate %lld not found in the config", (long long)childPredicateId); return false; } const int childIdx = childIt->second; if (cycleTracker[childIdx]) { ALOGE("Cycle detected in predicate config"); return false; } if (!determineConditionUpdateStatus(config, childIdx, oldConditionTrackerMap, oldConditionTrackers, newConditionTrackerMap, replacedMatchers, conditionsToUpdate, cycleTracker)) { return false; } if (conditionsToUpdate[childIdx] == UPDATE_REPLACE) { status = UPDATE_REPLACE; break; } } conditionsToUpdate[conditionIdx] = status; cycleTracker[conditionIdx] = false; return true; } default: { ALOGE("Predicate \"%lld\" malformed", (long long)id); return false; } } return true; } bool updateConditions(const ConfigKey& key, const StatsdConfig& config, const unordered_map& atomMatchingTrackerMap, const set& replacedMatchers, const unordered_map& oldConditionTrackerMap, const vector>& oldConditionTrackers, unordered_map& newConditionTrackerMap, vector>& newConditionTrackers, unordered_map>& trackerToConditionMap, vector& conditionCache, set& replacedConditions) { vector conditionProtos; const int conditionTrackerCount = config.predicate_size(); conditionProtos.reserve(conditionTrackerCount); newConditionTrackers.reserve(conditionTrackerCount); conditionCache.assign(conditionTrackerCount, ConditionState::kNotEvaluated); for (int i = 0; i < conditionTrackerCount; i++) { const Predicate& condition = config.predicate(i); if (newConditionTrackerMap.find(condition.id()) != newConditionTrackerMap.end()) { ALOGE("Duplicate Predicate found!"); return false; } newConditionTrackerMap[condition.id()] = i; conditionProtos.push_back(condition); } vector conditionsToUpdate(conditionTrackerCount, UPDATE_UNKNOWN); vector cycleTracker(conditionTrackerCount, false); for (int i = 0; i < conditionTrackerCount; i++) { if (!determineConditionUpdateStatus(config, i, oldConditionTrackerMap, oldConditionTrackers, newConditionTrackerMap, replacedMatchers, conditionsToUpdate, cycleTracker)) { return false; } } // Update status has been determined for all conditions. Now perform the update. set preservedConditions; for (int i = 0; i < conditionTrackerCount; i++) { const Predicate& predicate = config.predicate(i); const int64_t id = predicate.id(); switch (conditionsToUpdate[i]) { case UPDATE_PRESERVE: { preservedConditions.insert(i); const auto& oldConditionTrackerIt = oldConditionTrackerMap.find(id); if (oldConditionTrackerIt == oldConditionTrackerMap.end()) { ALOGE("Could not find Predicate %lld in the previous config, but expected it " "to be there", (long long)id); return false; } const int oldIndex = oldConditionTrackerIt->second; newConditionTrackers.push_back(oldConditionTrackers[oldIndex]); break; } case UPDATE_REPLACE: replacedConditions.insert(id); [[fallthrough]]; // Intentionally fallthrough to create the new condition tracker. case UPDATE_NEW: { sp tracker = createConditionTracker(key, predicate, i, atomMatchingTrackerMap); if (tracker == nullptr) { return false; } newConditionTrackers.push_back(tracker); break; } default: { ALOGE("Condition \"%lld\" update state is unknown. This should never happen", (long long)id); return false; } } } // Update indices of preserved predicates. for (const int conditionIndex : preservedConditions) { if (!newConditionTrackers[conditionIndex]->onConfigUpdated( conditionProtos, conditionIndex, newConditionTrackers, atomMatchingTrackerMap, newConditionTrackerMap)) { ALOGE("Failed to update condition %lld", (long long)newConditionTrackers[conditionIndex]->getConditionId()); return false; } } std::fill(cycleTracker.begin(), cycleTracker.end(), false); for (int conditionIndex = 0; conditionIndex < conditionTrackerCount; conditionIndex++) { const sp& conditionTracker = newConditionTrackers[conditionIndex]; // Calling init on preserved conditions is OK. It is needed to fill the condition cache. if (!conditionTracker->init(conditionProtos, newConditionTrackers, newConditionTrackerMap, cycleTracker, conditionCache)) { return false; } for (const int trackerIndex : conditionTracker->getAtomMatchingTrackerIndex()) { vector& conditionList = trackerToConditionMap[trackerIndex]; conditionList.push_back(conditionIndex); } } return true; } bool updateStates(const StatsdConfig& config, const map& oldStateProtoHashes, unordered_map& stateAtomIdMap, unordered_map>& allStateGroupMaps, map& newStateProtoHashes, set& replacedStates) { // Share with metrics_manager_util. if (!initStates(config, stateAtomIdMap, allStateGroupMaps, newStateProtoHashes)) { return false; } for (const auto& [stateId, stateHash] : oldStateProtoHashes) { const auto& it = newStateProtoHashes.find(stateId); if (it != newStateProtoHashes.end() && it->second != stateHash) { replacedStates.insert(stateId); } } return true; } // Returns true if any matchers in the metric activation were replaced. bool metricActivationDepsChange(const StatsdConfig& config, const unordered_map& metricToActivationMap, const int64_t metricId, const set& replacedMatchers) { const auto& metricActivationIt = metricToActivationMap.find(metricId); if (metricActivationIt == metricToActivationMap.end()) { return false; } const MetricActivation& metricActivation = config.metric_activation(metricActivationIt->second); for (int i = 0; i < metricActivation.event_activation_size(); i++) { const EventActivation& activation = metricActivation.event_activation(i); if (replacedMatchers.find(activation.atom_matcher_id()) != replacedMatchers.end()) { return true; } if (activation.has_deactivation_atom_matcher_id()) { if (replacedMatchers.find(activation.deactivation_atom_matcher_id()) != replacedMatchers.end()) { return true; } } } return false; } bool determineMetricUpdateStatus( const StatsdConfig& config, const MessageLite& metric, const int64_t metricId, const MetricType metricType, const set& matcherDependencies, const set& conditionDependencies, const ::google::protobuf::RepeatedField& stateDependencies, const ::google::protobuf::RepeatedPtrField& conditionLinks, const unordered_map& oldMetricProducerMap, const vector>& oldMetricProducers, const unordered_map& metricToActivationMap, const set& replacedMatchers, const set& replacedConditions, const set& replacedStates, UpdateStatus& updateStatus) { // Check if new metric const auto& oldMetricProducerIt = oldMetricProducerMap.find(metricId); if (oldMetricProducerIt == oldMetricProducerMap.end()) { updateStatus = UPDATE_NEW; return true; } // This is an existing metric, check if it has changed. uint64_t metricHash; if (!getMetricProtoHash(config, metric, metricId, metricToActivationMap, metricHash)) { return false; } const sp oldMetricProducer = oldMetricProducers[oldMetricProducerIt->second]; if (oldMetricProducer->getMetricType() != metricType || oldMetricProducer->getProtoHash() != metricHash) { updateStatus = UPDATE_REPLACE; return true; } // Take intersections of the matchers/predicates/states that the metric // depends on with those that have been replaced. If a metric depends on any // replaced component, it too must be replaced. set intersection; set_intersection(matcherDependencies.begin(), matcherDependencies.end(), replacedMatchers.begin(), replacedMatchers.end(), inserter(intersection, intersection.begin())); if (intersection.size() > 0) { updateStatus = UPDATE_REPLACE; return true; } set_intersection(conditionDependencies.begin(), conditionDependencies.end(), replacedConditions.begin(), replacedConditions.end(), inserter(intersection, intersection.begin())); if (intersection.size() > 0) { updateStatus = UPDATE_REPLACE; return true; } set_intersection(stateDependencies.begin(), stateDependencies.end(), replacedStates.begin(), replacedStates.end(), inserter(intersection, intersection.begin())); if (intersection.size() > 0) { updateStatus = UPDATE_REPLACE; return true; } for (const auto& metricConditionLink : conditionLinks) { if (replacedConditions.find(metricConditionLink.condition()) != replacedConditions.end()) { updateStatus = UPDATE_REPLACE; return true; } } if (metricActivationDepsChange(config, metricToActivationMap, metricId, replacedMatchers)) { updateStatus = UPDATE_REPLACE; return true; } updateStatus = UPDATE_PRESERVE; return true; } bool determineAllMetricUpdateStatuses(const StatsdConfig& config, const unordered_map& oldMetricProducerMap, const vector>& oldMetricProducers, const unordered_map& metricToActivationMap, const set& replacedMatchers, const set& replacedConditions, const set& replacedStates, vector& metricsToUpdate) { int metricIndex = 0; for (int i = 0; i < config.count_metric_size(); i++, metricIndex++) { const CountMetric& metric = config.count_metric(i); set conditionDependencies; if (metric.has_condition()) { conditionDependencies.insert(metric.condition()); } if (!determineMetricUpdateStatus( config, metric, metric.id(), METRIC_TYPE_COUNT, {metric.what()}, conditionDependencies, metric.slice_by_state(), metric.links(), oldMetricProducerMap, oldMetricProducers, metricToActivationMap, replacedMatchers, replacedConditions, replacedStates, metricsToUpdate[metricIndex])) { return false; } } for (int i = 0; i < config.duration_metric_size(); i++, metricIndex++) { const DurationMetric& metric = config.duration_metric(i); set conditionDependencies({metric.what()}); if (metric.has_condition()) { conditionDependencies.insert(metric.condition()); } if (!determineMetricUpdateStatus( config, metric, metric.id(), METRIC_TYPE_DURATION, /*matcherDependencies=*/{}, conditionDependencies, metric.slice_by_state(), metric.links(), oldMetricProducerMap, oldMetricProducers, metricToActivationMap, replacedMatchers, replacedConditions, replacedStates, metricsToUpdate[metricIndex])) { return false; } } for (int i = 0; i < config.event_metric_size(); i++, metricIndex++) { const EventMetric& metric = config.event_metric(i); set conditionDependencies; if (metric.has_condition()) { conditionDependencies.insert(metric.condition()); } if (!determineMetricUpdateStatus( config, metric, metric.id(), METRIC_TYPE_EVENT, {metric.what()}, conditionDependencies, ::google::protobuf::RepeatedField(), metric.links(), oldMetricProducerMap, oldMetricProducers, metricToActivationMap, replacedMatchers, replacedConditions, replacedStates, metricsToUpdate[metricIndex])) { return false; } } for (int i = 0; i < config.value_metric_size(); i++, metricIndex++) { const ValueMetric& metric = config.value_metric(i); set conditionDependencies; if (metric.has_condition()) { conditionDependencies.insert(metric.condition()); } if (!determineMetricUpdateStatus( config, metric, metric.id(), METRIC_TYPE_VALUE, {metric.what()}, conditionDependencies, metric.slice_by_state(), metric.links(), oldMetricProducerMap, oldMetricProducers, metricToActivationMap, replacedMatchers, replacedConditions, replacedStates, metricsToUpdate[metricIndex])) { return false; } } for (int i = 0; i < config.gauge_metric_size(); i++, metricIndex++) { const GaugeMetric& metric = config.gauge_metric(i); set conditionDependencies; if (metric.has_condition()) { conditionDependencies.insert(metric.condition()); } set matcherDependencies({metric.what()}); if (metric.has_trigger_event()) { matcherDependencies.insert(metric.trigger_event()); } if (!determineMetricUpdateStatus( config, metric, metric.id(), METRIC_TYPE_GAUGE, matcherDependencies, conditionDependencies, ::google::protobuf::RepeatedField(), metric.links(), oldMetricProducerMap, oldMetricProducers, metricToActivationMap, replacedMatchers, replacedConditions, replacedStates, metricsToUpdate[metricIndex])) { return false; } } return true; } // Called when a metric is preserved during a config update. Finds the metric in oldMetricProducers // and calls onConfigUpdated to update all indices. optional> updateMetric( const StatsdConfig& config, const int configIndex, const int metricIndex, const int64_t metricId, const vector>& allAtomMatchingTrackers, const unordered_map& oldAtomMatchingTrackerMap, const unordered_map& newAtomMatchingTrackerMap, const sp& matcherWizard, const vector>& allConditionTrackers, const unordered_map& conditionTrackerMap, const sp& wizard, const unordered_map& oldMetricProducerMap, const vector>& oldMetricProducers, const unordered_map& metricToActivationMap, unordered_map>& trackerToMetricMap, unordered_map>& conditionToMetricMap, unordered_map>& activationAtomTrackerToMetricMap, unordered_map>& deactivationAtomTrackerToMetricMap, vector& metricsWithActivation) { const auto& oldMetricProducerIt = oldMetricProducerMap.find(metricId); if (oldMetricProducerIt == oldMetricProducerMap.end()) { ALOGE("Could not find Metric %lld in the previous config, but expected it " "to be there", (long long)metricId); return nullopt; } const int oldIndex = oldMetricProducerIt->second; sp producer = oldMetricProducers[oldIndex]; if (!producer->onConfigUpdated(config, configIndex, metricIndex, allAtomMatchingTrackers, oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, matcherWizard, allConditionTrackers, conditionTrackerMap, wizard, metricToActivationMap, trackerToMetricMap, conditionToMetricMap, activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, metricsWithActivation)) { return nullopt; } return {producer}; } bool updateMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs, const int64_t currentTimeNs, const sp& pullerManager, const unordered_map& oldAtomMatchingTrackerMap, const unordered_map& newAtomMatchingTrackerMap, const set& replacedMatchers, const vector>& allAtomMatchingTrackers, const unordered_map& conditionTrackerMap, const set& replacedConditions, vector>& allConditionTrackers, const vector& initialConditionCache, const unordered_map& stateAtomIdMap, const unordered_map>& allStateGroupMaps, const set& replacedStates, const unordered_map& oldMetricProducerMap, const vector>& oldMetricProducers, unordered_map& newMetricProducerMap, vector>& newMetricProducers, unordered_map>& conditionToMetricMap, unordered_map>& trackerToMetricMap, set& noReportMetricIds, unordered_map>& activationAtomTrackerToMetricMap, unordered_map>& deactivationAtomTrackerToMetricMap, vector& metricsWithActivation, set& replacedMetrics) { sp wizard = new ConditionWizard(allConditionTrackers); sp matcherWizard = new EventMatcherWizard(allAtomMatchingTrackers); const int allMetricsCount = config.count_metric_size() + config.duration_metric_size() + config.event_metric_size() + config.gauge_metric_size() + config.value_metric_size(); newMetricProducers.reserve(allMetricsCount); // Construct map from metric id to metric activation index. The map will be used to determine // the metric activation corresponding to a metric. unordered_map metricToActivationMap; for (int i = 0; i < config.metric_activation_size(); i++) { const MetricActivation& metricActivation = config.metric_activation(i); int64_t metricId = metricActivation.metric_id(); if (metricToActivationMap.find(metricId) != metricToActivationMap.end()) { ALOGE("Metric %lld has multiple MetricActivations", (long long)metricId); return false; } metricToActivationMap.insert({metricId, i}); } vector metricsToUpdate(allMetricsCount, UPDATE_UNKNOWN); if (!determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap, replacedMatchers, replacedConditions, replacedStates, metricsToUpdate)) { return false; } // Now, perform the update. Must iterate the metric types in the same order int metricIndex = 0; for (int i = 0; i < config.count_metric_size(); i++, metricIndex++) { const CountMetric& metric = config.count_metric(i); newMetricProducerMap[metric.id()] = metricIndex; optional> producer; switch (metricsToUpdate[metricIndex]) { case UPDATE_PRESERVE: { producer = updateMetric( config, i, metricIndex, metric.id(), allAtomMatchingTrackers, oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, matcherWizard, allConditionTrackers, conditionTrackerMap, wizard, oldMetricProducerMap, oldMetricProducers, metricToActivationMap, trackerToMetricMap, conditionToMetricMap, activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, metricsWithActivation); break; } case UPDATE_REPLACE: replacedMetrics.insert(metric.id()); [[fallthrough]]; // Intentionally fallthrough to create the new metric producer. case UPDATE_NEW: { producer = createCountMetricProducerAndUpdateMetadata( key, config, timeBaseNs, currentTimeNs, metric, metricIndex, allAtomMatchingTrackers, newAtomMatchingTrackerMap, allConditionTrackers, conditionTrackerMap, initialConditionCache, wizard, stateAtomIdMap, allStateGroupMaps, metricToActivationMap, trackerToMetricMap, conditionToMetricMap, activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, metricsWithActivation); break; } default: { ALOGE("Metric \"%lld\" update state is unknown. This should never happen", (long long)metric.id()); return false; } } if (!producer) { return false; } newMetricProducers.push_back(producer.value()); } for (int i = 0; i < config.duration_metric_size(); i++, metricIndex++) { const DurationMetric& metric = config.duration_metric(i); newMetricProducerMap[metric.id()] = metricIndex; optional> producer; switch (metricsToUpdate[metricIndex]) { case UPDATE_PRESERVE: { producer = updateMetric( config, i, metricIndex, metric.id(), allAtomMatchingTrackers, oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, matcherWizard, allConditionTrackers, conditionTrackerMap, wizard, oldMetricProducerMap, oldMetricProducers, metricToActivationMap, trackerToMetricMap, conditionToMetricMap, activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, metricsWithActivation); break; } case UPDATE_REPLACE: replacedMetrics.insert(metric.id()); [[fallthrough]]; // Intentionally fallthrough to create the new metric producer. case UPDATE_NEW: { producer = createDurationMetricProducerAndUpdateMetadata( key, config, timeBaseNs, currentTimeNs, metric, metricIndex, allAtomMatchingTrackers, newAtomMatchingTrackerMap, allConditionTrackers, conditionTrackerMap, initialConditionCache, wizard, stateAtomIdMap, allStateGroupMaps, metricToActivationMap, trackerToMetricMap, conditionToMetricMap, activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, metricsWithActivation); break; } default: { ALOGE("Metric \"%lld\" update state is unknown. This should never happen", (long long)metric.id()); return false; } } if (!producer) { return false; } newMetricProducers.push_back(producer.value()); } for (int i = 0; i < config.event_metric_size(); i++, metricIndex++) { const EventMetric& metric = config.event_metric(i); newMetricProducerMap[metric.id()] = metricIndex; optional> producer; switch (metricsToUpdate[metricIndex]) { case UPDATE_PRESERVE: { producer = updateMetric( config, i, metricIndex, metric.id(), allAtomMatchingTrackers, oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, matcherWizard, allConditionTrackers, conditionTrackerMap, wizard, oldMetricProducerMap, oldMetricProducers, metricToActivationMap, trackerToMetricMap, conditionToMetricMap, activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, metricsWithActivation); break; } case UPDATE_REPLACE: replacedMetrics.insert(metric.id()); [[fallthrough]]; // Intentionally fallthrough to create the new metric producer. case UPDATE_NEW: { producer = createEventMetricProducerAndUpdateMetadata( key, config, timeBaseNs, metric, metricIndex, allAtomMatchingTrackers, newAtomMatchingTrackerMap, allConditionTrackers, conditionTrackerMap, initialConditionCache, wizard, metricToActivationMap, trackerToMetricMap, conditionToMetricMap, activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, metricsWithActivation); break; } default: { ALOGE("Metric \"%lld\" update state is unknown. This should never happen", (long long)metric.id()); return false; } } if (!producer) { return false; } newMetricProducers.push_back(producer.value()); } for (int i = 0; i < config.value_metric_size(); i++, metricIndex++) { const ValueMetric& metric = config.value_metric(i); newMetricProducerMap[metric.id()] = metricIndex; optional> producer; switch (metricsToUpdate[metricIndex]) { case UPDATE_PRESERVE: { producer = updateMetric( config, i, metricIndex, metric.id(), allAtomMatchingTrackers, oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, matcherWizard, allConditionTrackers, conditionTrackerMap, wizard, oldMetricProducerMap, oldMetricProducers, metricToActivationMap, trackerToMetricMap, conditionToMetricMap, activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, metricsWithActivation); break; } case UPDATE_REPLACE: replacedMetrics.insert(metric.id()); [[fallthrough]]; // Intentionally fallthrough to create the new metric producer. case UPDATE_NEW: { producer = createValueMetricProducerAndUpdateMetadata( key, config, timeBaseNs, currentTimeNs, pullerManager, metric, metricIndex, allAtomMatchingTrackers, newAtomMatchingTrackerMap, allConditionTrackers, conditionTrackerMap, initialConditionCache, wizard, matcherWizard, stateAtomIdMap, allStateGroupMaps, metricToActivationMap, trackerToMetricMap, conditionToMetricMap, activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, metricsWithActivation); break; } default: { ALOGE("Metric \"%lld\" update state is unknown. This should never happen", (long long)metric.id()); return false; } } if (!producer) { return false; } newMetricProducers.push_back(producer.value()); } for (int i = 0; i < config.gauge_metric_size(); i++, metricIndex++) { const GaugeMetric& metric = config.gauge_metric(i); newMetricProducerMap[metric.id()] = metricIndex; optional> producer; switch (metricsToUpdate[metricIndex]) { case UPDATE_PRESERVE: { producer = updateMetric( config, i, metricIndex, metric.id(), allAtomMatchingTrackers, oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, matcherWizard, allConditionTrackers, conditionTrackerMap, wizard, oldMetricProducerMap, oldMetricProducers, metricToActivationMap, trackerToMetricMap, conditionToMetricMap, activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, metricsWithActivation); break; } case UPDATE_REPLACE: replacedMetrics.insert(metric.id()); [[fallthrough]]; // Intentionally fallthrough to create the new metric producer. case UPDATE_NEW: { producer = createGaugeMetricProducerAndUpdateMetadata( key, config, timeBaseNs, currentTimeNs, pullerManager, metric, metricIndex, allAtomMatchingTrackers, newAtomMatchingTrackerMap, allConditionTrackers, conditionTrackerMap, initialConditionCache, wizard, matcherWizard, metricToActivationMap, trackerToMetricMap, conditionToMetricMap, activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, metricsWithActivation); break; } default: { ALOGE("Metric \"%lld\" update state is unknown. This should never happen", (long long)metric.id()); return false; } } if (!producer) { return false; } newMetricProducers.push_back(producer.value()); } for (int i = 0; i < config.no_report_metric_size(); ++i) { const int64_t noReportMetric = config.no_report_metric(i); if (newMetricProducerMap.find(noReportMetric) == newMetricProducerMap.end()) { ALOGW("no_report_metric %" PRId64 " not exist", noReportMetric); return false; } noReportMetricIds.insert(noReportMetric); } const set atomsAllowedFromAnyUid(config.whitelisted_atom_ids().begin(), config.whitelisted_atom_ids().end()); for (int i = 0; i < allMetricsCount; i++) { sp producer = newMetricProducers[i]; // Register metrics to StateTrackers for (int atomId : producer->getSlicedStateAtoms()) { // Register listener for atoms that use allowed_log_sources. // Using atoms allowed from any uid as a sliced state atom is not allowed. // Redo this check for all metrics in case the atoms allowed from any uid changed. if (atomsAllowedFromAnyUid.find(atomId) != atomsAllowedFromAnyUid.end()) { return false; // Preserved metrics should've already registered.` } else if (metricsToUpdate[i] != UPDATE_PRESERVE) { StateManager::getInstance().registerListener(atomId, producer); } } } // Init new/replaced metrics. for (size_t i = 0; i < newMetricProducers.size(); i++) { if (metricsToUpdate[i] == UPDATE_REPLACE || metricsToUpdate[i] == UPDATE_NEW) { newMetricProducers[i]->prepareFirstBucket(); } } return true; } bool determineAlertUpdateStatus(const Alert& alert, const unordered_map& oldAlertTrackerMap, const vector>& oldAnomalyTrackers, const set& replacedMetrics, UpdateStatus& updateStatus) { // Check if new alert. const auto& oldAnomalyTrackerIt = oldAlertTrackerMap.find(alert.id()); if (oldAnomalyTrackerIt == oldAlertTrackerMap.end()) { updateStatus = UPDATE_NEW; return true; } // This is an existing alert, check if it has changed. string serializedAlert; if (!alert.SerializeToString(&serializedAlert)) { ALOGW("Unable to serialize alert %lld", (long long)alert.id()); return false; } uint64_t newProtoHash = Hash64(serializedAlert); const auto [success, oldProtoHash] = oldAnomalyTrackers[oldAnomalyTrackerIt->second]->getProtoHash(); if (!success) { return false; } if (newProtoHash != oldProtoHash) { updateStatus = UPDATE_REPLACE; return true; } // Check if the metric this alert relies on has changed. if (replacedMetrics.find(alert.metric_id()) != replacedMetrics.end()) { updateStatus = UPDATE_REPLACE; return true; } updateStatus = UPDATE_PRESERVE; return true; } bool updateAlerts(const StatsdConfig& config, const int64_t currentTimeNs, const unordered_map& metricProducerMap, const set& replacedMetrics, const unordered_map& oldAlertTrackerMap, const vector>& oldAnomalyTrackers, const sp& anomalyAlarmMonitor, vector>& allMetricProducers, unordered_map& newAlertTrackerMap, vector>& newAnomalyTrackers) { int alertCount = config.alert_size(); vector alertUpdateStatuses(alertCount); for (int i = 0; i < alertCount; i++) { if (!determineAlertUpdateStatus(config.alert(i), oldAlertTrackerMap, oldAnomalyTrackers, replacedMetrics, alertUpdateStatuses[i])) { return false; } } for (int i = 0; i < alertCount; i++) { const Alert& alert = config.alert(i); newAlertTrackerMap[alert.id()] = newAnomalyTrackers.size(); switch (alertUpdateStatuses[i]) { case UPDATE_PRESERVE: { // Find the alert and update it. const auto& oldAnomalyTrackerIt = oldAlertTrackerMap.find(alert.id()); if (oldAnomalyTrackerIt == oldAlertTrackerMap.end()) { ALOGW("Could not find AnomalyTracker %lld in the previous config, but " "expected it to be there", (long long)alert.id()); return false; } sp anomalyTracker = oldAnomalyTrackers[oldAnomalyTrackerIt->second]; anomalyTracker->onConfigUpdated(); // Add the alert to the relevant metric. const auto& metricProducerIt = metricProducerMap.find(alert.metric_id()); if (metricProducerIt == metricProducerMap.end()) { ALOGW("alert \"%lld\" has unknown metric id: \"%lld\"", (long long)alert.id(), (long long)alert.metric_id()); return false; } allMetricProducers[metricProducerIt->second]->addAnomalyTracker(anomalyTracker, currentTimeNs); newAnomalyTrackers.push_back(anomalyTracker); break; } case UPDATE_REPLACE: case UPDATE_NEW: { optional> anomalyTracker = createAnomalyTracker(alert, anomalyAlarmMonitor, alertUpdateStatuses[i], currentTimeNs, metricProducerMap, allMetricProducers); if (!anomalyTracker) { return false; } newAnomalyTrackers.push_back(anomalyTracker.value()); break; } default: { ALOGE("Alert \"%lld\" update state is unknown. This should never happen", (long long)alert.id()); return false; } } } if (!initSubscribersForSubscriptionType(config, Subscription::ALERT, newAlertTrackerMap, newAnomalyTrackers)) { return false; } return true; } bool updateStatsdConfig(const ConfigKey& key, const StatsdConfig& config, const sp& uidMap, const sp& pullerManager, const sp& anomalyAlarmMonitor, const sp& periodicAlarmMonitor, const int64_t timeBaseNs, const int64_t currentTimeNs, const vector>& oldAtomMatchingTrackers, const unordered_map& oldAtomMatchingTrackerMap, const vector>& oldConditionTrackers, const unordered_map& oldConditionTrackerMap, const vector>& oldMetricProducers, const unordered_map& oldMetricProducerMap, const vector>& oldAnomalyTrackers, const unordered_map& oldAlertTrackerMap, const map& oldStateProtoHashes, set& allTagIds, vector>& newAtomMatchingTrackers, unordered_map& newAtomMatchingTrackerMap, vector>& newConditionTrackers, unordered_map& newConditionTrackerMap, vector>& newMetricProducers, unordered_map& newMetricProducerMap, vector>& newAnomalyTrackers, unordered_map& newAlertTrackerMap, vector>& newPeriodicAlarmTrackers, unordered_map>& conditionToMetricMap, unordered_map>& trackerToMetricMap, unordered_map>& trackerToConditionMap, unordered_map>& activationTrackerToMetricMap, unordered_map>& deactivationTrackerToMetricMap, vector& metricsWithActivation, map& newStateProtoHashes, set& noReportMetricIds) { set replacedMatchers; set replacedConditions; set replacedStates; set replacedMetrics; vector conditionCache; unordered_map stateAtomIdMap; unordered_map> allStateGroupMaps; if (!updateAtomMatchingTrackers(config, uidMap, oldAtomMatchingTrackerMap, oldAtomMatchingTrackers, allTagIds, newAtomMatchingTrackerMap, newAtomMatchingTrackers, replacedMatchers)) { ALOGE("updateAtomMatchingTrackers failed"); return false; } if (!updateConditions(key, config, newAtomMatchingTrackerMap, replacedMatchers, oldConditionTrackerMap, oldConditionTrackers, newConditionTrackerMap, newConditionTrackers, trackerToConditionMap, conditionCache, replacedConditions)) { ALOGE("updateConditions failed"); return false; } if (!updateStates(config, oldStateProtoHashes, stateAtomIdMap, allStateGroupMaps, newStateProtoHashes, replacedStates)) { ALOGE("updateStates failed"); return false; } if (!updateMetrics(key, config, timeBaseNs, currentTimeNs, pullerManager, oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, replacedMatchers, newAtomMatchingTrackers, newConditionTrackerMap, replacedConditions, newConditionTrackers, conditionCache, stateAtomIdMap, allStateGroupMaps, replacedStates, oldMetricProducerMap, oldMetricProducers, newMetricProducerMap, newMetricProducers, conditionToMetricMap, trackerToMetricMap, noReportMetricIds, activationTrackerToMetricMap, deactivationTrackerToMetricMap, metricsWithActivation, replacedMetrics)) { ALOGE("updateMetrics failed"); return false; } if (!updateAlerts(config, currentTimeNs, newMetricProducerMap, replacedMetrics, oldAlertTrackerMap, oldAnomalyTrackers, anomalyAlarmMonitor, newMetricProducers, newAlertTrackerMap, newAnomalyTrackers)) { ALOGE("updateAlerts failed"); return false; } // Alarms do not have any state, so we can reuse the initialization logic. if (!initAlarms(config, key, periodicAlarmMonitor, timeBaseNs, currentTimeNs, newPeriodicAlarmTrackers)) { ALOGE("initAlarms failed"); return false; } return true; } } // namespace statsd } // namespace os } // namespace android