Add tests for mapIsolatedUidToHostUid methods

- Remove unneeded dependency on atoms.proto
- Remove extractIntoVector function. Assert on actual output directly.
- Use a helper method for creating UidMap mock for all tests.
- Add AttributionChain variants of existing tests in puller_util_tests.
- Add tests in StatsLogProcessor_test to test mapIsolatedUidToHostUid()
    in StatsLogProcessor.

Bug: 154285085
Test: bit statsd_test:*

Change-Id: I6d0822be9f0fbca23bd2f441dc63b97110567307
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index b3da32fc..64d42bb 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -454,9 +454,9 @@
         BluetoothActivityInfo bluetooth_activity_info = 10007 [(module) = "framework"];
         ProcessMemoryState process_memory_state = 10013 [(module) = "framework"];
         SystemElapsedRealtime system_elapsed_realtime = 10014 [(module) = "framework"];
-        SystemUptime system_uptime = 10015 [(module) = "framework", (module) = "statsdtest"];
+        SystemUptime system_uptime = 10015 [(module) = "framework"];
         CpuActiveTime cpu_active_time = 10016 [(module) = "framework", (module) = "statsdtest"];
-        CpuClusterTime cpu_cluster_time = 10017 [(module) = "framework", (module) = "statsdtest"];
+        CpuClusterTime cpu_cluster_time = 10017 [(module) = "framework"];
         DiskSpace disk_space = 10018 [deprecated=true, (module) = "statsdtest"];
         RemainingBatteryCapacity remaining_battery_capacity = 10019 [(module) = "framework"];
         FullBatteryCapacity full_battery_capacity = 10020 [(module) = "framework"];
diff --git a/cmds/statsd/tests/StatsLogProcessor_test.cpp b/cmds/statsd/tests/StatsLogProcessor_test.cpp
index b809286..e6144c5 100644
--- a/cmds/statsd/tests/StatsLogProcessor_test.cpp
+++ b/cmds/statsd/tests/StatsLogProcessor_test.cpp
@@ -1691,6 +1691,111 @@
                             &buffer);
 }
 
+TEST(StatsLogProcessorTest_mapIsolatedUidToHostUid, LogHostUid) {
+    int hostUid = 20;
+    int isolatedUid = 30;
+    uint64_t eventTimeNs = 12355;
+    int atomId = 89;
+    int field1 = 90;
+    int field2 = 28;
+    sp<MockUidMap> mockUidMap = makeMockUidMapForOneHost(hostUid, {isolatedUid});
+    ConfigKey cfgKey;
+    StatsdConfig config = MakeConfig(false);
+    sp<StatsLogProcessor> processor =
+            CreateStatsLogProcessor(1, 1, config, cfgKey, nullptr, 0, mockUidMap);
+
+    shared_ptr<LogEvent> logEvent = makeUidLogEvent(atomId, eventTimeNs, hostUid, field1, field2);
+
+    processor->OnLogEvent(logEvent.get());
+
+    const vector<FieldValue>* actualFieldValues = &logEvent->getValues();
+    EXPECT_EQ(3, actualFieldValues->size());
+    EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value);
+    EXPECT_EQ(field1, actualFieldValues->at(1).mValue.int_value);
+    EXPECT_EQ(field2, actualFieldValues->at(2).mValue.int_value);
+}
+
+TEST(StatsLogProcessorTest_mapIsolatedUidToHostUid, LogIsolatedUid) {
+    int hostUid = 20;
+    int isolatedUid = 30;
+    uint64_t eventTimeNs = 12355;
+    int atomId = 89;
+    int field1 = 90;
+    int field2 = 28;
+    sp<MockUidMap> mockUidMap = makeMockUidMapForOneHost(hostUid, {isolatedUid});
+    ConfigKey cfgKey;
+    StatsdConfig config = MakeConfig(false);
+    sp<StatsLogProcessor> processor =
+            CreateStatsLogProcessor(1, 1, config, cfgKey, nullptr, 0, mockUidMap);
+
+    shared_ptr<LogEvent> logEvent =
+            makeUidLogEvent(atomId, eventTimeNs, isolatedUid, field1, field2);
+
+    processor->OnLogEvent(logEvent.get());
+
+    const vector<FieldValue>* actualFieldValues = &logEvent->getValues();
+    EXPECT_EQ(3, actualFieldValues->size());
+    EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value);
+    EXPECT_EQ(field1, actualFieldValues->at(1).mValue.int_value);
+    EXPECT_EQ(field2, actualFieldValues->at(2).mValue.int_value);
+}
+
+TEST(StatsLogProcessorTest_mapIsolatedUidToHostUid, LogHostUidAttributionChain) {
+    int hostUid = 20;
+    int isolatedUid = 30;
+    uint64_t eventTimeNs = 12355;
+    int atomId = 89;
+    int field1 = 90;
+    int field2 = 28;
+    sp<MockUidMap> mockUidMap = makeMockUidMapForOneHost(hostUid, {isolatedUid});
+    ConfigKey cfgKey;
+    StatsdConfig config = MakeConfig(false);
+    sp<StatsLogProcessor> processor =
+            CreateStatsLogProcessor(1, 1, config, cfgKey, nullptr, 0, mockUidMap);
+
+    shared_ptr<LogEvent> logEvent = makeAttributionLogEvent(atomId, eventTimeNs, {hostUid, 200},
+                                                            {"tag1", "tag2"}, field1, field2);
+
+    processor->OnLogEvent(logEvent.get());
+
+    const vector<FieldValue>* actualFieldValues = &logEvent->getValues();
+    EXPECT_EQ(6, actualFieldValues->size());
+    EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value);
+    EXPECT_EQ("tag1", actualFieldValues->at(1).mValue.str_value);
+    EXPECT_EQ(200, actualFieldValues->at(2).mValue.int_value);
+    EXPECT_EQ("tag2", actualFieldValues->at(3).mValue.str_value);
+    EXPECT_EQ(field1, actualFieldValues->at(4).mValue.int_value);
+    EXPECT_EQ(field2, actualFieldValues->at(5).mValue.int_value);
+}
+
+TEST(StatsLogProcessorTest_mapIsolatedUidToHostUid, LogIsolatedUidAttributionChain) {
+    int hostUid = 20;
+    int isolatedUid = 30;
+    uint64_t eventTimeNs = 12355;
+    int atomId = 89;
+    int field1 = 90;
+    int field2 = 28;
+    sp<MockUidMap> mockUidMap = makeMockUidMapForOneHost(hostUid, {isolatedUid});
+    ConfigKey cfgKey;
+    StatsdConfig config = MakeConfig(false);
+    sp<StatsLogProcessor> processor =
+            CreateStatsLogProcessor(1, 1, config, cfgKey, nullptr, 0, mockUidMap);
+
+    shared_ptr<LogEvent> logEvent = makeAttributionLogEvent(atomId, eventTimeNs, {isolatedUid, 200},
+                                                            {"tag1", "tag2"}, field1, field2);
+
+    processor->OnLogEvent(logEvent.get());
+
+    const vector<FieldValue>* actualFieldValues = &logEvent->getValues();
+    EXPECT_EQ(6, actualFieldValues->size());
+    EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value);
+    EXPECT_EQ("tag1", actualFieldValues->at(1).mValue.str_value);
+    EXPECT_EQ(200, actualFieldValues->at(2).mValue.int_value);
+    EXPECT_EQ("tag2", actualFieldValues->at(3).mValue.str_value);
+    EXPECT_EQ(field1, actualFieldValues->at(4).mValue.int_value);
+    EXPECT_EQ(field2, actualFieldValues->at(5).mValue.int_value);
+}
+
 #else
 GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif
diff --git a/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp b/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp
index c10703c..6bde79f 100644
--- a/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp
+++ b/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp
@@ -13,13 +13,15 @@
 // limitations under the License.
 
 #include "src/anomaly/AnomalyTracker.h"
-#include "../metrics/metrics_test_helper.h"
 
 #include <gtest/gtest.h>
 #include <math.h>
 #include <stdio.h>
+
 #include <vector>
 
+#include "tests/statsd_test_util.h"
+
 using namespace testing;
 using android::sp;
 using std::set;
diff --git a/cmds/statsd/tests/external/puller_util_test.cpp b/cmds/statsd/tests/external/puller_util_test.cpp
index c2cfb37..a21dc87 100644
--- a/cmds/statsd/tests/external/puller_util_test.cpp
+++ b/cmds/statsd/tests/external/puller_util_test.cpp
@@ -21,9 +21,9 @@
 #include <vector>
 
 #include "../metrics/metrics_test_helper.h"
+#include "FieldValue.h"
 #include "annotations.h"
 #include "stats_event.h"
-#include "statslog_statsdtest.h"
 #include "tests/statsd_test_util.h"
 
 #ifdef __ANDROID__
@@ -33,207 +33,371 @@
 namespace statsd {
 
 using namespace testing;
-using std::make_shared;
 using std::shared_ptr;
 using std::vector;
-using testing::Contains;
 /*
  * Test merge isolated and host uid
  */
 namespace {
-int uidAtomTagId = util::CPU_CLUSTER_TIME;
-const vector<int> uidAdditiveFields = {3};
-int nonUidAtomTagId = util::SYSTEM_UPTIME;
-int timestamp = 1234;
-int isolatedUid = 30;
-int isolatedAdditiveData = 31;
-int isolatedNonAdditiveData = 32;
-int hostUid = 20;
-int hostAdditiveData = 21;
-int hostNonAdditiveData = 22;
+const int uidAtomTagId = 100;
+const vector<int> additiveFields = {3};
+const int nonUidAtomTagId = 200;
+const int timestamp = 1234;
+const int isolatedUid1 = 30;
+const int isolatedUid2 = 40;
+const int isolatedNonAdditiveData = 32;
+const int isolatedAdditiveData = 31;
+const int hostUid = 20;
+const int hostNonAdditiveData = 22;
+const int hostAdditiveData = 21;
+const int attributionAtomTagId = 300;
 
-void extractIntoVector(vector<shared_ptr<LogEvent>> events,
-                      vector<vector<int>>& ret) {
-    ret.clear();
-    status_t err;
-    for (const auto& event : events) {
-        vector<int> vec;
-        vec.push_back(event->GetInt(1, &err));
-        vec.push_back(event->GetInt(2, &err));
-        vec.push_back(event->GetInt(3, &err));
-        ret.push_back(vec);
-    }
-}
-
-std::shared_ptr<LogEvent> makeUidLogEvent(uint64_t timestampNs, int uid, int data1, int data2) {
-    AStatsEvent* statsEvent = AStatsEvent_obtain();
-    AStatsEvent_setAtomId(statsEvent, uidAtomTagId);
-    AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
-
-    AStatsEvent_writeInt32(statsEvent, uid);
-    AStatsEvent_addBoolAnnotation(statsEvent, ANNOTATION_ID_IS_UID, true);
-    AStatsEvent_writeInt32(statsEvent, data1);
-    AStatsEvent_writeInt32(statsEvent, data2);
-
-    std::shared_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
-    parseStatsEventToLogEvent(statsEvent, logEvent.get());
-    return logEvent;
-}
-
-std::shared_ptr<LogEvent> makeNonUidAtomLogEvent(uint64_t timestampNs, int data1) {
-    AStatsEvent* statsEvent = AStatsEvent_obtain();
-    AStatsEvent_setAtomId(statsEvent, nonUidAtomTagId);
-    AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
-    AStatsEvent_writeInt32(statsEvent, data1);
-
-    std::shared_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
-    parseStatsEventToLogEvent(statsEvent, logEvent.get());
-    return logEvent;
+sp<MockUidMap> makeMockUidMap() {
+    return makeMockUidMapForOneHost(hostUid, {isolatedUid1, isolatedUid2});
 }
 
 }  // anonymous namespace
 
-TEST(puller_util, MergeNoDimension) {
-    vector<shared_ptr<LogEvent>> inputData;
+TEST(PullerUtilTest, MergeNoDimension) {
+    vector<shared_ptr<LogEvent>> data = {
+            // 30->22->31
+            makeUidLogEvent(uidAtomTagId, timestamp, isolatedUid1, hostNonAdditiveData,
+                            isolatedAdditiveData),
 
-    // 30->22->31
-    inputData.push_back(
-            makeUidLogEvent(timestamp, isolatedUid, hostNonAdditiveData, isolatedAdditiveData));
+            // 20->22->21
+            makeUidLogEvent(uidAtomTagId, timestamp, hostUid, hostNonAdditiveData,
+                            hostAdditiveData),
+    };
 
-    // 20->22->21
-    inputData.push_back(makeUidLogEvent(timestamp, hostUid, hostNonAdditiveData, hostAdditiveData));
+    sp<MockUidMap> uidMap = makeMockUidMap();
+    mapAndMergeIsolatedUidsToHostUid(data, uidMap, uidAtomTagId, additiveFields);
 
-    sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>();
-    EXPECT_CALL(*uidMap, getHostUidOrSelf(isolatedUid)).WillRepeatedly(Return(hostUid));
-    EXPECT_CALL(*uidMap, getHostUidOrSelf(Ne(isolatedUid))).WillRepeatedly(ReturnArg<0>());
-    mapAndMergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId, uidAdditiveFields);
-
-    vector<vector<int>> actual;
-    extractIntoVector(inputData, actual);
-    vector<int> expectedV1 = {20, 22, 52};
-    EXPECT_EQ(1, (int)actual.size());
-    EXPECT_THAT(actual, Contains(expectedV1));
+    ASSERT_EQ(1, (int)data.size());
+    const vector<FieldValue>* actualFieldValues = &data[0]->getValues();
+    ASSERT_EQ(3, actualFieldValues->size());
+    EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value);
+    EXPECT_EQ(hostNonAdditiveData, actualFieldValues->at(1).mValue.int_value);
+    EXPECT_EQ(isolatedAdditiveData + hostAdditiveData, actualFieldValues->at(2).mValue.int_value);
 }
 
-TEST(puller_util, MergeWithDimension) {
-    vector<shared_ptr<LogEvent>> inputData;
+TEST(PullerUtilTest, MergeWithDimension) {
+    vector<shared_ptr<LogEvent>> data = {
+            // 30->32->31
+            makeUidLogEvent(uidAtomTagId, timestamp, isolatedUid1, isolatedNonAdditiveData,
+                            isolatedAdditiveData),
 
-    // 30->32->31
-    inputData.push_back(
-            makeUidLogEvent(timestamp, isolatedUid, isolatedNonAdditiveData, isolatedAdditiveData));
+            // 20->32->21
+            makeUidLogEvent(uidAtomTagId, timestamp, hostUid, isolatedNonAdditiveData,
+                            hostAdditiveData),
 
-    // 20->32->21
-    inputData.push_back(
-            makeUidLogEvent(timestamp, hostUid, isolatedNonAdditiveData, hostAdditiveData));
+            // 20->22->21
+            makeUidLogEvent(uidAtomTagId, timestamp, hostUid, hostNonAdditiveData,
+                            hostAdditiveData),
+    };
 
-    // 20->22->21
-    inputData.push_back(makeUidLogEvent(timestamp, hostUid, hostNonAdditiveData, hostAdditiveData));
+    sp<MockUidMap> uidMap = makeMockUidMap();
+    mapAndMergeIsolatedUidsToHostUid(data, uidMap, uidAtomTagId, additiveFields);
 
-    sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>();
-    EXPECT_CALL(*uidMap, getHostUidOrSelf(isolatedUid)).WillRepeatedly(Return(hostUid));
-    EXPECT_CALL(*uidMap, getHostUidOrSelf(Ne(isolatedUid))).WillRepeatedly(ReturnArg<0>());
-    mapAndMergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId, uidAdditiveFields);
+    ASSERT_EQ(2, (int)data.size());
 
-    vector<vector<int>> actual;
-    extractIntoVector(inputData, actual);
-    vector<int> expectedV1 = {20, 22, 21};
-    vector<int> expectedV2 = {20, 32, 52};
-    EXPECT_EQ(2, (int)actual.size());
-    EXPECT_THAT(actual, Contains(expectedV1));
-    EXPECT_THAT(actual, Contains(expectedV2));
+    const vector<FieldValue>* actualFieldValues = &data[0]->getValues();
+    ASSERT_EQ(3, actualFieldValues->size());
+    EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value);
+    EXPECT_EQ(hostNonAdditiveData, actualFieldValues->at(1).mValue.int_value);
+    EXPECT_EQ(hostAdditiveData, actualFieldValues->at(2).mValue.int_value);
+
+    actualFieldValues = &data[1]->getValues();
+    ASSERT_EQ(3, actualFieldValues->size());
+    EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value);
+    EXPECT_EQ(isolatedNonAdditiveData, actualFieldValues->at(1).mValue.int_value);
+    EXPECT_EQ(hostAdditiveData + isolatedAdditiveData, actualFieldValues->at(2).mValue.int_value);
 }
 
-TEST(puller_util, NoMergeHostUidOnly) {
-    vector<shared_ptr<LogEvent>> inputData;
+TEST(PullerUtilTest, NoMergeHostUidOnly) {
+    vector<shared_ptr<LogEvent>> data = {
+            // 20->32->31
+            makeUidLogEvent(uidAtomTagId, timestamp, hostUid, isolatedNonAdditiveData,
+                            isolatedAdditiveData),
+
+            // 20->22->21
+            makeUidLogEvent(uidAtomTagId, timestamp, hostUid, hostNonAdditiveData,
+                            hostAdditiveData),
+    };
+
+    sp<MockUidMap> uidMap = makeMockUidMap();
+    mapAndMergeIsolatedUidsToHostUid(data, uidMap, uidAtomTagId, additiveFields);
+
+    ASSERT_EQ(2, (int)data.size());
+
+    const vector<FieldValue>* actualFieldValues = &data[0]->getValues();
+    ASSERT_EQ(3, actualFieldValues->size());
+    EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value);
+    EXPECT_EQ(hostNonAdditiveData, actualFieldValues->at(1).mValue.int_value);
+    EXPECT_EQ(hostAdditiveData, actualFieldValues->at(2).mValue.int_value);
+
+    actualFieldValues = &data[1]->getValues();
+    ASSERT_EQ(3, actualFieldValues->size());
+    EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value);
+    EXPECT_EQ(isolatedNonAdditiveData, actualFieldValues->at(1).mValue.int_value);
+    EXPECT_EQ(isolatedAdditiveData, actualFieldValues->at(2).mValue.int_value);
+}
+
+TEST(PullerUtilTest, IsolatedUidOnly) {
+    vector<shared_ptr<LogEvent>> data = {
+            // 30->32->31
+            makeUidLogEvent(uidAtomTagId, timestamp, isolatedUid1, isolatedNonAdditiveData,
+                            isolatedAdditiveData),
+
+            // 30->22->21
+            makeUidLogEvent(uidAtomTagId, timestamp, isolatedUid1, hostNonAdditiveData,
+                            hostAdditiveData),
+    };
+
+    sp<MockUidMap> uidMap = makeMockUidMap();
+    mapAndMergeIsolatedUidsToHostUid(data, uidMap, uidAtomTagId, additiveFields);
+
+    ASSERT_EQ(2, (int)data.size());
 
     // 20->32->31
-    inputData.push_back(
-            makeUidLogEvent(timestamp, hostUid, isolatedNonAdditiveData, isolatedAdditiveData));
+    const vector<FieldValue>* actualFieldValues = &data[0]->getValues();
+    ASSERT_EQ(3, actualFieldValues->size());
+    EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value);
+    EXPECT_EQ(hostNonAdditiveData, actualFieldValues->at(1).mValue.int_value);
+    EXPECT_EQ(hostAdditiveData, actualFieldValues->at(2).mValue.int_value);
 
     // 20->22->21
-    inputData.push_back(makeUidLogEvent(timestamp, hostUid, hostNonAdditiveData, hostAdditiveData));
-
-    sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>();
-    EXPECT_CALL(*uidMap, getHostUidOrSelf(isolatedUid)).WillRepeatedly(Return(hostUid));
-    EXPECT_CALL(*uidMap, getHostUidOrSelf(Ne(isolatedUid))).WillRepeatedly(ReturnArg<0>());
-    mapAndMergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId, uidAdditiveFields);
-
-    // 20->32->31
-    // 20->22->21
-    vector<vector<int>> actual;
-    extractIntoVector(inputData, actual);
-    vector<int> expectedV1 = {20, 32, 31};
-    vector<int> expectedV2 = {20, 22, 21};
-    EXPECT_EQ(2, (int)actual.size());
-    EXPECT_THAT(actual, Contains(expectedV1));
-    EXPECT_THAT(actual, Contains(expectedV2));
+    actualFieldValues = &data[1]->getValues();
+    ASSERT_EQ(3, actualFieldValues->size());
+    EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value);
+    EXPECT_EQ(isolatedNonAdditiveData, actualFieldValues->at(1).mValue.int_value);
+    EXPECT_EQ(isolatedAdditiveData, actualFieldValues->at(2).mValue.int_value);
 }
 
-TEST(puller_util, IsolatedUidOnly) {
-    vector<shared_ptr<LogEvent>> inputData;
+TEST(PullerUtilTest, MultipleIsolatedUidToOneHostUid) {
+    vector<shared_ptr<LogEvent>> data = {
+            // 30->32->31
+            makeUidLogEvent(uidAtomTagId, timestamp, isolatedUid1, isolatedNonAdditiveData,
+                            isolatedAdditiveData),
 
-    // 30->32->31
-    inputData.push_back(
-            makeUidLogEvent(timestamp, hostUid, isolatedNonAdditiveData, isolatedAdditiveData));
+            // 31->32->21
+            makeUidLogEvent(uidAtomTagId, timestamp, isolatedUid2, isolatedNonAdditiveData,
+                            hostAdditiveData),
 
-    // 30->22->21
-    inputData.push_back(makeUidLogEvent(timestamp, hostUid, hostNonAdditiveData, hostAdditiveData));
+            // 20->32->21
+            makeUidLogEvent(uidAtomTagId, timestamp, hostUid, isolatedNonAdditiveData,
+                            hostAdditiveData),
+    };
 
-    sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>();
-    EXPECT_CALL(*uidMap, getHostUidOrSelf(isolatedUid)).WillRepeatedly(Return(hostUid));
-    EXPECT_CALL(*uidMap, getHostUidOrSelf(Ne(isolatedUid))).WillRepeatedly(ReturnArg<0>());
-    mapAndMergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId, uidAdditiveFields);
+    sp<MockUidMap> uidMap = makeMockUidMap();
+    mapAndMergeIsolatedUidsToHostUid(data, uidMap, uidAtomTagId, additiveFields);
 
-    // 20->32->31
-    // 20->22->21
-    vector<vector<int>> actual;
-    extractIntoVector(inputData, actual);
-    vector<int> expectedV1 = {20, 32, 31};
-    vector<int> expectedV2 = {20, 22, 21};
-    EXPECT_EQ(2, (int)actual.size());
-    EXPECT_THAT(actual, Contains(expectedV1));
-    EXPECT_THAT(actual, Contains(expectedV2));
+    ASSERT_EQ(1, (int)data.size());
+
+    const vector<FieldValue>* actualFieldValues = &data[0]->getValues();
+    ASSERT_EQ(3, actualFieldValues->size());
+    EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value);
+    EXPECT_EQ(isolatedNonAdditiveData, actualFieldValues->at(1).mValue.int_value);
+    EXPECT_EQ(isolatedAdditiveData + hostAdditiveData + hostAdditiveData,
+              actualFieldValues->at(2).mValue.int_value);
 }
 
-TEST(puller_util, MultipleIsolatedUidToOneHostUid) {
-    vector<shared_ptr<LogEvent>> inputData;
+TEST(PullerUtilTest, NoNeedToMerge) {
+    vector<shared_ptr<LogEvent>> data = {
+            // 32->31
+            CreateTwoValueLogEvent(nonUidAtomTagId, timestamp, isolatedNonAdditiveData,
+                                   isolatedAdditiveData),
 
-    // 30->32->31
-    inputData.push_back(
-            makeUidLogEvent(timestamp, isolatedUid, isolatedNonAdditiveData, isolatedAdditiveData));
+            // 22->21
+            CreateTwoValueLogEvent(nonUidAtomTagId, timestamp, hostNonAdditiveData,
+                                   hostAdditiveData),
 
-    // 31->32->21
-    inputData.push_back(
-            makeUidLogEvent(timestamp, isolatedUid + 1, isolatedNonAdditiveData, hostAdditiveData));
+    };
 
-    // 20->32->21
-    inputData.push_back(
-            makeUidLogEvent(timestamp, hostUid, isolatedNonAdditiveData, hostAdditiveData));
+    sp<MockUidMap> uidMap = makeMockUidMap();
+    mapAndMergeIsolatedUidsToHostUid(data, uidMap, nonUidAtomTagId, {} /*no additive fields*/);
 
-    sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>();
-    EXPECT_CALL(*uidMap, getHostUidOrSelf(_)).WillRepeatedly(Return(hostUid));
-    mapAndMergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId, uidAdditiveFields);
+    ASSERT_EQ(2, (int)data.size());
 
-    vector<vector<int>> actual;
-    extractIntoVector(inputData, actual);
-    vector<int> expectedV1 = {20, 32, 73};
-    EXPECT_EQ(1, (int)actual.size());
-    EXPECT_THAT(actual, Contains(expectedV1));
+    const vector<FieldValue>* actualFieldValues = &data[0]->getValues();
+    ASSERT_EQ(2, actualFieldValues->size());
+    EXPECT_EQ(isolatedNonAdditiveData, actualFieldValues->at(0).mValue.int_value);
+    EXPECT_EQ(isolatedAdditiveData, actualFieldValues->at(1).mValue.int_value);
+
+    actualFieldValues = &data[1]->getValues();
+    ASSERT_EQ(2, actualFieldValues->size());
+    EXPECT_EQ(hostNonAdditiveData, actualFieldValues->at(0).mValue.int_value);
+    EXPECT_EQ(hostAdditiveData, actualFieldValues->at(1).mValue.int_value);
 }
 
-TEST(puller_util, NoNeedToMerge) {
-    vector<shared_ptr<LogEvent>> inputData;
+TEST(PullerUtilTest, MergeNoDimensionAttributionChain) {
+    vector<shared_ptr<LogEvent>> data = {
+            // 30->tag1->400->tag2->22->31
+            makeAttributionLogEvent(attributionAtomTagId, timestamp, {isolatedUid1, 400},
+                                    {"tag1", "tag2"}, hostNonAdditiveData, isolatedAdditiveData),
 
-    // 32
-    inputData.push_back(makeNonUidAtomLogEvent(timestamp, isolatedNonAdditiveData));
+            // 20->tag1->400->tag2->22->21
+            makeAttributionLogEvent(attributionAtomTagId, timestamp, {hostUid, 400},
+                                    {"tag1", "tag2"}, hostNonAdditiveData, hostAdditiveData),
+    };
 
-    // 22
-    inputData.push_back(makeNonUidAtomLogEvent(timestamp, hostNonAdditiveData));
+    sp<MockUidMap> uidMap = makeMockUidMap();
+    mapAndMergeIsolatedUidsToHostUid(data, uidMap, attributionAtomTagId, additiveFields);
 
-    sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>();
-    mapAndMergeIsolatedUidsToHostUid(inputData, uidMap, nonUidAtomTagId, {} /*no additive fields*/);
+    ASSERT_EQ(1, (int)data.size());
+    const vector<FieldValue>* actualFieldValues = &data[0]->getValues();
+    ASSERT_EQ(6, actualFieldValues->size());
+    EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value);
+    EXPECT_EQ("tag1", actualFieldValues->at(1).mValue.str_value);
+    EXPECT_EQ(400, actualFieldValues->at(2).mValue.int_value);
+    EXPECT_EQ("tag2", actualFieldValues->at(3).mValue.str_value);
+    EXPECT_EQ(hostNonAdditiveData, actualFieldValues->at(4).mValue.int_value);
+    EXPECT_EQ(isolatedAdditiveData + hostAdditiveData, actualFieldValues->at(5).mValue.int_value);
+}
 
-    EXPECT_EQ(2, (int)inputData.size());
+TEST(PullerUtilTest, MergeWithDimensionAttributionChain) {
+    vector<shared_ptr<LogEvent>> data = {
+            // 200->tag1->30->tag2->32->31
+            makeAttributionLogEvent(attributionAtomTagId, timestamp, {200, isolatedUid1},
+                                    {"tag1", "tag2"}, isolatedNonAdditiveData,
+                                    isolatedAdditiveData),
+
+            // 200->tag1->20->tag2->32->21
+            makeAttributionLogEvent(attributionAtomTagId, timestamp, {200, hostUid},
+                                    {"tag1", "tag2"}, isolatedNonAdditiveData, hostAdditiveData),
+
+            // 200->tag1->20->tag2->22->21
+            makeAttributionLogEvent(attributionAtomTagId, timestamp, {200, hostUid},
+                                    {"tag1", "tag2"}, hostNonAdditiveData, hostAdditiveData),
+    };
+
+    sp<MockUidMap> uidMap = makeMockUidMap();
+    mapAndMergeIsolatedUidsToHostUid(data, uidMap, attributionAtomTagId, additiveFields);
+
+    ASSERT_EQ(2, (int)data.size());
+
+    const vector<FieldValue>* actualFieldValues = &data[0]->getValues();
+    ASSERT_EQ(6, actualFieldValues->size());
+    EXPECT_EQ(200, actualFieldValues->at(0).mValue.int_value);
+    EXPECT_EQ("tag1", actualFieldValues->at(1).mValue.str_value);
+    EXPECT_EQ(hostUid, actualFieldValues->at(2).mValue.int_value);
+    EXPECT_EQ("tag2", actualFieldValues->at(3).mValue.str_value);
+    EXPECT_EQ(hostNonAdditiveData, actualFieldValues->at(4).mValue.int_value);
+    EXPECT_EQ(hostAdditiveData, actualFieldValues->at(5).mValue.int_value);
+
+    actualFieldValues = &data[1]->getValues();
+    ASSERT_EQ(6, actualFieldValues->size());
+    EXPECT_EQ(200, actualFieldValues->at(0).mValue.int_value);
+    EXPECT_EQ("tag1", actualFieldValues->at(1).mValue.str_value);
+    EXPECT_EQ(hostUid, actualFieldValues->at(2).mValue.int_value);
+    EXPECT_EQ("tag2", actualFieldValues->at(3).mValue.str_value);
+    EXPECT_EQ(isolatedNonAdditiveData, actualFieldValues->at(4).mValue.int_value);
+    EXPECT_EQ(hostAdditiveData + isolatedAdditiveData, actualFieldValues->at(5).mValue.int_value);
+}
+
+TEST(PullerUtilTest, NoMergeHostUidOnlyAttributionChain) {
+    vector<shared_ptr<LogEvent>> data = {
+            // 20->tag1->400->tag2->32->31
+            makeAttributionLogEvent(attributionAtomTagId, timestamp, {hostUid, 400},
+                                    {"tag1", "tag2"}, isolatedNonAdditiveData,
+                                    isolatedAdditiveData),
+
+            // 20->tag1->400->tag2->22->21
+            makeAttributionLogEvent(attributionAtomTagId, timestamp, {hostUid, 400},
+                                    {"tag1", "tag2"}, hostNonAdditiveData, hostAdditiveData),
+    };
+
+    sp<MockUidMap> uidMap = makeMockUidMap();
+    mapAndMergeIsolatedUidsToHostUid(data, uidMap, attributionAtomTagId, additiveFields);
+
+    ASSERT_EQ(2, (int)data.size());
+
+    const vector<FieldValue>* actualFieldValues = &data[0]->getValues();
+    ASSERT_EQ(6, actualFieldValues->size());
+    EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value);
+    EXPECT_EQ("tag1", actualFieldValues->at(1).mValue.str_value);
+    EXPECT_EQ(400, actualFieldValues->at(2).mValue.int_value);
+    EXPECT_EQ("tag2", actualFieldValues->at(3).mValue.str_value);
+    EXPECT_EQ(hostNonAdditiveData, actualFieldValues->at(4).mValue.int_value);
+    EXPECT_EQ(hostAdditiveData, actualFieldValues->at(5).mValue.int_value);
+
+    actualFieldValues = &data[1]->getValues();
+    ASSERT_EQ(6, actualFieldValues->size());
+    EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value);
+    EXPECT_EQ("tag1", actualFieldValues->at(1).mValue.str_value);
+    EXPECT_EQ(400, actualFieldValues->at(2).mValue.int_value);
+    EXPECT_EQ("tag2", actualFieldValues->at(3).mValue.str_value);
+    EXPECT_EQ(isolatedNonAdditiveData, actualFieldValues->at(4).mValue.int_value);
+    EXPECT_EQ(isolatedAdditiveData, actualFieldValues->at(5).mValue.int_value);
+}
+
+TEST(PullerUtilTest, IsolatedUidOnlyAttributionChain) {
+    vector<shared_ptr<LogEvent>> data = {
+            // 30->tag1->400->tag2->32->31
+            makeAttributionLogEvent(attributionAtomTagId, timestamp, {isolatedUid1, 400},
+                                    {"tag1", "tag2"}, isolatedNonAdditiveData,
+                                    isolatedAdditiveData),
+
+            // 30->tag1->400->tag2->22->21
+            makeAttributionLogEvent(attributionAtomTagId, timestamp, {isolatedUid1, 400},
+                                    {"tag1", "tag2"}, hostNonAdditiveData, hostAdditiveData),
+    };
+
+    sp<MockUidMap> uidMap = makeMockUidMap();
+    mapAndMergeIsolatedUidsToHostUid(data, uidMap, attributionAtomTagId, additiveFields);
+
+    ASSERT_EQ(2, (int)data.size());
+
+    // 20->tag1->400->tag2->32->31
+    const vector<FieldValue>* actualFieldValues = &data[0]->getValues();
+    ASSERT_EQ(6, actualFieldValues->size());
+    EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value);
+    EXPECT_EQ("tag1", actualFieldValues->at(1).mValue.str_value);
+    EXPECT_EQ(400, actualFieldValues->at(2).mValue.int_value);
+    EXPECT_EQ("tag2", actualFieldValues->at(3).mValue.str_value);
+    EXPECT_EQ(hostNonAdditiveData, actualFieldValues->at(4).mValue.int_value);
+    EXPECT_EQ(hostAdditiveData, actualFieldValues->at(5).mValue.int_value);
+
+    // 20->tag1->400->tag2->22->21
+    actualFieldValues = &data[1]->getValues();
+    ASSERT_EQ(6, actualFieldValues->size());
+    EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value);
+    EXPECT_EQ("tag1", actualFieldValues->at(1).mValue.str_value);
+    EXPECT_EQ(400, actualFieldValues->at(2).mValue.int_value);
+    EXPECT_EQ("tag2", actualFieldValues->at(3).mValue.str_value);
+    EXPECT_EQ(isolatedNonAdditiveData, actualFieldValues->at(4).mValue.int_value);
+    EXPECT_EQ(isolatedAdditiveData, actualFieldValues->at(5).mValue.int_value);
+}
+
+TEST(PullerUtilTest, MultipleIsolatedUidToOneHostUidAttributionChain) {
+    vector<shared_ptr<LogEvent>> data = {
+            // 30->tag1->400->tag2->32->31
+            makeAttributionLogEvent(attributionAtomTagId, timestamp, {isolatedUid1, 400},
+                                    {"tag1", "tag2"}, isolatedNonAdditiveData,
+                                    isolatedAdditiveData),
+
+            // 31->tag1->400->tag2->32->21
+            makeAttributionLogEvent(attributionAtomTagId, timestamp, {isolatedUid2, 400},
+                                    {"tag1", "tag2"}, isolatedNonAdditiveData, hostAdditiveData),
+
+            // 20->tag1->400->tag2->32->21
+            makeAttributionLogEvent(attributionAtomTagId, timestamp, {hostUid, 400},
+                                    {"tag1", "tag2"}, isolatedNonAdditiveData, hostAdditiveData),
+    };
+
+    sp<MockUidMap> uidMap = makeMockUidMap();
+    mapAndMergeIsolatedUidsToHostUid(data, uidMap, attributionAtomTagId, additiveFields);
+
+    ASSERT_EQ(1, (int)data.size());
+
+    const vector<FieldValue>* actualFieldValues = &data[0]->getValues();
+    ASSERT_EQ(6, actualFieldValues->size());
+    EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value);
+    EXPECT_EQ("tag1", actualFieldValues->at(1).mValue.str_value);
+    EXPECT_EQ(400, actualFieldValues->at(2).mValue.int_value);
+    EXPECT_EQ("tag2", actualFieldValues->at(3).mValue.str_value);
+    EXPECT_EQ(isolatedNonAdditiveData, actualFieldValues->at(4).mValue.int_value);
+    EXPECT_EQ(isolatedAdditiveData + hostAdditiveData + hostAdditiveData,
+              actualFieldValues->at(5).mValue.int_value);
 }
 
 }  // namespace statsd
diff --git a/cmds/statsd/tests/metrics/metrics_test_helper.h b/cmds/statsd/tests/metrics/metrics_test_helper.h
index be410b1..46ef0f6 100644
--- a/cmds/statsd/tests/metrics/metrics_test_helper.h
+++ b/cmds/statsd/tests/metrics/metrics_test_helper.h
@@ -48,12 +48,6 @@
                  void(const ConfigKey& configKey, wp<PullUidProvider> provider));
 };
 
-class MockUidMap : public UidMap {
- public:
-  MOCK_CONST_METHOD1(getHostUidOrSelf, int(int uid));
-  MOCK_CONST_METHOD1(getAppUid, std::set<int32_t>(const string& package));
-};
-
 HashableDimensionKey getMockedDimensionKey(int tagId, int key, std::string value);
 MetricDimensionKey getMockedMetricDimensionKey(int tagId, int key, std::string value);
 
diff --git a/cmds/statsd/tests/statsd_test_util.cpp b/cmds/statsd/tests/statsd_test_util.cpp
index 7216e1d..2315fd7 100644
--- a/cmds/statsd/tests/statsd_test_util.cpp
+++ b/cmds/statsd/tests/statsd_test_util.cpp
@@ -563,6 +563,48 @@
     return logEvent;
 }
 
+shared_ptr<LogEvent> makeUidLogEvent(int atomId, int64_t eventTimeNs, int uid, int data1,
+                                     int data2) {
+    AStatsEvent* statsEvent = AStatsEvent_obtain();
+    AStatsEvent_setAtomId(statsEvent, atomId);
+    AStatsEvent_overwriteTimestamp(statsEvent, eventTimeNs);
+
+    AStatsEvent_writeInt32(statsEvent, uid);
+    AStatsEvent_addBoolAnnotation(statsEvent, ANNOTATION_ID_IS_UID, true);
+    AStatsEvent_writeInt32(statsEvent, data1);
+    AStatsEvent_writeInt32(statsEvent, data2);
+
+    shared_ptr<LogEvent> logEvent = std::make_shared<LogEvent>(/*uid=*/0, /*pid=*/0);
+    parseStatsEventToLogEvent(statsEvent, logEvent.get());
+    return logEvent;
+}
+
+shared_ptr<LogEvent> makeAttributionLogEvent(int atomId, int64_t eventTimeNs,
+                                             const vector<int>& uids, const vector<string>& tags,
+                                             int data1, int data2) {
+    AStatsEvent* statsEvent = AStatsEvent_obtain();
+    AStatsEvent_setAtomId(statsEvent, atomId);
+    AStatsEvent_overwriteTimestamp(statsEvent, eventTimeNs);
+
+    writeAttribution(statsEvent, uids, tags);
+    AStatsEvent_writeInt32(statsEvent, data1);
+    AStatsEvent_writeInt32(statsEvent, data2);
+
+    shared_ptr<LogEvent> logEvent = std::make_shared<LogEvent>(/*uid=*/0, /*pid=*/0);
+    parseStatsEventToLogEvent(statsEvent, logEvent.get());
+    return logEvent;
+}
+
+sp<MockUidMap> makeMockUidMapForOneHost(int hostUid, const vector<int>& isolatedUids) {
+    sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>();
+    EXPECT_CALL(*uidMap, getHostUidOrSelf(_)).WillRepeatedly(ReturnArg<0>());
+    for (const int isolatedUid : isolatedUids) {
+        EXPECT_CALL(*uidMap, getHostUidOrSelf(isolatedUid)).WillRepeatedly(Return(hostUid));
+    }
+
+    return uidMap;
+}
+
 std::unique_ptr<LogEvent> CreateScreenStateChangedEvent(
         uint64_t timestampNs, const android::view::DisplayStateEnum state) {
     AStatsEvent* statsEvent = AStatsEvent_obtain();
@@ -878,8 +920,7 @@
 sp<StatsLogProcessor> CreateStatsLogProcessor(const int64_t timeBaseNs, const int64_t currentTimeNs,
                                               const StatsdConfig& config, const ConfigKey& key,
                                               const shared_ptr<IPullAtomCallback>& puller,
-                                              const int32_t atomTag) {
-    sp<UidMap> uidMap = new UidMap();
+                                              const int32_t atomTag, const sp<UidMap> uidMap) {
     sp<StatsPullerManager> pullerManager = new StatsPullerManager();
     if (puller != nullptr) {
         pullerManager->RegisterPullAtomCallback(/*uid=*/0, atomTag, NS_PER_SEC, NS_PER_SEC * 10, {},
diff --git a/cmds/statsd/tests/statsd_test_util.h b/cmds/statsd/tests/statsd_test_util.h
index 4d68ea2..dc012c5 100644
--- a/cmds/statsd/tests/statsd_test_util.h
+++ b/cmds/statsd/tests/statsd_test_util.h
@@ -17,6 +17,7 @@
 #include <aidl/android/os/BnPullAtomCallback.h>
 #include <aidl/android/os/IPullAtomCallback.h>
 #include <aidl/android/os/IPullAtomResultReceiver.h>
+#include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
 #include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
@@ -24,6 +25,7 @@
 #include "src/StatsLogProcessor.h"
 #include "src/hash.h"
 #include "src/logd/LogEvent.h"
+#include "src/packages/UidMap.h"
 #include "src/stats_log_util.h"
 #include "stats_event.h"
 #include "statslog_statsdtest.h"
@@ -32,6 +34,7 @@
 namespace os {
 namespace statsd {
 
+using namespace testing;
 using ::aidl::android::os::BnPullAtomCallback;
 using ::aidl::android::os::IPullAtomCallback;
 using ::aidl::android::os::IPullAtomResultReceiver;
@@ -44,6 +47,12 @@
 
 enum BucketSplitEvent { APP_UPGRADE, BOOT_COMPLETE };
 
+class MockUidMap : public UidMap {
+public:
+    MOCK_METHOD(int, getHostUidOrSelf, (int uid), (const));
+    MOCK_METHOD(std::set<int32_t>, getAppUid, (const string& package), (const));
+};
+
 // Converts a ProtoOutputStream to a StatsLogReport proto.
 StatsLogReport outputStreamToProto(ProtoOutputStream* proto);
 
@@ -212,6 +221,15 @@
 
 void CreateNoValuesLogEvent(LogEvent* logEvent, int atomId, int64_t eventTimeNs);
 
+std::shared_ptr<LogEvent> makeUidLogEvent(int atomId, int64_t eventTimeNs, int uid, int data1,
+                                          int data2);
+
+std::shared_ptr<LogEvent> makeAttributionLogEvent(int atomId, int64_t eventTimeNs,
+                                                  const vector<int>& uids,
+                                                  const vector<string>& tags, int data1, int data2);
+
+sp<MockUidMap> makeMockUidMapForOneHost(int hostUid, const vector<int>& isolatedUids);
+
 // Create log event for screen state changed.
 std::unique_ptr<LogEvent> CreateScreenStateChangedEvent(
         uint64_t timestampNs, const android::view::DisplayStateEnum state);
@@ -293,7 +311,8 @@
 sp<StatsLogProcessor> CreateStatsLogProcessor(const int64_t timeBaseNs, const int64_t currentTimeNs,
                                               const StatsdConfig& config, const ConfigKey& key,
                                               const shared_ptr<IPullAtomCallback>& puller = nullptr,
-                                              const int32_t atomTag = 0 /*for puller only*/);
+                                              const int32_t atomTag = 0 /*for puller only*/,
+                                              const sp<UidMap> = new UidMap());
 
 // Util function to sort the log events by timestamp.
 void sortLogEventsByTimestamp(std::vector<std::unique_ptr<LogEvent>> *events);