TetherController: Process tether stats from bpf maps

Test: Manual test
1. Add two interface tether stats manually
2. Call getTetherStats() and check the content

Change-Id: I5bf7e57e67cf61829ef71fd7caccdb60d4c35213
diff --git a/server/TetherController.cpp b/server/TetherController.cpp
index a1e2ea4..16f272c 100644
--- a/server/TetherController.cpp
+++ b/server/TetherController.cpp
@@ -77,6 +77,7 @@
 const char IPV6_FORWARDING_PROC_FILE[] = "/proc/sys/net/ipv6/conf/all/forwarding";
 const char SEPARATOR[] = "|";
 constexpr const char kTcpBeLiberal[] = "/proc/sys/net/netfilter/nf_conntrack_tcp_be_liberal";
+constexpr const char kBpfOffloadInterface[] = "BPFOffloadInterface";
 
 // Chosen to match AID_DNS_TETHER, as made "friendly" by fs_config_generator.py.
 constexpr const char kDnsmasqUsername[] = "dns_tether";
@@ -162,16 +163,7 @@
     } else {
         setIpFwdEnabled();
     }
-
-    // Open BPF maps, ignoring errors because the device might not support BPF offload.
-    int fd = getTetherIngressMapFd();
-    if (fd != -1) {
-        mBpfIngressMap.reset(fd);
-    }
-    fd = getTetherStatsMapFd();
-    if (fd != -1) {
-        mBpfStatsMap.reset(fd);
-    }
+    maybeInitMaps();
 }
 
 bool TetherController::setIpFwdEnabled() {
@@ -204,6 +196,25 @@
     return setIpFwdEnabled();
 }
 
+void TetherController::maybeInitMaps() {
+    if (!bpf::isBpfSupported()) return;
+
+    // Used for parsing tether stats from BPF maps. If open the map failed, skip to open
+    // tether BPF offload maps.
+    mIfaceIndexNameMap.init(IFACE_INDEX_NAME_MAP_PATH);
+    if (!mIfaceIndexNameMap.isValid()) return;
+
+    // Open BPF maps, ignoring errors because the device might not support BPF offload.
+    int fd = getTetherIngressMapFd();
+    if (fd != -1) {
+        mBpfIngressMap.reset(fd);
+    }
+    fd = getTetherStatsMapFd();
+    if (fd != -1) {
+        mBpfStatsMap.reset(fd);
+    }
+}
+
 const std::set<std::string>& TetherController::getIpfwdRequesterList() const {
     return mForwardingRequests;
 }
@@ -988,6 +999,29 @@
         }
     }
 
+    if (mBpfStatsMap.isValid()) {
+        const auto processTetherStats = [this, &statsList](
+                                                const uint32_t& key, const TetherStatsValue& value,
+                                                const BpfMap<uint32_t, TetherStatsValue>&) {
+            auto ifname = mIfaceIndexNameMap.readValue(key);
+            if (!ifname.ok()) {
+                // Keep on going regardless to parse as much as possible.
+                return Result<void>();
+            }
+            addStats(statsList,
+                     {kBpfOffloadInterface, ifname.value().name,
+                      static_cast<int64_t>(value.rxBytes), static_cast<int64_t>(value.rxPackets),
+                      static_cast<int64_t>(value.txBytes), static_cast<int64_t>(value.txPackets)});
+            return Result<void>();
+        };
+
+        auto ret = mBpfStatsMap.iterateWithValue(processTetherStats);
+        if (!ret.ok()) {
+            // Ignore error to return the non-BPF tether stats result.
+            ALOGE("Error processing tether stats from BPF maps: %s", ret.error().message().c_str());
+        }
+    }
+
     return statsList;
 }
 
diff --git a/server/TetherController.h b/server/TetherController.h
index 7bf8d32..7835cf6 100644
--- a/server/TetherController.h
+++ b/server/TetherController.h
@@ -70,8 +70,10 @@
         int sendAllState(int daemonFd) const;
     } mDnsmasqState{};
 
+    // BPF maps, initialized by maybeInitMaps.
     bpf::BpfMap<TetherIngressKey, TetherIngressValue> mBpfIngressMap;
     bpf::BpfMap<uint32_t, TetherStatsValue> mBpfStatsMap;
+    bpf::BpfMap<uint32_t, IfaceValue> mIfaceIndexNameMap;
 
   public:
     TetherController();
@@ -183,6 +185,7 @@
     int setForwardRules(bool set, const char *intIface, const char *extIface);
     int setTetherCountingRules(bool add, const char *intIface, const char *extIface);
 
+    void maybeInitMaps();
     void maybeStartBpf(const char* extIface);
     void maybeStopBpf(const char* extIface);