Support read iface stats detail

The networkStatsFactory need to parse the detail traffic stats of each
interface instead of the total number. This change added a helper
function to return a vector of stats_line contain stats for each iface.

Bug: 72111305
Test: ./libbpf_test
Change-Id: I9a9cda7ab90cf533c2f2cc81b37b4d520d442ce2
diff --git a/libbpf/BpfNetworkStats.cpp b/libbpf/BpfNetworkStats.cpp
index bc9a932..acde1e6 100644
--- a/libbpf/BpfNetworkStats.cpp
+++ b/libbpf/BpfNetworkStats.cpp
@@ -123,9 +123,9 @@
                               const char* ifname) {
     stats_line newLine;
     strlcpy(newLine.iface, ifname, sizeof(newLine.iface));
-    newLine.uid = statsKey.uid;
-    newLine.set = statsKey.counterSet;
-    newLine.tag = statsKey.tag;
+    newLine.uid = (int32_t)statsKey.uid;
+    newLine.set = (int32_t)statsKey.counterSet;
+    newLine.tag = (int32_t)statsKey.tag;
     newLine.rxPackets = statsEntry.rxPackets;
     newLine.txPackets = statsEntry.txPackets;
     newLine.rxBytes = statsEntry.rxBytes;
@@ -240,6 +240,47 @@
     return ret;
 }
 
+int parseBpfNetworkStatsDevInternal(std::vector<stats_line>* lines,
+                                    const base::unique_fd& statsMapFd,
+                                    const base::unique_fd& ifaceMapFd) {
+    int64_t unknownIfaceBytesTotal = 0;
+    uint32_t nonExistentKey = NONEXISTENT_IFACE_STATS_KEY;
+    struct StatsValue dummyValue;
+    auto processDetailIfaceStats = [lines, &unknownIfaceBytesTotal, &ifaceMapFd](
+                                    void* key, void* value, const base::unique_fd& statsMapFd) {
+        uint32_t ifIndex = *(uint32_t*)key;
+        char ifname[IFNAMSIZ];
+        if (getIfaceNameFromMap(ifaceMapFd, statsMapFd, ifIndex, ifname, &ifIndex,
+                                &unknownIfaceBytesTotal)) {
+            return BPF_CONTINUE;
+        }
+        StatsValue* statsEntry = (StatsValue*)value;
+        StatsKey fakeKey = {
+            .uid = (uint32_t)UID_ALL, .counterSet = (uint32_t)SET_ALL, .tag = (uint32_t)TAG_NONE};
+        lines->push_back(populateStatsEntry(fakeKey, *statsEntry, ifname));
+        return BPF_CONTINUE;
+    };
+    return bpfIterateMapWithValue(nonExistentKey, dummyValue, statsMapFd, processDetailIfaceStats);
+}
+
+int parseBpfNetworkStatsDev(std::vector<stats_line>* lines) {
+    int ret = 0;
+    base::unique_fd ifaceIndexNameMap(bpf::mapRetrieve(IFACE_INDEX_NAME_MAP_PATH, BPF_OPEN_FLAGS));
+    if (ifaceIndexNameMap < 0) {
+        ret = -errno;
+        ALOGE("get ifaceIndexName map fd failed: %s", strerror(errno));
+        return ret;
+    }
+
+    base::unique_fd ifaceStatsMap(bpf::mapRetrieve(IFACE_STATS_MAP_PATH, BPF_OPEN_FLAGS));
+    if (ifaceStatsMap < 0) {
+        ret = -errno;
+        ALOGE("get ifaceStats map fd failed: %s", strerror(errno));
+        return ret;
+    }
+    return parseBpfNetworkStatsDevInternal(lines, ifaceStatsMap, ifaceIndexNameMap);
+}
+
 uint64_t combineUidTag(const uid_t uid, const uint32_t tag) {
     return (uint64_t)uid << 32 | tag;
 }
diff --git a/libbpf/BpfNetworkStatsTest.cpp b/libbpf/BpfNetworkStatsTest.cpp
index cd7a066..33df0f2 100644
--- a/libbpf/BpfNetworkStatsTest.cpp
+++ b/libbpf/BpfNetworkStatsTest.cpp
@@ -378,5 +378,38 @@
     expectStatsLineEqual(value1, IFACE_NAME1, TEST_UID1, TEST_COUNTERSET0, 0, lines.front());
 }
 
+TEST_F(BpfNetworkStatsHelperTest, TestGetIfaceStatsDetail) {
+    updateIfaceMap(IFACE_NAME1, IFACE_INDEX1);
+    updateIfaceMap(IFACE_NAME2, IFACE_INDEX2);
+    updateIfaceMap(IFACE_NAME3, IFACE_INDEX3);
+    StatsValue value1 = {
+        .rxBytes = TEST_BYTES0,
+        .rxPackets = TEST_PACKET0,
+        .txBytes = TEST_BYTES1,
+        .txPackets = TEST_PACKET1,
+    };
+    StatsValue value2 = {
+        .rxBytes = TEST_BYTES1,
+        .rxPackets = TEST_PACKET1,
+        .txBytes = TEST_BYTES0,
+        .txPackets = TEST_PACKET0,
+    };
+    uint32_t ifaceStatsKey = IFACE_INDEX1;
+    EXPECT_EQ(0, writeToMapEntry(mFakeIfaceStatsMap, &ifaceStatsKey, &value1, BPF_ANY));
+    ifaceStatsKey = IFACE_INDEX2;
+    EXPECT_EQ(0, writeToMapEntry(mFakeIfaceStatsMap, &ifaceStatsKey, &value2, BPF_ANY));
+    ifaceStatsKey = IFACE_INDEX3;
+    EXPECT_EQ(0, writeToMapEntry(mFakeIfaceStatsMap, &ifaceStatsKey, &value1, BPF_ANY));
+    std::vector<stats_line> lines;
+    ASSERT_EQ(0,
+              parseBpfNetworkStatsDevInternal(&lines, mFakeIfaceStatsMap, mFakeIfaceIndexNameMap));
+    ASSERT_EQ((unsigned long)3, lines.size());
+    std::sort(lines.begin(), lines.end(), [](const auto& line1, const auto& line2)-> bool {
+        return strcmp(line1.iface, line2.iface) < 0;
+    });
+    expectStatsLineEqual(value1, IFACE_NAME1, UID_ALL, SET_ALL, TAG_NONE, lines[0]);
+    expectStatsLineEqual(value1, IFACE_NAME3, UID_ALL, SET_ALL, TAG_NONE, lines[1]);
+    expectStatsLineEqual(value2, IFACE_NAME2, UID_ALL, SET_ALL, TAG_NONE, lines[2]);
+}
 }  // namespace bpf
 }  // namespace android
diff --git a/libbpf/include/bpf/BpfNetworkStats.h b/libbpf/include/bpf/BpfNetworkStats.h
index db0c185..63cf0bc 100644
--- a/libbpf/include/bpf/BpfNetworkStats.h
+++ b/libbpf/include/bpf/BpfNetworkStats.h
@@ -53,12 +53,18 @@
 int getIfaceNameFromMap(const base::unique_fd& ifaceMapFd, const base::unique_fd& statsMapFd,
                         uint32_t ifaceIndex, char* ifname, void* curKey,
                         int64_t* unknownIfaceBytesTotal);
+// For test only
+int parseBpfNetworkStatsDevInternal(std::vector<stats_line>* lines,
+                                    const base::unique_fd& statsMapFd,
+                                    const base::unique_fd& ifaceMapFd);
 
 int bpfGetUidStats(uid_t uid, struct Stats* stats);
 int bpfGetIfaceStats(const char* iface, struct Stats* stats);
 int parseBpfNetworkStatsDetail(std::vector<stats_line>* lines,
                                const std::vector<std::string>& limitIfaces, int limitTag,
                                int limitUid);
+
+int parseBpfNetworkStatsDev(std::vector<stats_line>* lines);
 int cleanStatsMap();
 }  // namespace bpf
 }  // namespace android