Refactor grouped by methods
This is a no-op refactoring, which re-write the group by methods
to use a common method that can slice NetworkStats by a given
lambda. This is useful for subsequent changes to slice NetworkStats
with different conditions.
Test: atest FrameworksNetTests:android.net.connectivity.android.net.NetworkStatsTest
Bug: 290728278
Change-Id: Ia28571021fba46fc59380798053ccdc91ddc6ae4
diff --git a/framework-t/src/android/net/NetworkStats.java b/framework-t/src/android/net/NetworkStats.java
index 8719960..0b5bb85 100644
--- a/framework-t/src/android/net/NetworkStats.java
+++ b/framework-t/src/android/net/NetworkStats.java
@@ -46,6 +46,7 @@
import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.function.Function;
import java.util.function.Predicate;
/**
@@ -455,6 +456,41 @@
return operations;
}
+ /**
+ * Set Key fields for this entry.
+ *
+ * @return this object.
+ * @hide
+ */
+ private Entry setKeys(@Nullable String iface, int uid, @State int set,
+ int tag, @Meteredness int metered, @Roaming int roaming,
+ @DefaultNetwork int defaultNetwork) {
+ this.iface = iface;
+ this.uid = uid;
+ this.set = set;
+ this.tag = tag;
+ this.metered = metered;
+ this.roaming = roaming;
+ this.defaultNetwork = defaultNetwork;
+ return this;
+ }
+
+ /**
+ * Set Value fields for this entry.
+ *
+ * @return this object.
+ * @hide
+ */
+ private Entry setValues(long rxBytes, long rxPackets, long txBytes, long txPackets,
+ long operations) {
+ this.rxBytes = rxBytes;
+ this.rxPackets = rxPackets;
+ this.txBytes = txBytes;
+ this.txPackets = txPackets;
+ this.operations = operations;
+ return this;
+ }
+
@Override
public String toString() {
final StringBuilder builder = new StringBuilder();
@@ -1210,30 +1246,21 @@
* @hide
*/
public NetworkStats groupedByIface() {
- final NetworkStats stats = new NetworkStats(elapsedRealtime, 10);
+ // Keep backward compatibility where the method filtered out tagged stats and keep the
+ // operation counts as 0. The method used to deal with uid snapshot where tagged and
+ // non-tagged stats were mixed. And this method was also in Android O API list,
+ // so it is possible OEM can access it.
+ final NetworkStats copiedStats = this.clone();
+ copiedStats.filter(e -> e.getTag() == TAG_NONE);
- final Entry entry = new Entry();
- entry.uid = UID_ALL;
- entry.set = SET_ALL;
- entry.tag = TAG_NONE;
- entry.metered = METERED_ALL;
- entry.roaming = ROAMING_ALL;
- entry.defaultNetwork = DEFAULT_NETWORK_ALL;
- entry.operations = 0L;
+ final Entry temp = new Entry();
+ final NetworkStats mappedStats = copiedStats.map(entry -> temp.setKeys(entry.getIface(),
+ UID_ALL, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL));
- for (int i = 0; i < size; i++) {
- // skip specific tags, since already counted in TAG_NONE
- if (tag[i] != TAG_NONE) continue;
-
- entry.iface = iface[i];
- entry.rxBytes = rxBytes[i];
- entry.rxPackets = rxPackets[i];
- entry.txBytes = txBytes[i];
- entry.txPackets = txPackets[i];
- stats.combineValues(entry);
+ for (int i = 0; i < mappedStats.size; i++) {
+ mappedStats.operations[i] = 0L;
}
-
- return stats;
+ return mappedStats;
}
/**
@@ -1242,30 +1269,15 @@
* @hide
*/
public NetworkStats groupedByUid() {
- final NetworkStats stats = new NetworkStats(elapsedRealtime, 10);
+ // Keep backward compatibility where the method filtered out tagged stats. The method used
+ // to deal with uid snapshot where tagged and non-tagged stats were mixed. And
+ // this method is also in Android O API list, so it is possible OEM can access it.
+ final NetworkStats copiedStats = this.clone();
+ copiedStats.filter(e -> e.getTag() == TAG_NONE);
- final Entry entry = new Entry();
- entry.iface = IFACE_ALL;
- entry.set = SET_ALL;
- entry.tag = TAG_NONE;
- entry.metered = METERED_ALL;
- entry.roaming = ROAMING_ALL;
- entry.defaultNetwork = DEFAULT_NETWORK_ALL;
-
- for (int i = 0; i < size; i++) {
- // skip specific tags, since already counted in TAG_NONE
- if (tag[i] != TAG_NONE) continue;
-
- entry.uid = uid[i];
- entry.rxBytes = rxBytes[i];
- entry.rxPackets = rxPackets[i];
- entry.txBytes = txBytes[i];
- entry.txPackets = txPackets[i];
- entry.operations = operations[i];
- stats.combineValues(entry);
- }
-
- return stats;
+ final Entry temp = new Entry();
+ return copiedStats.map(entry -> temp.setKeys(IFACE_ALL,
+ entry.getUid(), SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL));
}
/**
@@ -1302,6 +1314,29 @@
}
/**
+ * Returns a new NetworkStats object where entries are transformed.
+ *
+ * Note that because NetworkStats is more akin to a map than to a list,
+ * the entries will be grouped after they are mapped by the key fields,
+ * e.g. uid, set, tag, defaultNetwork.
+ * Value fields with the same keys will be added together.
+ */
+ @NonNull
+ private NetworkStats map(@NonNull Function<Entry, Entry> f) {
+ final NetworkStats ret = new NetworkStats(0, 1);
+ for (Entry e : this) {
+ final NetworkStats.Entry transformed = f.apply(e);
+ if (transformed == e) {
+ throw new IllegalStateException("A new entry must be created.");
+ }
+ transformed.setValues(e.getRxBytes(), e.getRxPackets(), e.getTxBytes(),
+ e.getTxPackets(), e.getOperations());
+ ret.combineValues(transformed);
+ }
+ return ret;
+ }
+
+ /**
* Only keep entries that match all specified filters.
*
* <p>This mutates the original structure in place. After this method is called,