Add method for reading stats by iface index

This change adds the nativeGetIfIndexStat JNI method, which can be used
to read the traffic stats associated with a specific iface index.

Test: Included unit test.
Bug: 241098920
Change-Id: I7a4adcb9610562ff01fe5cd1234d6dfc57f9408d
diff --git a/service-t/jni/com_android_server_net_NetworkStatsService.cpp b/service-t/jni/com_android_server_net_NetworkStatsService.cpp
index dab9d07..8d8dc32 100644
--- a/service-t/jni/com_android_server_net_NetworkStatsService.cpp
+++ b/service-t/jni/com_android_server_net_NetworkStatsService.cpp
@@ -34,6 +34,7 @@
 
 using android::bpf::bpfGetUidStats;
 using android::bpf::bpfGetIfaceStats;
+using android::bpf::bpfGetIfIndexStats;
 using android::bpf::NetworkTraceHandler;
 
 namespace android {
@@ -94,6 +95,15 @@
     }
 }
 
+static jlong nativeGetIfIndexStat(JNIEnv* env, jclass clazz, jint ifindex, jint type) {
+    Stats stats = {};
+    if (bpfGetIfIndexStats(ifindex, &stats) == 0) {
+        return getStatsType(&stats, (StatsType) type);
+    } else {
+        return UNKNOWN;
+    }
+}
+
 static jlong nativeGetUidStat(JNIEnv* env, jclass clazz, jint uid, jint type) {
     Stats stats = {};
 
@@ -111,6 +121,7 @@
 static const JNINativeMethod gMethods[] = {
         {"nativeGetTotalStat", "(I)J", (void*)nativeGetTotalStat},
         {"nativeGetIfaceStat", "(Ljava/lang/String;I)J", (void*)nativeGetIfaceStat},
+        {"nativeGetIfIndexStat", "(II)J", (void*)nativeGetIfIndexStat},
         {"nativeGetUidStat", "(II)J", (void*)nativeGetUidStat},
         {"nativeInitNetworkTracing", "()V", (void*)nativeInitNetworkTracing},
 };
diff --git a/service-t/native/libs/libnetworkstats/BpfNetworkStats.cpp b/service-t/native/libs/libnetworkstats/BpfNetworkStats.cpp
index 5579e43..2e6e3e5 100644
--- a/service-t/native/libs/libnetworkstats/BpfNetworkStats.cpp
+++ b/service-t/native/libs/libnetworkstats/BpfNetworkStats.cpp
@@ -105,6 +105,25 @@
     return bpfGetIfaceStatsInternal(iface, stats, ifaceStatsMap, ifaceIndexNameMap);
 }
 
+int bpfGetIfIndexStatsInternal(uint32_t ifindex, Stats* stats,
+                               const BpfMap<uint32_t, StatsValue>& ifaceStatsMap) {
+    InitStats(stats);
+    auto statsEntry = ifaceStatsMap.readValue(ifindex);
+    if (statsEntry.ok()) {
+        stats->rxPackets = statsEntry.value().rxPackets;
+        stats->txPackets = statsEntry.value().txPackets;
+        stats->rxBytes = statsEntry.value().rxBytes;
+        stats->txBytes = statsEntry.value().txBytes;
+        return 0;
+    }
+    return (statsEntry.error().code() == ENOENT) ? 0 : -statsEntry.error().code();
+}
+
+int bpfGetIfIndexStats(int ifindex, Stats* stats) {
+    static BpfMapRO<uint32_t, StatsValue> ifaceStatsMap(IFACE_STATS_MAP_PATH);
+    return bpfGetIfIndexStatsInternal(ifindex, stats, ifaceStatsMap);
+}
+
 stats_line populateStatsEntry(const StatsKey& statsKey, const StatsValue& statsEntry,
                               const char* ifname) {
     stats_line newLine;
diff --git a/service-t/native/libs/libnetworkstats/BpfNetworkStatsTest.cpp b/service-t/native/libs/libnetworkstats/BpfNetworkStatsTest.cpp
index ccd3f5e..4f85d9b 100644
--- a/service-t/native/libs/libnetworkstats/BpfNetworkStatsTest.cpp
+++ b/service-t/native/libs/libnetworkstats/BpfNetworkStatsTest.cpp
@@ -275,6 +275,20 @@
     expectStatsEqual(totalValue, totalResult);
 }
 
+TEST_F(BpfNetworkStatsHelperTest, TestGetIfIndexStatsInternal) {
+    StatsValue value = {
+          .rxPackets = TEST_PACKET0,
+          .rxBytes = TEST_BYTES0,
+          .txPackets = TEST_PACKET1,
+          .txBytes = TEST_BYTES1,
+    };
+    EXPECT_RESULT_OK(mFakeIfaceStatsMap.writeValue(IFACE_INDEX1, value, BPF_ANY));
+
+    Stats result = {};
+    ASSERT_EQ(0, bpfGetIfIndexStatsInternal(IFACE_INDEX1, &result, mFakeIfaceStatsMap));
+    expectStatsEqual(value, result);
+}
+
 TEST_F(BpfNetworkStatsHelperTest, TestGetStatsDetail) {
     updateIfaceMap(IFACE_NAME1, IFACE_INDEX1);
     updateIfaceMap(IFACE_NAME2, IFACE_INDEX2);
diff --git a/service-t/native/libs/libnetworkstats/include/netdbpf/BpfNetworkStats.h b/service-t/native/libs/libnetworkstats/include/netdbpf/BpfNetworkStats.h
index 133009f..0a9c012 100644
--- a/service-t/native/libs/libnetworkstats/include/netdbpf/BpfNetworkStats.h
+++ b/service-t/native/libs/libnetworkstats/include/netdbpf/BpfNetworkStats.h
@@ -63,6 +63,9 @@
                              const BpfMap<uint32_t, StatsValue>& ifaceStatsMap,
                              const BpfMap<uint32_t, IfaceValue>& ifaceNameMap);
 // For test only
+int bpfGetIfIndexStatsInternal(uint32_t ifindex, Stats* stats,
+                               const BpfMap<uint32_t, StatsValue>& ifaceStatsMap);
+// For test only
 int parseBpfNetworkStatsDetailInternal(std::vector<stats_line>& lines,
                                        const BpfMap<StatsKey, StatsValue>& statsMap,
                                        const BpfMap<uint32_t, IfaceValue>& ifaceMap);
@@ -112,6 +115,7 @@
 
 int bpfGetUidStats(uid_t uid, Stats* stats);
 int bpfGetIfaceStats(const char* iface, Stats* stats);
+int bpfGetIfIndexStats(int ifindex, Stats* stats);
 int parseBpfNetworkStatsDetail(std::vector<stats_line>* lines);
 
 int parseBpfNetworkStatsDev(std::vector<stats_line>* lines);
diff --git a/service-t/src/com/android/server/net/NetworkStatsService.java b/service-t/src/com/android/server/net/NetworkStatsService.java
index 6635fd3..3f4113a 100644
--- a/service-t/src/com/android/server/net/NetworkStatsService.java
+++ b/service-t/src/com/android/server/net/NetworkStatsService.java
@@ -3303,6 +3303,7 @@
 
     private static native long nativeGetTotalStat(int type);
     private static native long nativeGetIfaceStat(String iface, int type);
+    private static native long nativeGetIfIndexStat(int ifindex, int type);
     private static native long nativeGetUidStat(int uid, int type);
 
     /** Initializes and registers the Perfetto Network Trace data source */