Return offloaded traffic when querying from TrafficStats API
TrafficStats API are being used for querying realtime network
statistics for years. However, on certain devices, some network
traffic are produced by hardware components and not be
able to seen by kernel counters.
Thus, include statistics for those missing network traffic is
necessary. Note that the included statistics might be stale
since polling newest stats from hardware might impact system
health and not suitable for TrafficStats API use cases.
Test: atest FrameworksNetTests TetheringTests
Bug: 16229221
Change-Id: I6741c41cb5145ca8748f9b083b9c15e7e2735681
diff --git a/core/java/android/net/TrafficStats.java b/core/java/android/net/TrafficStats.java
index e7bba69..7a79ceb 100644
--- a/core/java/android/net/TrafficStats.java
+++ b/core/java/android/net/TrafficStats.java
@@ -976,11 +976,17 @@
}
}
- // NOTE: keep these in sync with android_net_TrafficStats.cpp
- private static final int TYPE_RX_BYTES = 0;
- private static final int TYPE_RX_PACKETS = 1;
- private static final int TYPE_TX_BYTES = 2;
- private static final int TYPE_TX_PACKETS = 3;
- private static final int TYPE_TCP_RX_PACKETS = 4;
- private static final int TYPE_TCP_TX_PACKETS = 5;
+ // NOTE: keep these in sync with {@code com_android_server_net_NetworkStatsService.cpp}.
+ /** {@hide} */
+ public static final int TYPE_RX_BYTES = 0;
+ /** {@hide} */
+ public static final int TYPE_RX_PACKETS = 1;
+ /** {@hide} */
+ public static final int TYPE_TX_BYTES = 2;
+ /** {@hide} */
+ public static final int TYPE_TX_PACKETS = 3;
+ /** {@hide} */
+ public static final int TYPE_TCP_RX_PACKETS = 4;
+ /** {@hide} */
+ public static final int TYPE_TCP_TX_PACKETS = 5;
}
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index 96e3c51..bfd0211 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -1083,9 +1083,11 @@
if (nativeIfaceStats == -1) {
return nativeIfaceStats;
} else {
- // TODO: When tethering offload is in use, nativeIfaceStats does not contain usage from
- // offload, add it back here.
- return nativeIfaceStats;
+ // When tethering offload is in use, nativeIfaceStats does not contain usage from
+ // offload, add it back here. Note that the included statistics might be stale
+ // since polling newest stats from hardware might impact system health and not
+ // suitable for TrafficStats API use cases.
+ return nativeIfaceStats + getProviderIfaceStats(iface, type);
}
}
@@ -1096,7 +1098,31 @@
return nativeTotalStats;
} else {
// Refer to comment in getIfaceStats
- return nativeTotalStats;
+ return nativeTotalStats + getProviderIfaceStats(IFACE_ALL, type);
+ }
+ }
+
+ private long getProviderIfaceStats(@Nullable String iface, int type) {
+ final NetworkStats providerSnapshot = getNetworkStatsFromProviders(STATS_PER_IFACE);
+ final HashSet<String> limitIfaces;
+ if (iface == IFACE_ALL) {
+ limitIfaces = null;
+ } else {
+ limitIfaces = new HashSet<>();
+ limitIfaces.add(iface);
+ }
+ final NetworkStats.Entry entry = providerSnapshot.getTotal(null, limitIfaces);
+ switch (type) {
+ case TrafficStats.TYPE_RX_BYTES:
+ return entry.rxBytes;
+ case TrafficStats.TYPE_RX_PACKETS:
+ return entry.rxPackets;
+ case TrafficStats.TYPE_TX_BYTES:
+ return entry.txBytes;
+ case TrafficStats.TYPE_TX_PACKETS:
+ return entry.txPackets;
+ default:
+ return 0;
}
}
@@ -1464,29 +1490,7 @@
final boolean persistUid = (flags & FLAG_PERSIST_UID) != 0;
final boolean persistForce = (flags & FLAG_PERSIST_FORCE) != 0;
- // Request asynchronous stats update from all providers for next poll. And wait a bit of
- // time to allow providers report-in given that normally binder call should be fast. Note
- // that size of list might be changed because addition/removing at the same time. For
- // addition, the stats of the missed provider can only be collected in next poll;
- // for removal, wait might take up to MAX_STATS_PROVIDER_POLL_WAIT_TIME_MS
- // once that happened.
- // TODO: request with a valid token.
- Trace.traceBegin(TRACE_TAG_NETWORK, "provider.requestStatsUpdate");
- final int registeredCallbackCount = mStatsProviderCbList.size();
- mStatsProviderSem.drainPermits();
- invokeForAllStatsProviderCallbacks(
- (cb) -> cb.mProvider.onRequestStatsUpdate(0 /* unused */));
- try {
- mStatsProviderSem.tryAcquire(registeredCallbackCount,
- MAX_STATS_PROVIDER_POLL_WAIT_TIME_MS, TimeUnit.MILLISECONDS);
- } catch (InterruptedException e) {
- // Strictly speaking it's possible a provider happened to deliver between the timeout
- // and the log, and that doesn't matter too much as this is just a debug log.
- Log.d(TAG, "requestStatsUpdate - providers responded "
- + mStatsProviderSem.availablePermits()
- + "/" + registeredCallbackCount + " : " + e);
- }
- Trace.traceEnd(TRACE_TAG_NETWORK);
+ performPollFromProvidersLocked();
// TODO: consider marking "untrusted" times in historical stats
final long currentTime = mClock.millis();
@@ -1531,6 +1535,33 @@
Trace.traceEnd(TRACE_TAG_NETWORK);
}
+ @GuardedBy("mStatsLock")
+ private void performPollFromProvidersLocked() {
+ // Request asynchronous stats update from all providers for next poll. And wait a bit of
+ // time to allow providers report-in given that normally binder call should be fast. Note
+ // that size of list might be changed because addition/removing at the same time. For
+ // addition, the stats of the missed provider can only be collected in next poll;
+ // for removal, wait might take up to MAX_STATS_PROVIDER_POLL_WAIT_TIME_MS
+ // once that happened.
+ // TODO: request with a valid token.
+ Trace.traceBegin(TRACE_TAG_NETWORK, "provider.requestStatsUpdate");
+ final int registeredCallbackCount = mStatsProviderCbList.size();
+ mStatsProviderSem.drainPermits();
+ invokeForAllStatsProviderCallbacks(
+ (cb) -> cb.mProvider.onRequestStatsUpdate(0 /* unused */));
+ try {
+ mStatsProviderSem.tryAcquire(registeredCallbackCount,
+ MAX_STATS_PROVIDER_POLL_WAIT_TIME_MS, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) {
+ // Strictly speaking it's possible a provider happened to deliver between the timeout
+ // and the log, and that doesn't matter too much as this is just a debug log.
+ Log.d(TAG, "requestStatsUpdate - providers responded "
+ + mStatsProviderSem.availablePermits()
+ + "/" + registeredCallbackCount + " : " + e);
+ }
+ Trace.traceEnd(TRACE_TAG_NETWORK);
+ }
+
/**
* Sample recent statistics summary into {@link EventLog}.
*/
@@ -2183,6 +2214,8 @@
}
}
+ // TODO: Remove unused definitions after removing JNI that fills these variables.
+ // See {@code com_android_server_net_NetworkStatsService.cpp}.
private static int TYPE_RX_BYTES;
private static int TYPE_RX_PACKETS;
private static int TYPE_TX_BYTES;