Avoid excessive allocation churn in network stats.
Getting the network stats required making 3 full copies of all the
network stats since boot. Reorganize the code so we only have to
allocate incremental network stats objects.
Flag: EXEMPT bugfix
Bug: 399154099
Test: atest ConnectivityCoverageTests
Test: atest NetworkStatsIntegrationTest
Change-Id: Id531c4ffd6e0d127b8aaac38bda6a89a3d700c64
diff --git a/framework-t/src/android/net/NetworkStats.java b/framework-t/src/android/net/NetworkStats.java
index a2c4fc3..5b7dddb 100644
--- a/framework-t/src/android/net/NetworkStats.java
+++ b/framework-t/src/android/net/NetworkStats.java
@@ -494,6 +494,21 @@
return this;
}
+ /**
+ * Checks if this entry matches the given filter parameters.
+ * @param uid UID to filter for, or {@link #UID_ALL}.
+ * @param ifaces Interfaces to filter for, or {@link #INTERFACES_ALL}.
+ * @param tag Tag to filter for, or {@link #TAG_ALL}.
+ *
+ * @return true if this entry matches the given filter parameters.
+ * @hide
+ */
+ public boolean matches(int uid, String[] ifaces, int tag) {
+ return (uid == UID_ALL || uid == this.uid)
+ && (tag == TAG_ALL || tag == this.tag)
+ && (ifaces == INTERFACES_ALL || CollectionUtils.contains(ifaces, this.iface));
+ }
+
@Override
public String toString() {
final StringBuilder builder = new StringBuilder();
@@ -1379,10 +1394,7 @@
if (limitUid == UID_ALL && limitTag == TAG_ALL && limitIfaces == INTERFACES_ALL) {
return;
}
- filter(e -> (limitUid == UID_ALL || limitUid == e.uid)
- && (limitTag == TAG_ALL || limitTag == e.tag)
- && (limitIfaces == INTERFACES_ALL
- || CollectionUtils.contains(limitIfaces, e.iface)));
+ filter(e -> e.matches(limitUid, limitIfaces, limitTag));
}
/**
@@ -1410,6 +1422,34 @@
size = nextOutputEntry;
}
+ /**
+ * Make a filtered copy of the network stats.
+ *
+ * @param limitUid UID to filter for, or {@link #UID_ALL}.
+ * @param limitIfaces Interfaces to filter for, or {@link #INTERFACES_ALL}.
+ * @param limitTag Tag to filter for, or {@link #TAG_ALL}.
+ * @hide
+ */
+ public NetworkStats filteredClone(int limitUid, String[] limitIfaces, int limitTag) {
+ NetworkStats.Entry e = null;
+ int filteredSize = 0;
+ for (int i = 0; i < size; i++) {
+ e = getValues(i, e);
+ if (e.matches(limitUid, limitIfaces, limitTag)) {
+ filteredSize++;
+ }
+ }
+
+ final NetworkStats clone = new NetworkStats(elapsedRealtime, filteredSize);
+ for (int i = 0; i < size; i++) {
+ e = getValues(i, e);
+ if (e.matches(limitUid, limitIfaces, limitTag)) {
+ clone.insertEntry(e);
+ }
+ }
+ return clone;
+ }
+
/** @hide */
public void dump(String prefix, PrintWriter pw) {
pw.print(prefix);
diff --git a/service-t/src/com/android/server/net/NetworkStatsFactory.java b/service-t/src/com/android/server/net/NetworkStatsFactory.java
index c5a69c0..7d48b71 100644
--- a/service-t/src/com/android/server/net/NetworkStatsFactory.java
+++ b/service-t/src/com/android/server/net/NetworkStatsFactory.java
@@ -252,8 +252,6 @@
synchronized (mPersistentDataLock) {
// Take a reference. If this gets swapped out, we still have the old reference.
final UnderlyingNetworkInfo[] vpnArray = mUnderlyingNetworkInfos;
- // Take a defensive copy. mPersistSnapshot is mutated in some cases below
- final NetworkStats prev = mPersistSnapshot.clone();
requestSwapActiveStatsMapLocked();
// Stats are always read from the inactive map, so they must be read after the
@@ -266,11 +264,27 @@
mPersistSnapshot.setElapsedRealtime(diff.getElapsedRealtime());
mPersistSnapshot.combineAllValues(filteredDiff);
- NetworkStats adjustedStats = adjustForTunAnd464Xlat(mPersistSnapshot, prev, vpnArray);
+ // Update the stats adjusted for TunAnd464Xlat
+ // Apply 464xlat adjustments before VPN adjustments. If VPNs are using v4 on a v6 only
+ // network, the overhead is their fault.
+ // No locking here: apply464xlatAdjustments behaves fine with an add-only
+ // ConcurrentHashMap.
+ filteredDiff.apply464xlatAdjustments(mStackedIfaces);
+
+ // Migrate data usage over a VPN to the TUN network.
+ for (UnderlyingNetworkInfo info : vpnArray) {
+ filteredDiff.migrateTun(info.getOwnerUid(), info.getInterface(),
+ info.getUnderlyingInterfaces());
+ // Filter out debug entries as that may lead to over counting.
+ filteredDiff.filterDebugEntries();
+ }
+
+ // Update mTunAnd464xlatAdjustedStats with migrated stats.
+ mTunAnd464xlatAdjustedStats.combineAllValues(filteredDiff);
+ mTunAnd464xlatAdjustedStats.setElapsedRealtime(filteredDiff.getElapsedRealtime());
// Filter return values
- adjustedStats.filter(limitUid, limitIfaces, limitTag);
- return adjustedStats;
+ return mTunAnd464xlatAdjustedStats.filteredClone(limitUid, limitIfaces, limitTag);
}
}
@@ -309,33 +323,6 @@
return filteredStats;
}
- @GuardedBy("mPersistentDataLock")
- private NetworkStats adjustForTunAnd464Xlat(NetworkStats uidDetailStats,
- NetworkStats previousStats, UnderlyingNetworkInfo[] vpnArray) {
- // Calculate delta from last snapshot
- final NetworkStats delta = uidDetailStats.subtract(previousStats);
-
- // Apply 464xlat adjustments before VPN adjustments. If VPNs are using v4 on a v6 only
- // network, the overhead is their fault.
- // No locking here: apply464xlatAdjustments behaves fine with an add-only
- // ConcurrentHashMap.
- delta.apply464xlatAdjustments(mStackedIfaces);
-
- // Migrate data usage over a VPN to the TUN network.
- for (UnderlyingNetworkInfo info : vpnArray) {
- delta.migrateTun(info.getOwnerUid(), info.getInterface(),
- info.getUnderlyingInterfaces());
- // Filter out debug entries as that may lead to over counting.
- delta.filterDebugEntries();
- }
-
- // Update mTunAnd464xlatAdjustedStats with migrated delta.
- mTunAnd464xlatAdjustedStats.combineAllValues(delta);
- mTunAnd464xlatAdjustedStats.setElapsedRealtime(uidDetailStats.getElapsedRealtime());
-
- return mTunAnd464xlatAdjustedStats.clone();
- }
-
/**
* Remove stats from {@code mPersistSnapshot} and {@code mTunAnd464xlatAdjustedStats} for the
* given uids.