Use new NetworkStats#addEntries API
Currently, NetworkStats#addEntry creates a new instance
containing the requested result instead of mutating the
original object. This creates a heavy memory footprint
when the caller invokes it in a loop because a new NetworkStats
instance is created whenever an addEntry is called.
This is a no-op change except it utilizes the newly
created API to add a list of entries at once.
This approach only clones the instance once,
thereby reducing the required memory footprint.
Test: atest CtsStatsdAtomHostTestCases:android.cts.statsdatom.net.BytesTransferredTest
Test: adb shell cmd stats pull-source 10000
Test: atest FrameworksServicesTests:com.android.server.stats.pull.netstats.NetworkStatsUtilsTest
Flag: com.android.server.stats.netstats_use_add_entries
Bug: 335680025
Change-Id: I1b8195d1cacc53e6dc77f154ac1a8108a0220c5b
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index 35003af..5324709 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -66,6 +66,7 @@
import static com.android.server.stats.pull.ProcfsMemoryUtil.readCmdlineFromProcfs;
import static com.android.server.stats.pull.ProcfsMemoryUtil.readMemorySnapshotFromProcfs;
import static com.android.server.stats.pull.netstats.NetworkStatsUtils.fromPublicNetworkStats;
+import static com.android.server.stats.pull.netstats.NetworkStatsUtils.isAddEntriesSupported;
import static libcore.io.IoUtils.closeQuietly;
@@ -1336,14 +1337,22 @@
@NonNull
private static NetworkStats removeEmptyEntries(NetworkStats stats) {
- NetworkStats ret = new NetworkStats(0, 1);
+ final ArrayList<NetworkStats.Entry> entries = new ArrayList<>();
for (NetworkStats.Entry e : stats) {
if (e.getRxBytes() != 0 || e.getRxPackets() != 0 || e.getTxBytes() != 0
|| e.getTxPackets() != 0 || e.getOperations() != 0) {
- ret = ret.addEntry(e);
+ entries.add(e);
}
}
- return ret;
+ if (isAddEntriesSupported()) {
+ return new NetworkStats(0, entries.size()).addEntries(entries);
+ } else {
+ NetworkStats outputStats = new NetworkStats(0L, 1);
+ for (NetworkStats.Entry e : entries) {
+ outputStats = outputStats.addEntry(e);
+ }
+ return outputStats;
+ }
}
private void addNetworkStats(int atomTag, @NonNull List<StatsEvent> ret,
@@ -1656,11 +1665,19 @@
@NonNull
private NetworkStats sliceNetworkStats(@NonNull NetworkStats stats,
@NonNull Function<NetworkStats.Entry, NetworkStats.Entry> slicer) {
- NetworkStats ret = new NetworkStats(0, 1);
+ final ArrayList<NetworkStats.Entry> entries = new ArrayList();
for (NetworkStats.Entry e : stats) {
- ret = ret.addEntry(slicer.apply(e));
+ entries.add(slicer.apply(e));
}
- return ret;
+ if (isAddEntriesSupported()) {
+ return new NetworkStats(0, entries.size()).addEntries(entries);
+ } else {
+ NetworkStats outputStats = new NetworkStats(0L, 1);
+ for (NetworkStats.Entry e : entries) {
+ outputStats = outputStats.addEntry(e);
+ }
+ return outputStats;
+ }
}
private void registerWifiBytesTransferBackground() {
diff --git a/services/core/java/com/android/server/stats/pull/netstats/NetworkStatsUtils.java b/services/core/java/com/android/server/stats/pull/netstats/NetworkStatsUtils.java
index de58852..0318bdd 100644
--- a/services/core/java/com/android/server/stats/pull/netstats/NetworkStatsUtils.java
+++ b/services/core/java/com/android/server/stats/pull/netstats/NetworkStatsUtils.java
@@ -24,6 +24,9 @@
import android.app.usage.NetworkStats;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.stats.Flags;
+
+import java.util.ArrayList;
/**
* Utility methods for accessing {@link android.net.NetworkStats}.
@@ -35,12 +38,21 @@
*/
public static android.net.NetworkStats fromPublicNetworkStats(
NetworkStats publiceNetworkStats) {
- android.net.NetworkStats stats = new android.net.NetworkStats(0L, 0);
+ final ArrayList<android.net.NetworkStats.Entry> entries = new ArrayList<>();
while (publiceNetworkStats.hasNextBucket()) {
NetworkStats.Bucket bucket = new NetworkStats.Bucket();
publiceNetworkStats.getNextBucket(bucket);
- final android.net.NetworkStats.Entry entry = fromBucket(bucket);
- stats = stats.addEntry(entry);
+ entries.add(fromBucket(bucket));
+ }
+ android.net.NetworkStats stats = new android.net.NetworkStats(0L, 1);
+ // The new API is only supported on devices running the mainline version of `NetworkStats`.
+ // It should always be used when available for memory efficiency.
+ if (isAddEntriesSupported()) {
+ stats = stats.addEntries(entries);
+ } else {
+ for (android.net.NetworkStats.Entry entry : entries) {
+ stats = stats.addEntry(entry);
+ }
}
return stats;
}
@@ -106,4 +118,8 @@
}
return 0;
}
+
+ public static boolean isAddEntriesSupported() {
+ return Flags.netstatsUseAddEntries();
+ }
}
diff --git a/services/core/java/com/android/server/stats/stats_flags.aconfig b/services/core/java/com/android/server/stats/stats_flags.aconfig
index 101b98e..7ef6fee 100644
--- a/services/core/java/com/android/server/stats/stats_flags.aconfig
+++ b/services/core/java/com/android/server/stats/stats_flags.aconfig
@@ -1,6 +1,20 @@
package: "com.android.server.stats"
container: "system"
+# Note: To ensure compatibility across all release configurations, initiate the ramp-up process
+# only after the 'com.android.net.flags.netstats_add_entries' flag has been fully deployed.
+# This flag provides the necessary API from the Connectivity module.
+# The flag was added because the flag 'com.android.net.flags.netstats_add_entries' for API
+# is already being rolled out, and modifying behavior during an active rollout might
+# lead to unwanted issues.
+flag {
+ name: "netstats_use_add_entries"
+ namespace: "statsd"
+ description: "Use NetworkStats#addEntries to reduce memory footprint"
+ bug: "335680025"
+ is_fixed_read_only: true
+}
+
flag {
name: "add_mobile_bytes_transfer_by_proc_state_puller"
namespace: "statsd"