Skip to content

Commit 34c73ac

Browse files
committed
Sample atomic network stats buckets, full poll.
When sampling network stats, always use atomic buckets instead of interpolating. Always poll iface and UID together so we distribute into buckets equally. Move stale bucket trimming to just before writing stats. Bug: 5321340 Change-Id: I78a2226778a79c875f3668336e39ea24a7b4d5c4
1 parent 00cd1d4 commit 34c73ac

File tree

4 files changed

+87
-110
lines changed

4 files changed

+87
-110
lines changed

core/java/android/net/NetworkStatsHistory.java

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -441,10 +441,10 @@ public Entry getValues(long start, long end, long now, Entry recycle) {
441441
final long curStart = bucketStart[i];
442442
final long curEnd = curStart + bucketDuration;
443443

444-
// bucket is older than record; we're finished
445-
if (curEnd < start) break;
446-
// bucket is newer than record; keep looking
447-
if (curStart > end) continue;
444+
// bucket is older than request; we're finished
445+
if (curEnd <= start) break;
446+
// bucket is newer than request; keep looking
447+
if (curStart >= end) continue;
448448

449449
// include full value for active buckets, otherwise only fractional
450450
final boolean activeBucket = curStart < now && curEnd > now;
@@ -466,7 +466,6 @@ public Entry getValues(long start, long end, long now, Entry recycle) {
466466
if (txPackets != null) entry.txPackets += txPackets[i] * overlap / bucketDuration;
467467
if (operations != null) entry.operations += operations[i] * overlap / bucketDuration;
468468
}
469-
470469
return entry;
471470
}
472471

core/java/android/provider/Settings.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4031,8 +4031,6 @@ public static final String getBluetoothInputDevicePriorityKey(String address) {
40314031
public static final String NETSTATS_UID_MAX_HISTORY = "netstats_uid_max_history";
40324032
/** {@hide} */
40334033
public static final String NETSTATS_TAG_MAX_HISTORY = "netstats_tag_max_history";
4034-
/** {@hide} */
4035-
public static final String NETSTATS_FORCE_COMPLETE_POLL = "netstats_force_complete_poll";
40364034

40374035
/** Preferred NTP server. {@hide} */
40384036
public static final String NTP_SERVER = "ntp_server";

services/java/com/android/server/net/NetworkStatsService.java

Lines changed: 78 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@
3636
import static android.net.NetworkTemplate.buildTemplateWifi;
3737
import static android.net.TrafficStats.UID_REMOVED;
3838
import static android.net.TrafficStats.UID_TETHERING;
39-
import static android.provider.Settings.Secure.NETSTATS_FORCE_COMPLETE_POLL;
4039
import static android.provider.Settings.Secure.NETSTATS_NETWORK_BUCKET_DURATION;
4140
import static android.provider.Settings.Secure.NETSTATS_NETWORK_MAX_HISTORY;
4241
import static android.provider.Settings.Secure.NETSTATS_PERSIST_THRESHOLD;
@@ -136,15 +135,10 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
136135
private static final int MSG_PERFORM_POLL = 0x1;
137136

138137
/** Flags to control detail level of poll event. */
139-
private static final int FLAG_POLL_NETWORK = 0x1;
140-
private static final int FLAG_POLL_UID = 0x2;
141-
private static final int FLAG_POLL_TETHER = 0x3;
142138
private static final int FLAG_PERSIST_NETWORK = 0x10;
143139
private static final int FLAG_PERSIST_UID = 0x20;
144-
private static final int FLAG_FORCE_PERSIST = 0x100;
145-
146-
private static final int FLAG_POLL_ALL = FLAG_POLL_NETWORK | FLAG_POLL_UID | FLAG_POLL_TETHER;
147140
private static final int FLAG_PERSIST_ALL = FLAG_PERSIST_NETWORK | FLAG_PERSIST_UID;
141+
private static final int FLAG_PERSIST_FORCE = 0x100;
148142

149143
private final Context mContext;
150144
private final INetworkManagementService mNetworkManager;
@@ -182,7 +176,6 @@ public interface NetworkStatsSettings {
182176
public long getUidMaxHistory();
183177
public long getTagMaxHistory();
184178
public long getTimeCacheMaxAge();
185-
public boolean getForceCompletePoll();
186179
}
187180

188181
private final Object mStatsLock = new Object();
@@ -529,7 +522,7 @@ public void setUidForeground(int uid, boolean uidForeground) {
529522
@Override
530523
public void forceUpdate() {
531524
mContext.enforceCallingOrSelfPermission(READ_NETWORK_USAGE_HISTORY, TAG);
532-
performPoll(FLAG_POLL_ALL | FLAG_PERSIST_ALL);
525+
performPoll(FLAG_PERSIST_ALL);
533526
}
534527

535528
/**
@@ -561,7 +554,7 @@ public void onReceive(Context context, Intent intent) {
561554
public void onReceive(Context context, Intent intent) {
562555
// on background handler thread, and verified CONNECTIVITY_INTERNAL
563556
// permission above.
564-
performPoll(FLAG_POLL_TETHER);
557+
performPoll(FLAG_PERSIST_NETWORK);
565558
}
566559
};
567560

@@ -570,7 +563,7 @@ public void onReceive(Context context, Intent intent) {
570563
public void onReceive(Context context, Intent intent) {
571564
// on background handler thread, and verified UPDATE_DEVICE_STATS
572565
// permission above.
573-
performPoll(FLAG_POLL_ALL | FLAG_PERSIST_ALL);
566+
performPoll(FLAG_PERSIST_ALL);
574567

575568
// verify that we're watching global alert
576569
registerGlobalAlert();
@@ -617,7 +610,7 @@ public void limitReached(String limitName, String iface) {
617610
if (LIMIT_GLOBAL_ALERT.equals(limitName)) {
618611
// kick off background poll to collect network stats; UID stats
619612
// are handled during normal polling interval.
620-
final int flags = FLAG_POLL_NETWORK | FLAG_PERSIST_NETWORK;
613+
final int flags = FLAG_PERSIST_NETWORK;
621614
mHandler.obtainMessage(MSG_PERFORM_POLL, flags, 0).sendToTarget();
622615

623616
// re-arm global alert for next update
@@ -639,10 +632,9 @@ private void updateIfacesLocked() {
639632
// isn't perfect, since the kernel may already be counting traffic from
640633
// the updated network.
641634

642-
// poll both network and UID stats, but only persist network stats,
643-
// since this codepath should stay fast. UID stats will be persisted
644-
// during next alarm poll event.
645-
performPollLocked(FLAG_POLL_ALL | FLAG_PERSIST_NETWORK);
635+
// poll, but only persist network stats to keep codepath fast. UID stats
636+
// will be persisted during next alarm poll event.
637+
performPollLocked(FLAG_PERSIST_NETWORK);
646638

647639
final NetworkState[] states;
648640
try {
@@ -706,21 +698,9 @@ private void performPollLocked(int flags) {
706698
if (LOGV) Slog.v(TAG, "performPollLocked(flags=0x" + Integer.toHexString(flags) + ")");
707699
final long startRealtime = SystemClock.elapsedRealtime();
708700

709-
boolean pollNetwork = (flags & FLAG_POLL_NETWORK) != 0;
710-
boolean pollUid = (flags & FLAG_POLL_UID) != 0;
711-
boolean pollTether = (flags & FLAG_POLL_TETHER) != 0;
712-
713-
// when complete poll requested, any partial poll enables everything
714-
final boolean forceCompletePoll = mSettings.getForceCompletePoll();
715-
if (forceCompletePoll && (pollNetwork || pollUid || pollTether)) {
716-
pollNetwork = true;
717-
pollUid = true;
718-
pollTether = true;
719-
}
720-
721701
final boolean persistNetwork = (flags & FLAG_PERSIST_NETWORK) != 0;
722702
final boolean persistUid = (flags & FLAG_PERSIST_UID) != 0;
723-
final boolean forcePersist = (flags & FLAG_FORCE_PERSIST) != 0;
703+
final boolean persistForce = (flags & FLAG_PERSIST_FORCE) != 0;
724704

725705
// try refreshing time source when stale
726706
if (mTime.getCacheAge() > mSettings.getTimeCacheMaxAge()) {
@@ -733,41 +713,36 @@ private void performPollLocked(int flags) {
733713
final long threshold = mSettings.getPersistThreshold();
734714

735715
try {
736-
if (pollNetwork) {
737-
final NetworkStats networkSnapshot = mNetworkManager.getNetworkStatsSummary();
738-
performNetworkPollLocked(networkSnapshot, currentTime);
739-
740-
// persist when enough network data has occurred
741-
final NetworkStats persistNetworkDelta = computeStatsDelta(
742-
mLastPersistNetworkSnapshot, networkSnapshot, true);
743-
final boolean pastThreshold = persistNetworkDelta.getTotalBytes() > threshold;
744-
if (forcePersist || (persistNetwork && pastThreshold)) {
745-
writeNetworkStatsLocked();
746-
mLastPersistNetworkSnapshot = networkSnapshot;
747-
}
716+
// record network stats
717+
final NetworkStats networkSnapshot = mNetworkManager.getNetworkStatsSummary();
718+
performNetworkPollLocked(networkSnapshot, currentTime);
719+
720+
// persist when enough network data has occurred
721+
final NetworkStats persistNetworkDelta = computeStatsDelta(
722+
mLastPersistNetworkSnapshot, networkSnapshot, true);
723+
final boolean networkPastThreshold = persistNetworkDelta.getTotalBytes() > threshold;
724+
if (persistForce || (persistNetwork && networkPastThreshold)) {
725+
writeNetworkStatsLocked();
726+
mLastPersistNetworkSnapshot = networkSnapshot;
748727
}
749728

750-
if (pollTether) {
751-
final String[] ifacePairs = mConnManager.getTetheredIfacePairs();
752-
final NetworkStats tetherSnapshot = mNetworkManager.getNetworkStatsTethering(
753-
ifacePairs);
754-
performTetherPollLocked(tetherSnapshot, currentTime);
755-
756-
// persisted during normal UID cycle below
757-
}
758-
759-
if (pollUid) {
760-
final NetworkStats uidSnapshot = mNetworkManager.getNetworkStatsUidDetail(UID_ALL);
761-
performUidPollLocked(uidSnapshot, currentTime);
762-
763-
// persist when enough network data has occurred
764-
final NetworkStats persistUidDelta = computeStatsDelta(
765-
mLastPersistUidSnapshot, uidSnapshot, true);
766-
final boolean pastThreshold = persistUidDelta.getTotalBytes() > threshold;
767-
if (forcePersist || (persistUid && pastThreshold)) {
768-
writeUidStatsLocked();
769-
mLastPersistUidSnapshot = uidSnapshot;
770-
}
729+
// record tethering stats; persisted during normal UID cycle below
730+
final String[] ifacePairs = mConnManager.getTetheredIfacePairs();
731+
final NetworkStats tetherSnapshot = mNetworkManager.getNetworkStatsTethering(
732+
ifacePairs);
733+
performTetherPollLocked(tetherSnapshot, currentTime);
734+
735+
// record uid stats
736+
final NetworkStats uidSnapshot = mNetworkManager.getNetworkStatsUidDetail(UID_ALL);
737+
performUidPollLocked(uidSnapshot, currentTime);
738+
739+
// persist when enough network data has occurred
740+
final NetworkStats persistUidDelta = computeStatsDelta(
741+
mLastPersistUidSnapshot, uidSnapshot, true);
742+
final boolean uidPastThreshold = persistUidDelta.getTotalBytes() > threshold;
743+
if (persistForce || (persistUid && uidPastThreshold)) {
744+
writeUidStatsLocked();
745+
mLastPersistUidSnapshot = uidSnapshot;
771746
}
772747
} catch (IllegalStateException e) {
773748
Log.wtf(TAG, "problem reading network stats", e);
@@ -781,9 +756,7 @@ private void performPollLocked(int flags) {
781756
}
782757

783758
// sample stats after each full poll
784-
if (pollNetwork && pollUid) {
785-
performSample();
786-
}
759+
performSample();
787760

788761
// finally, dispatch updated event to any listeners
789762
final Intent updatedIntent = new Intent(ACTION_NETWORK_STATS_UPDATED);
@@ -813,12 +786,6 @@ private void performNetworkPollLocked(NetworkStats networkSnapshot, long current
813786
history.recordData(timeStart, currentTime, entry);
814787
}
815788

816-
// trim any history beyond max
817-
final long maxHistory = mSettings.getNetworkMaxHistory();
818-
for (NetworkStatsHistory history : mNetworkStats.values()) {
819-
history.removeBucketsBefore(currentTime - maxHistory);
820-
}
821-
822789
mLastPollNetworkSnapshot = networkSnapshot;
823790

824791
if (LOGD && unknownIface.size() > 0) {
@@ -862,20 +829,6 @@ private void performUidPollLocked(NetworkStats uidSnapshot, long currentTime) {
862829
history.recordData(timeStart, currentTime, entry);
863830
}
864831

865-
// trim any history beyond max
866-
final long maxUidHistory = mSettings.getUidMaxHistory();
867-
final long maxTagHistory = mSettings.getTagMaxHistory();
868-
for (UidStatsKey key : mUidStats.keySet()) {
869-
final NetworkStatsHistory history = mUidStats.get(key);
870-
871-
// detailed tags are trimmed sooner than summary in TAG_NONE
872-
if (key.tag == TAG_NONE) {
873-
history.removeBucketsBefore(currentTime - maxUidHistory);
874-
} else {
875-
history.removeBucketsBefore(currentTime - maxTagHistory);
876-
}
877-
}
878-
879832
mLastPollUidSnapshot = uidSnapshot;
880833
mLastPollOperationsSnapshot = mOperations;
881834
mOperations = new NetworkStats(0L, 10);
@@ -917,9 +870,13 @@ private void performTetherPollLocked(NetworkStats tetherSnapshot, long currentTi
917870
* Sample recent statistics summary into {@link EventLog}.
918871
*/
919872
private void performSample() {
920-
// take sample as total over last 4 hours
921-
final long end = mTime.hasCache() ? mTime.currentTimeMillis() : System.currentTimeMillis();
922-
final long start = end - (4 * HOUR_IN_MILLIS);
873+
final long largestBucketSize = Math.max(
874+
mSettings.getNetworkBucketDuration(), mSettings.getUidBucketDuration());
875+
876+
// take sample as atomic buckets
877+
final long now = mTime.hasCache() ? mTime.currentTimeMillis() : System.currentTimeMillis();
878+
final long end = now - (now % largestBucketSize) + largestBucketSize;
879+
final long start = end - largestBucketSize;
923880

924881
NetworkTemplate template = null;
925882
NetworkStats.Entry ifaceTotal = null;
@@ -929,21 +886,17 @@ private void performSample() {
929886
template = buildTemplateMobileAll(getActiveSubscriberId(mContext));
930887
ifaceTotal = getSummaryForNetwork(template, start, end).getTotal(ifaceTotal);
931888
uidTotal = getSummaryForAllUid(template, start, end, false).getTotal(uidTotal);
932-
EventLogTags.writeNetstatsMobileSample(
933-
ifaceTotal.rxBytes, ifaceTotal.txBytes,
934-
ifaceTotal.rxPackets, ifaceTotal.txPackets,
935-
uidTotal.rxBytes, uidTotal.txBytes,
936-
uidTotal.rxPackets, uidTotal.txPackets);
889+
EventLogTags.writeNetstatsMobileSample(ifaceTotal.rxBytes, ifaceTotal.rxPackets,
890+
ifaceTotal.txBytes, ifaceTotal.txPackets, uidTotal.rxBytes, uidTotal.rxPackets,
891+
uidTotal.txBytes, uidTotal.rxPackets);
937892

938893
// collect wifi sample
939894
template = buildTemplateWifi();
940895
ifaceTotal = getSummaryForNetwork(template, start, end).getTotal(ifaceTotal);
941896
uidTotal = getSummaryForAllUid(template, start, end, false).getTotal(uidTotal);
942-
EventLogTags.writeNetstatsWifiSample(
943-
ifaceTotal.rxBytes, ifaceTotal.txBytes,
944-
ifaceTotal.rxPackets, ifaceTotal.txPackets,
945-
uidTotal.rxBytes, uidTotal.txBytes,
946-
uidTotal.rxPackets, uidTotal.txPackets);
897+
EventLogTags.writeNetstatsWifiSample(ifaceTotal.rxBytes, ifaceTotal.rxPackets,
898+
ifaceTotal.txBytes, ifaceTotal.txPackets, uidTotal.rxBytes, uidTotal.rxPackets,
899+
uidTotal.txBytes, uidTotal.rxPackets);
947900
}
948901

949902
/**
@@ -1143,6 +1096,15 @@ private void writeNetworkStatsLocked() {
11431096

11441097
// TODO: consider duplicating stats and releasing lock while writing
11451098

1099+
// trim any history beyond max
1100+
if (mTime.hasCache()) {
1101+
final long currentTime = mTime.currentTimeMillis();
1102+
final long maxHistory = mSettings.getNetworkMaxHistory();
1103+
for (NetworkStatsHistory history : mNetworkStats.values()) {
1104+
history.removeBucketsBefore(currentTime - maxHistory);
1105+
}
1106+
}
1107+
11461108
FileOutputStream fos = null;
11471109
try {
11481110
fos = mNetworkFile.startWrite();
@@ -1178,6 +1140,23 @@ private void writeUidStatsLocked() {
11781140

11791141
// TODO: consider duplicating stats and releasing lock while writing
11801142

1143+
// trim any history beyond max
1144+
if (mTime.hasCache()) {
1145+
final long currentTime = mTime.currentTimeMillis();
1146+
final long maxUidHistory = mSettings.getUidMaxHistory();
1147+
final long maxTagHistory = mSettings.getTagMaxHistory();
1148+
for (UidStatsKey key : mUidStats.keySet()) {
1149+
final NetworkStatsHistory history = mUidStats.get(key);
1150+
1151+
// detailed tags are trimmed sooner than summary in TAG_NONE
1152+
if (key.tag == TAG_NONE) {
1153+
history.removeBucketsBefore(currentTime - maxUidHistory);
1154+
} else {
1155+
history.removeBucketsBefore(currentTime - maxTagHistory);
1156+
}
1157+
}
1158+
}
1159+
11811160
// build UidStatsKey lists grouped by ident
11821161
final HashMap<NetworkIdentitySet, ArrayList<UidStatsKey>> keysByIdent = Maps.newHashMap();
11831162
for (UidStatsKey key : mUidStats.keySet()) {
@@ -1242,7 +1221,7 @@ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
12421221
}
12431222

12441223
if (argSet.contains("poll")) {
1245-
performPollLocked(FLAG_POLL_ALL | FLAG_PERSIST_ALL | FLAG_FORCE_PERSIST);
1224+
performPollLocked(FLAG_PERSIST_ALL | FLAG_PERSIST_FORCE);
12461225
pw.println("Forced poll");
12471226
return;
12481227
}
@@ -1470,8 +1449,5 @@ public long getTagMaxHistory() {
14701449
public long getTimeCacheMaxAge() {
14711450
return DAY_IN_MILLIS;
14721451
}
1473-
public boolean getForceCompletePoll() {
1474-
return getSecureBoolean(NETSTATS_FORCE_COMPLETE_POLL, false);
1475-
}
14761452
}
14771453
}

services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,11 @@ public void testStatsRebootPersist() throws Exception {
272272

273273
// graceful shutdown system, which should trigger persist of stats, and
274274
// clear any values in memory.
275+
expectCurrentTime();
276+
expectDefaultSettings();
277+
replay();
275278
mServiceContext.sendBroadcast(new Intent(Intent.ACTION_SHUTDOWN));
279+
verifyAndReset();
276280

277281
// talk with zombie service to assert stats have gone; and assert that
278282
// we persisted them to file.
@@ -487,6 +491,7 @@ public void testUidRemovedIsMoved() throws Exception {
487491

488492
// now pretend two UIDs are uninstalled, which should migrate stats to
489493
// special "removed" bucket.
494+
expectCurrentTime();
490495
expectDefaultSettings();
491496
replay();
492497
final Intent intent = new Intent(ACTION_UID_REMOVED);
@@ -758,7 +763,6 @@ private void expectSettings(long persistThreshold, long bucketDuration, long max
758763
expect(mSettings.getUidMaxHistory()).andReturn(maxHistory).anyTimes();
759764
expect(mSettings.getTagMaxHistory()).andReturn(maxHistory).anyTimes();
760765
expect(mSettings.getTimeCacheMaxAge()).andReturn(DAY_IN_MILLIS).anyTimes();
761-
expect(mSettings.getForceCompletePoll()).andReturn(false).anyTimes();
762766
}
763767

764768
private void expectCurrentTime() throws Exception {

0 commit comments

Comments
 (0)