Slice by state in CountMetricProducer

- When a log event is matched to the "what" atom of a metric,
MetricProducer parses the event for state primary key(s) and queries
StateTracker for the correct state values
- CountMetricProducer writes state information to the dump report proto
- Added e2e CountMetric tests that push events to StatsLogProcessor and
check that the resulting dump report is correct

Test: bit statsd_test:*
Bug: 136566566
Bug: 142124705
Change-Id: I9ca5b097151c0c73a617f3b16c8c3584b068cd82
diff --git a/cmds/statsd/src/metrics/MetricProducer.cpp b/cmds/statsd/src/metrics/MetricProducer.cpp
index 2a700ef..2c8f0e3 100644
--- a/cmds/statsd/src/metrics/MetricProducer.cpp
+++ b/cmds/statsd/src/metrics/MetricProducer.cpp
@@ -16,8 +16,11 @@
 
 #define DEBUG false  // STOPSHIP if true
 #include "Log.h"
+
 #include "MetricProducer.h"
 
+#include "state/StateTracker.h"
+
 using android::util::FIELD_COUNT_REPEATED;
 using android::util::FIELD_TYPE_ENUM;
 using android::util::FIELD_TYPE_INT32;
@@ -92,9 +95,43 @@
         condition = mCondition == ConditionState::kTrue;
     }
 
+    // Stores atom id to primary key pairs for each state atom that the metric is
+    // sliced by.
+    std::map<int, HashableDimensionKey> statePrimaryKeys;
+
+    // For states with primary fields, use MetricStateLinks to get the primary
+    // field values from the log event. These values will form a primary key
+    // that will be used to query StateTracker for the correct state value.
+    for (const auto& stateLink : mMetric2StateLinks) {
+        getDimensionForState(event.getValues(), stateLink,
+                             &statePrimaryKeys[stateLink.stateAtomId]);
+    }
+
+    // For each sliced state, query StateTracker for the state value using
+    // either the primary key from the previous step or the DEFAULT_DIMENSION_KEY.
+    //
+    // Expected functionality: for any case where the MetricStateLinks are
+    // initialized incorrectly (ex. # of state links != # of primary fields, no
+    // links are provided for a state with primary fields, links are provided
+    // in the wrong order, etc.), StateTracker will simply return kStateUnknown
+    // when queried using an incorrect key.
+    HashableDimensionKey stateValuesKey;
+    for (auto atomId : mSlicedStateAtoms) {
+        FieldValue value;
+        if (statePrimaryKeys.find(atomId) != statePrimaryKeys.end()) {
+            // found a primary key for this state, query using the key
+            getMappedStateValue(atomId, statePrimaryKeys[atomId], &value);
+        } else {
+            // if no MetricStateLinks exist for this state atom,
+            // query using the default dimension key (empty HashableDimensionKey)
+            getMappedStateValue(atomId, DEFAULT_DIMENSION_KEY, &value);
+        }
+        stateValuesKey.addValue(value);
+    }
+
     HashableDimensionKey dimensionInWhat;
     filterValues(mDimensionsInWhat, event.getValues(), &dimensionInWhat);
-    MetricDimensionKey metricKey(dimensionInWhat, DEFAULT_DIMENSION_KEY);
+    MetricDimensionKey metricKey(dimensionInWhat, stateValuesKey);
     onMatchedLogEventInternalLocked(
             matcherIndex, metricKey, conditionKey, condition, event);
 }
@@ -227,6 +264,31 @@
     }
 }
 
+void MetricProducer::getMappedStateValue(const int32_t atomId, const HashableDimensionKey& queryKey,
+                                         FieldValue* value) {
+    if (!StateManager::getInstance().getStateValue(atomId, queryKey, value)) {
+        value->mValue = Value(StateTracker::kStateUnknown);
+        ALOGW("StateTracker not found for state atom %d", atomId);
+        return;
+    }
+
+    // check if there is a state map for this atom
+    auto atomIt = mStateGroupMap.find(atomId);
+    if (atomIt == mStateGroupMap.end()) {
+        return;
+    }
+    auto valueIt = atomIt->second.find(value->mValue.int_value);
+    if (valueIt == atomIt->second.end()) {
+        // state map exists, but value was not put in a state group
+        // so set mValue to kStateUnknown
+        // TODO(tsaichristine): handle incomplete state maps
+        value->mValue.setInt(StateTracker::kStateUnknown);
+    } else {
+        // set mValue to group_id
+        value->mValue.setLong(valueIt->second);
+    }
+}
+
 }  // namespace statsd
 }  // namespace os
 }  // namespace android