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
This commit is contained in:
Chenbo Feng
2019-02-27 19:07:39 -08:00
parent 0c419ca9eb
commit 876218abcb

View File

@@ -24,7 +24,10 @@ import static android.net.NetworkStats.UID_ALL;
import static com.android.server.NetworkManagementSocketTagger.kernelToTag; import static com.android.server.NetworkManagementSocketTagger.kernelToTag;
import android.annotation.Nullable; import android.annotation.Nullable;
import android.net.INetd;
import android.net.NetworkStats; import android.net.NetworkStats;
import android.net.util.NetdService;
import android.os.RemoteException;
import android.os.StrictMode; import android.os.StrictMode;
import android.os.SystemClock; import android.os.SystemClock;
@@ -65,6 +68,8 @@ public class NetworkStatsFactory {
private boolean mUseBpfStats; private boolean mUseBpfStats;
private INetd mNetdService;
// A persistent Snapshot since device start for eBPF stats // A persistent Snapshot since device start for eBPF stats
@GuardedBy("mPersistSnapshot") @GuardedBy("mPersistSnapshot")
private final NetworkStats mPersistSnapshot; private final NetworkStats mPersistSnapshot;
@@ -279,6 +284,19 @@ public class NetworkStatsFactory {
return stats; 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 // TODO: delete the lastStats parameter
private NetworkStats readNetworkStatsDetailInternal(int limitUid, String[] limitIfaces, private NetworkStats readNetworkStatsDetailInternal(int limitUid, String[] limitIfaces,
int limitTag, NetworkStats lastStats) throws IOException { int limitTag, NetworkStats lastStats) throws IOException {
@@ -292,6 +310,13 @@ public class NetworkStatsFactory {
} }
if (mUseBpfStats) { if (mUseBpfStats) {
synchronized (mPersistSnapshot) { 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, if (nativeReadNetworkStatsDetail(stats, mStatsXtUid.getAbsolutePath(), UID_ALL,
null, TAG_ALL, mUseBpfStats) != 0) { null, TAG_ALL, mUseBpfStats) != 0) {
throw new IOException("Failed to parse network stats"); throw new IOException("Failed to parse network stats");