Ask netd to swap stats map before reading

To avoid protentail race problem between netd and system_server when
reading the network stats map. Always inform netd before reading the
stats and let netd to do a swap between active stats map and inactive
stats map. So the system_server can safely remove the stats after
reading.

Bug: 126620214
Test: android.app.usage.cts.NetworkUsageStatsTest
      android.net.cts.TrafficStatsTest

Change-Id: I8fa37c26bec23ffca0b29b679e72ba1189f557f1
Merged-In: I8fa37c26bec23ffca0b29b679e72ba1189f557f1
(cherry picked from commit b7a5ed712570811045cd286da72defd95628fe30)
diff --git a/services/core/java/com/android/server/net/NetworkStatsFactory.java b/services/core/java/com/android/server/net/NetworkStatsFactory.java
index 174f600..69efd02 100644
--- a/services/core/java/com/android/server/net/NetworkStatsFactory.java
+++ b/services/core/java/com/android/server/net/NetworkStatsFactory.java
@@ -24,7 +24,10 @@
 import static com.android.server.NetworkManagementSocketTagger.kernelToTag;
 
 import android.annotation.Nullable;
+import android.net.INetd;
 import android.net.NetworkStats;
+import android.net.util.NetdService;
+import android.os.RemoteException;
 import android.os.StrictMode;
 import android.os.SystemClock;
 
@@ -65,6 +68,8 @@
 
     private boolean mUseBpfStats;
 
+    private INetd mNetdService;
+
     // A persistent Snapshot since device start for eBPF stats
     @GuardedBy("mPersistSnapshot")
     private final NetworkStats mPersistSnapshot;
@@ -274,6 +279,19 @@
         return stats;
     }
 
+    @GuardedBy("mPersistSnapshot")
+    private void requestSwapActiveStatsMapLocked() throws RemoteException {
+        // Ask netd to do a active map stats swap. When the binder call successfully returns,
+        // the system server should be able to safely read and clean the inactive map
+        // without race problem.
+        if (mUseBpfStats) {
+            if (mNetdService == null) {
+                mNetdService = NetdService.getInstance();
+            }
+            mNetdService.trafficSwapActiveStatsMap();
+        }
+    }
+
     // TODO: delete the lastStats parameter
     private NetworkStats readNetworkStatsDetailInternal(int limitUid, String[] limitIfaces,
             int limitTag, NetworkStats lastStats) throws IOException {
@@ -287,6 +305,13 @@
             }
             if (mUseBpfStats) {
                 synchronized (mPersistSnapshot) {
+                    try {
+                        requestSwapActiveStatsMapLocked();
+                    } catch (RemoteException e) {
+                        throw new IOException(e);
+                    }
+                    // Stats are always read from the inactive map, so they must be read after the
+                    // swap
                     if (nativeReadNetworkStatsDetail(stats, mStatsXtUid.getAbsolutePath(), UID_ALL,
                             null, TAG_ALL, mUseBpfStats) != 0) {
                         throw new IOException("Failed to parse network stats");