Add LogEvent::hasAttributionChain()

Optionally, accept a std::pair<int, int>* that is populated with the
starting and ending indices of attribution nodes in FieldValues vector.

Bug: 154286011
Test: bit statsd_test:*
Change-Id: I6f19abea7fbb27712b6c2d5a7679aa4f428c191d
diff --git a/cmds/statsd/src/FieldValue.h b/cmds/statsd/src/FieldValue.h
index e251399..afb55f0 100644
--- a/cmds/statsd/src/FieldValue.h
+++ b/cmds/statsd/src/FieldValue.h
@@ -27,7 +27,6 @@
 struct Field;
 struct FieldValue;
 
-const int32_t kAttributionField = 1;
 const int32_t kMaxLogDepth = 2;
 const int32_t kLastBitMask = 0x80;
 const int32_t kClearLastBitDeco = 0x7f;
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index d914ab2..50fe47d 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -139,14 +139,13 @@
 }
 
 void StatsLogProcessor::mapIsolatedUidToHostUidIfNecessaryLocked(LogEvent* event) const {
-    if (event->getAttributionChainIndex() != -1) {
-        for (auto& value : *(event->getMutableValues())) {
-            if (value.mField.getPosAtDepth(0) > kAttributionField) {
-                break;
-            }
-            if (isAttributionUidField(value)) {
-                const int hostUid = mUidMap->getHostUidOrSelf(value.mValue.int_value);
-                value.mValue.setInt(hostUid);
+    if (std::pair<int, int> indexRange; event->hasAttributionChain(&indexRange)) {
+        vector<FieldValue>* const fieldValues = event->getMutableValues();
+        for (int i = indexRange.first; i <= indexRange.second; i++) {
+            FieldValue& fieldValue = fieldValues->at(i);
+            if (isAttributionUidField(fieldValue)) {
+                const int hostUid = mUidMap->getHostUidOrSelf(fieldValue.mValue.int_value);
+                fieldValue.mValue.setInt(hostUid);
             }
         }
     } else {
diff --git a/cmds/statsd/src/external/puller_util.cpp b/cmds/statsd/src/external/puller_util.cpp
index 9e72a23..c688133 100644
--- a/cmds/statsd/src/external/puller_util.cpp
+++ b/cmds/statsd/src/external/puller_util.cpp
@@ -51,7 +51,8 @@
                                       int tagId, const vector<int>& additiveFieldsVec) {
     // Check the first LogEvent for attribution chain or a uid field as either all atoms with this
     // tagId have them or none of them do.
-    const bool hasAttributionChain = data[0]->getAttributionChainIndex() != -1;
+    std::pair<int, int> attrIndexRange;
+    const bool hasAttributionChain = data[0]->hasAttributionChain(&attrIndexRange);
     bool hasUidField = (data[0]->getUidFieldIndex() != -1);
 
     if (!hasAttributionChain && !hasUidField) {
@@ -65,14 +66,13 @@
             ALOGE("Wrong atom. Expecting %d, got %d", tagId, event->GetTagId());
             return;
         }
-        if (event->getAttributionChainIndex() != -1) {
-            for (auto& value : *(event->getMutableValues())) {
-                if (value.mField.getPosAtDepth(0) > kAttributionField) {
-                    break;
-                }
-                if (isAttributionUidField(value)) {
-                    const int hostUid = uidMap->getHostUidOrSelf(value.mValue.int_value);
-                    value.mValue.setInt(hostUid);
+        if (hasAttributionChain) {
+            vector<FieldValue>* const fieldValues = event->getMutableValues();
+            for (int i = attrIndexRange.first; i <= attrIndexRange.second; i++) {
+                FieldValue& fieldValue = fieldValues->at(i);
+                if (isAttributionUidField(fieldValue)) {
+                    const int hostUid = uidMap->getHostUidOrSelf(fieldValue.mValue.int_value);
+                    fieldValue.mValue.setInt(hostUid);
                 }
             }
         } else {
diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp
index 61cd017..18f1b82 100644
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -219,8 +219,8 @@
 
 void LogEvent::parseAttributionChain(int32_t* pos, int32_t depth, bool* last,
                                      uint8_t numAnnotations) {
-    int firstUidInChainIndex = mValues.size();
-    int32_t numNodes = readNextValue<uint8_t>();
+    const unsigned int firstUidInChainIndex = mValues.size();
+    const int32_t numNodes = readNextValue<uint8_t>();
     for (pos[1] = 1; pos[1] <= numNodes; pos[1]++) {
         last[1] = (pos[1] == numNodes);
 
@@ -233,6 +233,11 @@
         last[2] = true;
         parseString(pos, /*depth=*/2, last, /*numAnnotations=*/0);
     }
+    // Check if at least one node was successfully parsed.
+    if (mValues.size() - 1 > firstUidInChainIndex) {
+        mAttributionChainStartIndex = firstUidInChainIndex;
+        mAttributionChainEndIndex = mValues.size() - 1;
+    }
 
     parseAnnotations(numAnnotations, firstUidInChainIndex);
 
@@ -411,7 +416,6 @@
                 break;
             case ATTRIBUTION_CHAIN_TYPE:
                 parseAttributionChain(pos, /*depth=*/0, last, getNumAnnotations(typeInfo));
-                if (mAttributionChainIndex == -1) mAttributionChainIndex = pos[0];
                 break;
             default:
                 mValid = false;
@@ -572,6 +576,19 @@
     writeFieldValueTreeToStream(mTagId, getValues(), &protoOutput);
 }
 
+bool LogEvent::hasAttributionChain(std::pair<int, int>* indexRange) const {
+    if (mAttributionChainStartIndex == -1 || mAttributionChainEndIndex == -1) {
+        return false;
+    }
+
+    if (nullptr != indexRange) {
+        indexRange->first = mAttributionChainStartIndex;
+        indexRange->second = mAttributionChainEndIndex;
+    }
+
+    return true;
+}
+
 void writeExperimentIdsToProto(const std::vector<int64_t>& experimentIds,
                                std::vector<uint8_t>* protoOut) {
     ProtoOutputStream proto;
diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h
index 41fdcc2..e7340ef 100644
--- a/cmds/statsd/src/logd/LogEvent.h
+++ b/cmds/statsd/src/logd/LogEvent.h
@@ -163,12 +163,10 @@
         return mUidFieldIndex;
     }
 
-    // Returns the index of (the first) attribution chain within the atom
-    // definition. Note that the value is 1-indexed. If there is no attribution
-    // chain, returns -1.
-    inline int getAttributionChainIndex() {
-        return mAttributionChainIndex;
-    }
+    // Returns whether this LogEvent has an AttributionChain.
+    // If it does and indexRange is not a nullptr, populate indexRange with the start and end index
+    // of the AttributionChain within mValues.
+    bool hasAttributionChain(std::pair<int, int>* indexRange = nullptr) const;
 
     // Returns the index of the exclusive state field within the FieldValues vector if
     // an exclusive state exists. If there is no exclusive state field, returns -1.
@@ -310,7 +308,8 @@
     // Annotations
     bool mTruncateTimestamp = false;
     int mUidFieldIndex = -1;
-    int mAttributionChainIndex = -1;
+    int mAttributionChainStartIndex = -1;
+    int mAttributionChainEndIndex = -1;
     int mExclusiveStateFieldIndex = -1;
 };
 
diff --git a/cmds/statsd/tests/LogEvent_test.cpp b/cmds/statsd/tests/LogEvent_test.cpp
index bb4578d..e2c2743 100644
--- a/cmds/statsd/tests/LogEvent_test.cpp
+++ b/cmds/statsd/tests/LogEvent_test.cpp
@@ -95,6 +95,7 @@
     EXPECT_EQ(100, logEvent.GetTagId());
     EXPECT_EQ(1000, logEvent.GetUid());
     EXPECT_EQ(1001, logEvent.GetPid());
+    EXPECT_FALSE(logEvent.hasAttributionChain());
 
     const vector<FieldValue>& values = logEvent.getValues();
     EXPECT_EQ(4, values.size());
@@ -143,6 +144,7 @@
     EXPECT_EQ(100, logEvent.GetTagId());
     EXPECT_EQ(1000, logEvent.GetUid());
     EXPECT_EQ(1001, logEvent.GetPid());
+    EXPECT_FALSE(logEvent.hasAttributionChain());
 
     const vector<FieldValue>& values = logEvent.getValues();
     EXPECT_EQ(2, values.size());
@@ -179,6 +181,7 @@
     EXPECT_EQ(100, logEvent.GetTagId());
     EXPECT_EQ(1000, logEvent.GetUid());
     EXPECT_EQ(1001, logEvent.GetPid());
+    EXPECT_FALSE(logEvent.hasAttributionChain());
 
     const vector<FieldValue>& values = logEvent.getValues();
     EXPECT_EQ(1, values.size());
@@ -248,6 +251,11 @@
     const vector<FieldValue>& values = logEvent.getValues();
     EXPECT_EQ(4, values.size());  // 2 per attribution node
 
+    std::pair<int, int> attrIndexRange;
+    EXPECT_TRUE(logEvent.hasAttributionChain(&attrIndexRange));
+    EXPECT_EQ(0, attrIndexRange.first);
+    EXPECT_EQ(3, attrIndexRange.second);
+
     // Check first attribution node
     const FieldValue& uid1Item = values[0];
     Field expectedField = getField(100, {1, 1, 1}, 2, {true, false, false});