summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--services/core/java/com/android/server/stats/pull/StatsPullAtomService.java234
1 files changed, 180 insertions, 54 deletions
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 29c1243c299d..0b3254f53324 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -179,6 +179,7 @@ import java.util.concurrent.Executor;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
+import java.util.function.BiConsumer;
/**
* SystemService containing PullAtomCallbacks that are registered with statsd.
@@ -325,6 +326,7 @@ public class StatsPullAtomService extends SystemService {
case FrameworkStatsLog.WIFI_BYTES_TRANSFER_BY_FG_BG:
case FrameworkStatsLog.MOBILE_BYTES_TRANSFER:
case FrameworkStatsLog.MOBILE_BYTES_TRANSFER_BY_FG_BG:
+ case FrameworkStatsLog.BYTES_TRANSFER_BY_TAG_AND_METERED:
return pullDataBytesTransfer(atomTag, data);
case FrameworkStatsLog.BLUETOOTH_BYTES_TRANSFER:
return pullBluetoothBytesTransfer(atomTag, data);
@@ -641,11 +643,14 @@ public class StatsPullAtomService extends SystemService {
collectNetworkStatsSnapshotForAtom(FrameworkStatsLog.MOBILE_BYTES_TRANSFER));
mNetworkStatsBaselines.addAll(collectNetworkStatsSnapshotForAtom(
FrameworkStatsLog.MOBILE_BYTES_TRANSFER_BY_FG_BG));
+ mNetworkStatsBaselines.addAll(collectNetworkStatsSnapshotForAtom(
+ FrameworkStatsLog.BYTES_TRANSFER_BY_TAG_AND_METERED));
registerWifiBytesTransfer();
registerWifiBytesTransferBackground();
registerMobileBytesTransfer();
registerMobileBytesTransferBackground();
+ registerBytesTransferByTagAndMetered();
}
/**
@@ -787,50 +792,94 @@ public class StatsPullAtomService extends SystemService {
private static class NetworkStatsExt {
@NonNull
public final NetworkStats stats;
- public final int transport;
- public final boolean withFgbg;
+ public final int[] transports;
+ public final boolean slicedByFgbg;
+ public final boolean slicedByTag;
+ public final boolean slicedByMetered;
+
+ NetworkStatsExt(@NonNull NetworkStats stats, int[] transports, boolean slicedByFgbg) {
+ this(stats, transports, slicedByFgbg, /*slicedByTag=*/false, /*slicedByMetered=*/false);
+ }
- NetworkStatsExt(@NonNull NetworkStats stats, int transport, boolean withFgbg) {
+ NetworkStatsExt(@NonNull NetworkStats stats, int[] transports, boolean slicedByFgbg,
+ boolean slicedByTag, boolean slicedByMetered) {
this.stats = stats;
- this.transport = transport;
- this.withFgbg = withFgbg;
+
+ // Sort transports array so that we can test for equality without considering order.
+ this.transports = Arrays.copyOf(transports, transports.length);
+ Arrays.sort(this.transports);
+
+ this.slicedByFgbg = slicedByFgbg;
+ this.slicedByTag = slicedByTag;
+ this.slicedByMetered = slicedByMetered;
+ }
+
+ public boolean hasSameSlicing(@NonNull NetworkStatsExt other) {
+ return Arrays.equals(transports, other.transports) && slicedByFgbg == other.slicedByFgbg
+ && slicedByTag == other.slicedByTag && slicedByMetered == other.slicedByMetered;
}
}
@NonNull
private List<NetworkStatsExt> collectNetworkStatsSnapshotForAtom(int atomTag) {
+ List<NetworkStatsExt> ret = new ArrayList<>();
switch(atomTag) {
- case FrameworkStatsLog.WIFI_BYTES_TRANSFER:
- return collectUidNetworkStatsSnapshot(TRANSPORT_WIFI, /*withFgbg=*/false);
- case FrameworkStatsLog.WIFI_BYTES_TRANSFER_BY_FG_BG:
- return collectUidNetworkStatsSnapshot(TRANSPORT_WIFI, /*withFgbg=*/true);
- case FrameworkStatsLog.MOBILE_BYTES_TRANSFER:
- return collectUidNetworkStatsSnapshot(TRANSPORT_CELLULAR, /*withFgbg=*/false);
- case FrameworkStatsLog.MOBILE_BYTES_TRANSFER_BY_FG_BG:
- return collectUidNetworkStatsSnapshot(TRANSPORT_CELLULAR, /*withFgbg=*/true);
+ case FrameworkStatsLog.WIFI_BYTES_TRANSFER: {
+ final NetworkStats stats = getUidNetworkStatsSnapshot(TRANSPORT_WIFI,
+ /*includeTags=*/false);
+ if (stats != null) {
+ ret.add(new NetworkStatsExt(stats.groupedByUid(), new int[] {TRANSPORT_WIFI},
+ /*slicedByFgbg=*/false));
+ }
+ break;
+ }
+ case FrameworkStatsLog.WIFI_BYTES_TRANSFER_BY_FG_BG: {
+ final NetworkStats stats = getUidNetworkStatsSnapshot(TRANSPORT_WIFI,
+ /*includeTags=*/false);
+ if (stats != null) {
+ ret.add(new NetworkStatsExt(sliceNetworkStatsByUidAndFgbg(stats),
+ new int[] {TRANSPORT_WIFI}, /*slicedByFgbg=*/true));
+ }
+ break;
+ }
+ case FrameworkStatsLog.MOBILE_BYTES_TRANSFER: {
+ final NetworkStats stats = getUidNetworkStatsSnapshot(TRANSPORT_CELLULAR,
+ /*includeTags=*/false);
+ if (stats != null) {
+ ret.add(new NetworkStatsExt(stats.groupedByUid(),
+ new int[] {TRANSPORT_CELLULAR}, /*slicedByFgbg=*/false));
+ }
+ break;
+ }
+ case FrameworkStatsLog.MOBILE_BYTES_TRANSFER_BY_FG_BG: {
+ final NetworkStats stats = getUidNetworkStatsSnapshot(TRANSPORT_CELLULAR,
+ /*includeTags=*/false);
+ if (stats != null) {
+ ret.add(new NetworkStatsExt(sliceNetworkStatsByUidAndFgbg(stats),
+ new int[] {TRANSPORT_CELLULAR}, /*slicedByFgbg=*/true));
+ }
+ break;
+ }
+ case FrameworkStatsLog.BYTES_TRANSFER_BY_TAG_AND_METERED: {
+ final NetworkStats wifiStats = getUidNetworkStatsSnapshot(TRANSPORT_WIFI,
+ /*includeTags=*/true);
+ final NetworkStats cellularStats = getUidNetworkStatsSnapshot(TRANSPORT_CELLULAR,
+ /*includeTags=*/true);
+ if (wifiStats != null && cellularStats != null) {
+ final NetworkStats stats = wifiStats.add(cellularStats);
+ ret.add(new NetworkStatsExt(sliceNetworkStatsByUidTagAndMetered(stats),
+ new int[] {TRANSPORT_WIFI, TRANSPORT_CELLULAR},
+ /*slicedByFgbg=*/false, /*slicedByTag=*/true,
+ /*slicedByMetered=*/true));
+ }
+ break;
+ }
default:
throw new IllegalArgumentException("Unknown atomTag " + atomTag);
}
- }
-
- // Get a snapshot of Uid NetworkStats. The snapshot contains NetworkStats with its associated
- // information, and wrapped by a list since multiple NetworkStatsExt objects might be collected.
- @NonNull
- private List<NetworkStatsExt> collectUidNetworkStatsSnapshot(int transport, boolean withFgbg) {
- final List<NetworkStatsExt> ret = new ArrayList<>();
- final NetworkTemplate template = (transport == TRANSPORT_CELLULAR
- ? NetworkTemplate.buildTemplateMobileWithRatType(
- /*subscriptionId=*/null, NETWORK_TYPE_ALL)
- : NetworkTemplate.buildTemplateWifiWildcard());
-
- final NetworkStats stats = getUidNetworkStatsSnapshot(template, withFgbg);
- if (stats != null) {
- ret.add(new NetworkStatsExt(stats, transport, withFgbg));
- }
return ret;
}
-
private int pullDataBytesTransfer(
int atomTag, @NonNull List<StatsEvent> pulledData) {
final List<NetworkStatsExt> current = collectNetworkStatsSnapshotForAtom(atomTag);
@@ -842,22 +891,28 @@ public class StatsPullAtomService extends SystemService {
for (final NetworkStatsExt item : current) {
final NetworkStatsExt baseline = CollectionUtils.find(mNetworkStatsBaselines,
- it -> it.withFgbg == item.withFgbg && it.transport == item.transport);
+ it -> it.hasSameSlicing(item));
// No matched baseline indicates error has occurred during initialization stage,
// skip reporting anything since the snapshot is invalid.
if (baseline == null) {
- Slog.e(TAG, "baseline is null for " + atomTag + ", transport="
- + item.transport + " , withFgbg=" + item.withFgbg + ", return.");
+ Slog.e(TAG, "baseline is null for " + atomTag + ", return.");
return StatsManager.PULL_SKIP;
}
- final NetworkStatsExt diff = new NetworkStatsExt(item.stats.subtract(
- baseline.stats).removeEmptyEntries(), item.transport, item.withFgbg);
+ final NetworkStatsExt diff = new NetworkStatsExt(
+ item.stats.subtract(baseline.stats).removeEmptyEntries(), item.transports,
+ item.slicedByFgbg, item.slicedByTag, item.slicedByMetered);
// If no diff, skip.
if (diff.stats.size() == 0) continue;
- addNetworkStats(atomTag, pulledData, diff);
+ switch (atomTag) {
+ case FrameworkStatsLog.BYTES_TRANSFER_BY_TAG_AND_METERED:
+ addBytesTransferByTagAndMeteredAtoms(diff, pulledData);
+ break;
+ default:
+ addNetworkStats(atomTag, pulledData, diff);
+ }
}
return StatsManager.PULL_SUCCESS;
}
@@ -879,7 +934,7 @@ public class StatsPullAtomService extends SystemService {
}
e.writeInt(entry.uid);
e.addBooleanAnnotation(ANNOTATION_ID_IS_UID, true);
- if (statsExt.withFgbg) {
+ if (statsExt.slicedByFgbg) {
e.writeInt(entry.set);
}
e.writeLong(entry.rxBytes);
@@ -890,14 +945,38 @@ public class StatsPullAtomService extends SystemService {
}
}
+ private void addBytesTransferByTagAndMeteredAtoms(@NonNull NetworkStatsExt statsExt,
+ @NonNull List<StatsEvent> pulledData) {
+ final NetworkStats.Entry entry = new NetworkStats.Entry(); // for recycling
+ for (int i = 0; i < statsExt.stats.size(); i++) {
+ statsExt.stats.getValues(i, entry);
+ StatsEvent e = StatsEvent.newBuilder()
+ .setAtomId(FrameworkStatsLog.BYTES_TRANSFER_BY_TAG_AND_METERED)
+ .addBooleanAnnotation(ANNOTATION_ID_TRUNCATE_TIMESTAMP, true)
+ .writeInt(entry.uid)
+ .addBooleanAnnotation(ANNOTATION_ID_IS_UID, true)
+ .writeBoolean(entry.metered == NetworkStats.METERED_YES)
+ .writeInt(entry.tag)
+ .writeLong(entry.rxBytes)
+ .writeLong(entry.rxPackets)
+ .writeLong(entry.txBytes)
+ .writeLong(entry.txPackets)
+ .build();
+ pulledData.add(e);
+ }
+ }
+
/**
* Create a snapshot of NetworkStats since boot, but add 1 bucket duration before boot as a
* buffer to ensure at least one full bucket will be included.
* Note that this should be only used to calculate diff since the snapshot might contains
* some traffic before boot.
*/
- @Nullable private NetworkStats getUidNetworkStatsSnapshot(
- @NonNull NetworkTemplate template, boolean withFgbg) {
+ @Nullable private NetworkStats getUidNetworkStatsSnapshot(int transport, boolean includeTags) {
+ final NetworkTemplate template = (transport == TRANSPORT_CELLULAR)
+ ? NetworkTemplate.buildTemplateMobileWithRatType(
+ /*subscriptionId=*/null, NETWORK_TYPE_ALL)
+ : NetworkTemplate.buildTemplateWifiWildcard();
final long elapsedMillisSinceBoot = SystemClock.elapsedRealtime();
final long currentTimeInMillis = MICROSECONDS.toMillis(SystemClock.currentTimeMicro());
@@ -906,38 +985,72 @@ public class StatsPullAtomService extends SystemService {
try {
final NetworkStats stats = getNetworkStatsSession().getSummaryForAllUid(template,
currentTimeInMillis - elapsedMillisSinceBoot - bucketDuration,
- currentTimeInMillis, /*includeTags=*/false);
- return withFgbg ? rollupNetworkStatsByFgbg(stats) : stats.groupedByUid();
+ currentTimeInMillis, includeTags);
+ return stats;
} catch (RemoteException | NullPointerException e) {
- Slog.e(TAG, "Pulling netstats for " + template
- + " fgbg= " + withFgbg + " bytes has error", e);
+ Slog.e(TAG, "Pulling netstats for template=" + template + " and includeTags="
+ + includeTags + " causes error", e);
}
return null;
}
+ @NonNull private NetworkStats sliceNetworkStatsByUidAndFgbg(@NonNull NetworkStats stats) {
+ return sliceNetworkStats(stats,
+ (newEntry, oldEntry) -> {
+ newEntry.uid = oldEntry.uid;
+ newEntry.set = oldEntry.set;
+ });
+ }
+
+ @NonNull private NetworkStats sliceNetworkStatsByUidTagAndMetered(@NonNull NetworkStats stats) {
+ return sliceNetworkStats(stats,
+ (newEntry, oldEntry) -> {
+ newEntry.uid = oldEntry.uid;
+ newEntry.tag = oldEntry.tag;
+ newEntry.metered = oldEntry.metered;
+ });
+ }
+
/**
- * Allows rollups per UID but keeping the set (foreground/background) slicing.
- * Adapted from groupedByUid in frameworks/base/core/java/android/net/NetworkStats.java
+ * Slices NetworkStats along the dimensions specified in the slicer lambda and aggregates over
+ * non-sliced dimensions.
+ *
+ * This function iterates through each NetworkStats.Entry, sets its dimensions equal to the
+ * default state (with the presumption that we don't want to slice on anything), and then
+ * applies the slicer lambda to allow users to control which dimensions to slice on. This is
+ * adapted from groupedByUid within NetworkStats.java
+ *
+ * @param slicer An operation taking into two parameters, new NetworkStats.Entry and old
+ * NetworkStats.Entry, that should be used to copy state from the old to the new.
+ * This is useful for slicing by particular dimensions. For example, if we wished
+ * to slice by uid and tag, we could write the following lambda:
+ * (new, old) -> {
+ * new.uid = old.uid;
+ * new.tag = old.tag;
+ * }
+ * If no slicer is provided, the data is not sliced by any dimensions.
+ * @return new NeworkStats object appropriately sliced
*/
- @NonNull private NetworkStats rollupNetworkStatsByFgbg(@NonNull NetworkStats stats) {
+ @NonNull private NetworkStats sliceNetworkStats(@NonNull NetworkStats stats,
+ @Nullable BiConsumer<NetworkStats.Entry, NetworkStats.Entry> slicer) {
final NetworkStats ret = new NetworkStats(stats.getElapsedRealtime(), 1);
final NetworkStats.Entry entry = new NetworkStats.Entry();
+ entry.uid = NetworkStats.UID_ALL;
entry.iface = NetworkStats.IFACE_ALL;
+ entry.set = NetworkStats.SET_ALL;
entry.tag = NetworkStats.TAG_NONE;
entry.metered = NetworkStats.METERED_ALL;
entry.roaming = NetworkStats.ROAMING_ALL;
+ entry.defaultNetwork = NetworkStats.DEFAULT_NETWORK_ALL;
- int size = stats.size();
- final NetworkStats.Entry recycle = new NetworkStats.Entry(); // Used for retrieving values
- for (int i = 0; i < size; i++) {
+ final NetworkStats.Entry recycle = new NetworkStats.Entry(); // used for retrieving values
+ for (int i = 0; i < stats.size(); i++) {
stats.getValues(i, recycle);
+ if (slicer != null) {
+ slicer.accept(entry, recycle);
+ }
- // Skip specific tags, since already counted in TAG_NONE
- if (recycle.tag != NetworkStats.TAG_NONE) continue;
-
- entry.set = recycle.set; // Allows slicing by background/foreground
- entry.uid = recycle.uid;
entry.rxBytes = recycle.rxBytes;
entry.rxPackets = recycle.rxPackets;
entry.txBytes = recycle.txBytes;
@@ -987,6 +1100,19 @@ public class StatsPullAtomService extends SystemService {
);
}
+ private void registerBytesTransferByTagAndMetered() {
+ int tagId = FrameworkStatsLog.BYTES_TRANSFER_BY_TAG_AND_METERED;
+ PullAtomMetadata metadata = new PullAtomMetadata.Builder()
+ .setAdditiveFields(new int[] {4, 5, 6, 7})
+ .build();
+ mStatsManager.setPullAtomCallback(
+ tagId,
+ metadata,
+ BackgroundThread.getExecutor(),
+ mStatsCallbackImpl
+ );
+ }
+
private void registerBluetoothBytesTransfer() {
int tagId = FrameworkStatsLog.BLUETOOTH_BYTES_TRANSFER;
PullAtomMetadata metadata = new PullAtomMetadata.Builder()