Track xtables summary, move tether stats, time.
Begin tracking xtables summary of data usage to compare with values
reported from /proc/net/dev. Roll tethering directly into UID stats
to trigger UID stats persisting when crossing threshold.
Include xtables summary and authoritative time in samples.
Bug: 5373561, 5397882, 5381980
Change-Id: Ib7945522caadfbe0864fdf391582dc820f4f371e
diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java
index f3be39c..e554975 100644
--- a/core/java/android/net/NetworkStats.java
+++ b/core/java/android/net/NetworkStats.java
@@ -272,6 +272,17 @@
}
/**
+ * Combine all values from another {@link NetworkStats} into this object.
+ */
+ public void combineAllValues(NetworkStats another) {
+ NetworkStats.Entry entry = null;
+ for (int i = 0; i < another.size; i++) {
+ entry = another.getValues(i, entry);
+ combineValues(entry);
+ }
+ }
+
+ /**
* Find first stats index that matches the requested parameters.
*/
public int findIndex(String iface, int uid, int set, int tag) {
@@ -456,6 +467,34 @@
return result;
}
+ /**
+ * Return total statistics grouped by {@link #iface}; doesn't mutate the
+ * original structure.
+ */
+ public NetworkStats groupedByIface() {
+ final NetworkStats stats = new NetworkStats(elapsedRealtime, 10);
+
+ final Entry entry = new Entry();
+ entry.uid = UID_ALL;
+ entry.set = SET_ALL;
+ entry.tag = TAG_NONE;
+ entry.operations = 0L;
+
+ 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);
+ }
+
+ return stats;
+ }
+
public void dump(String prefix, PrintWriter pw) {
pw.print(prefix);
pw.print("NetworkStats: elapsedRealtime="); pw.println(elapsedRealtime);
diff --git a/core/tests/coretests/src/android/net/NetworkStatsTest.java b/core/tests/coretests/src/android/net/NetworkStatsTest.java
index c36685d..d78d2ef 100644
--- a/core/tests/coretests/src/android/net/NetworkStatsTest.java
+++ b/core/tests/coretests/src/android/net/NetworkStatsTest.java
@@ -18,6 +18,8 @@
import static android.net.NetworkStats.SET_DEFAULT;
import static android.net.NetworkStats.SET_FOREGROUND;
+import static android.net.NetworkStats.SET_ALL;
+import static android.net.NetworkStats.IFACE_ALL;
import static android.net.NetworkStats.TAG_NONE;
import static android.net.NetworkStats.UID_ALL;
@@ -176,8 +178,63 @@
assertEquals(64L, uidTag.getTotalBytes());
}
+ public void testGroupedByIfaceEmpty() throws Exception {
+ final NetworkStats uidStats = new NetworkStats(TEST_START, 3);
+ final NetworkStats grouped = uidStats.groupedByIface();
+
+ assertEquals(0, uidStats.size());
+ assertEquals(0, grouped.size());
+ }
+
+ public void testGroupedByIfaceAll() throws Exception {
+ final NetworkStats uidStats = new NetworkStats(TEST_START, 3)
+ .addValues(IFACE_ALL, 100, SET_ALL, TAG_NONE, 128L, 8L, 0L, 2L, 20L)
+ .addValues(IFACE_ALL, 101, SET_FOREGROUND, TAG_NONE, 128L, 8L, 0L, 2L, 20L);
+ final NetworkStats grouped = uidStats.groupedByIface();
+
+ assertEquals(2, uidStats.size());
+ assertEquals(1, grouped.size());
+
+ assertValues(grouped, 0, IFACE_ALL, UID_ALL, SET_ALL, TAG_NONE, 256L, 16L, 0L, 4L, 0L);
+ }
+
+ public void testGroupedByIface() throws Exception {
+ final NetworkStats uidStats = new NetworkStats(TEST_START, 3)
+ .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 128L, 8L, 0L, 2L, 20L)
+ .addValues(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, 512L, 32L, 0L, 0L, 0L)
+ .addValues(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, 64L, 4L, 0L, 0L, 0L)
+ .addValues(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, 512L, 32L, 0L, 0L, 0L)
+ .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 128L, 8L, 0L, 0L, 0L)
+ .addValues(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, 128L, 8L, 0L, 0L, 0L);
+
+ final NetworkStats grouped = uidStats.groupedByIface();
+
+ assertEquals(6, uidStats.size());
+
+ assertEquals(2, grouped.size());
+ assertValues(grouped, 0, TEST_IFACE, UID_ALL, SET_ALL, TAG_NONE, 256L, 16L, 0L, 2L, 0L);
+ assertValues(grouped, 1, TEST_IFACE2, UID_ALL, SET_ALL, TAG_NONE, 1024L, 64L, 0L, 0L, 0L);
+ }
+
+ public void testAddAllValues() {
+ final NetworkStats first = new NetworkStats(TEST_START, 5)
+ .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 32L, 0L, 0L, 0L, 0L)
+ .addValues(TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, 32L, 0L, 0L, 0L, 0L);
+
+ final NetworkStats second = new NetworkStats(TEST_START, 2)
+ .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 32L, 0L, 0L, 0L, 0L)
+ .addValues(TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, 32L, 0L, 0L, 0L, 0L);
+
+ first.combineAllValues(second);
+
+ assertEquals(3, first.size());
+ assertValues(first, 0, TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 64L, 0L, 0L, 0L, 0L);
+ assertValues(first, 1, TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, 32L, 0L, 0L, 0L, 0L);
+ assertValues(first, 2, TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, 32L, 0L, 0L, 0L, 0L);
+ }
+
private static void assertValues(NetworkStats stats, int index, String iface, int uid, int set,
- int tag, long rxBytes, long rxPackets, long txBytes, long txPackets, int operations) {
+ int tag, long rxBytes, long rxPackets, long txBytes, long txPackets, long operations) {
final NetworkStats.Entry entry = stats.getValues(index, null);
assertEquals(iface, entry.iface);
assertEquals(uid, entry.uid);
diff --git a/services/java/com/android/server/EventLogTags.logtags b/services/java/com/android/server/EventLogTags.logtags
index 99f6b8e..a7eff93 100644
--- a/services/java/com/android/server/EventLogTags.logtags
+++ b/services/java/com/android/server/EventLogTags.logtags
@@ -142,5 +142,5 @@
# ---------------------------
# NetworkStatsService.java
# ---------------------------
-51100 netstats_mobile_sample (iface_rx_bytes|2|2),(iface_tx_bytes|2|2),(iface_rx_pkts|2|1),(iface_tx_pkts|2|1),(uid_rx_bytes|2|2),(uid_tx_bytes|2|2),(uid_rx_pkts|2|1),(uid_tx_pkts|2|1)
-51101 netstats_wifi_sample (iface_rx_bytes|2|2),(iface_tx_bytes|2|2),(iface_rx_pkts|2|1),(iface_tx_pkts|2|1),(uid_rx_bytes|2|2),(uid_tx_bytes|2|2),(uid_rx_pkts|2|1),(uid_tx_pkts|2|1)
+51100 netstats_mobile_sample (dev_rx_bytes|2|2),(dev_tx_bytes|2|2),(dev_rx_pkts|2|1),(dev_tx_pkts|2|1),(xt_rx_bytes|2|2),(xt_tx_bytes|2|2),(xt_rx_pkts|2|1),(xt_tx_pkts|2|1),(uid_rx_bytes|2|2),(uid_tx_bytes|2|2),(uid_rx_pkts|2|1),(uid_tx_pkts|2|1),(trusted_time|2|3)
+51101 netstats_wifi_sample (dev_rx_bytes|2|2),(dev_tx_bytes|2|2),(dev_rx_pkts|2|1),(dev_tx_pkts|2|1),(xt_rx_bytes|2|2),(xt_tx_bytes|2|2),(xt_rx_pkts|2|1),(xt_tx_pkts|2|1),(uid_rx_bytes|2|2),(uid_tx_bytes|2|2),(uid_rx_pkts|2|1),(uid_tx_pkts|2|1),(trusted_time|2|3)
diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java
index aacaa6a..6baf5fe 100644
--- a/services/java/com/android/server/NetworkManagementService.java
+++ b/services/java/com/android/server/NetworkManagementService.java
@@ -21,6 +21,7 @@
import static android.net.NetworkStats.IFACE_ALL;
import static android.net.NetworkStats.SET_DEFAULT;
import static android.net.NetworkStats.TAG_NONE;
+import static android.net.TrafficStats.UID_TETHERING;
import static android.net.NetworkStats.UID_ALL;
import static android.provider.Settings.Secure.NETSTATS_ENABLED;
import static com.android.server.NetworkManagementSocketTagger.PROP_QTAGUID_ENABLED;
@@ -1522,7 +1523,7 @@
try {
final NetworkStats.Entry entry = new NetworkStats.Entry();
entry.iface = ifaceIn;
- entry.uid = UID_ALL;
+ entry.uid = UID_TETHERING;
entry.set = SET_DEFAULT;
entry.tag = TAG_NONE;
entry.rxBytes = Long.parseLong(tok[3]);
diff --git a/services/java/com/android/server/net/NetworkStatsService.java b/services/java/com/android/server/net/NetworkStatsService.java
index aa46795..e5f0a77 100644
--- a/services/java/com/android/server/net/NetworkStatsService.java
+++ b/services/java/com/android/server/net/NetworkStatsService.java
@@ -35,7 +35,6 @@
import static android.net.NetworkTemplate.buildTemplateMobileAll;
import static android.net.NetworkTemplate.buildTemplateWifi;
import static android.net.TrafficStats.UID_REMOVED;
-import static android.net.TrafficStats.UID_TETHERING;
import static android.provider.Settings.Secure.NETSTATS_NETWORK_BUCKET_DURATION;
import static android.provider.Settings.Secure.NETSTATS_NETWORK_MAX_HISTORY;
import static android.provider.Settings.Secure.NETSTATS_PERSIST_THRESHOLD;
@@ -72,7 +71,6 @@
import android.net.NetworkStats;
import android.net.NetworkStatsHistory;
import android.net.NetworkTemplate;
-import android.net.TrafficStats;
import android.os.Binder;
import android.os.Environment;
import android.os.Handler;
@@ -140,11 +138,14 @@
private static final int MSG_UPDATE_IFACES = 2;
/** Flags to control detail level of poll event. */
- private static final int FLAG_PERSIST_NETWORK = 0x10;
- private static final int FLAG_PERSIST_UID = 0x20;
+ private static final int FLAG_PERSIST_NETWORK = 0x1;
+ private static final int FLAG_PERSIST_UID = 0x2;
private static final int FLAG_PERSIST_ALL = FLAG_PERSIST_NETWORK | FLAG_PERSIST_UID;
private static final int FLAG_PERSIST_FORCE = 0x100;
+ /** Sample recent usage after each poll event. */
+ private static final boolean ENABLE_SAMPLE_AFTER_POLL = true;
+
private final Context mContext;
private final INetworkManagementService mNetworkManager;
private final IAlarmManager mAlarmManager;
@@ -188,20 +189,23 @@
/** Set of currently active ifaces. */
private HashMap<String, NetworkIdentitySet> mActiveIfaces = Maps.newHashMap();
- /** Set of historical network layer stats for known networks. */
- private HashMap<NetworkIdentitySet, NetworkStatsHistory> mNetworkStats = Maps.newHashMap();
- /** Set of historical network layer stats for known UIDs. */
+ /** Set of historical {@code dev} stats for known networks. */
+ private HashMap<NetworkIdentitySet, NetworkStatsHistory> mNetworkDevStats = Maps.newHashMap();
+ /** Set of historical {@code xtables} stats for known networks. */
+ private HashMap<NetworkIdentitySet, NetworkStatsHistory> mNetworkXtStats = Maps.newHashMap();
+ /** Set of historical {@code xtables} stats for known UIDs. */
private HashMap<UidStatsKey, NetworkStatsHistory> mUidStats = Maps.newHashMap();
/** Flag if {@link #mUidStats} have been loaded from disk. */
private boolean mUidStatsLoaded = false;
- private NetworkStats mLastPollNetworkSnapshot;
+ private NetworkStats mLastPollNetworkDevSnapshot;
+ private NetworkStats mLastPollNetworkXtSnapshot;
private NetworkStats mLastPollUidSnapshot;
private NetworkStats mLastPollOperationsSnapshot;
- private NetworkStats mLastPollTetherSnapshot;
- private NetworkStats mLastPersistNetworkSnapshot;
+ private NetworkStats mLastPersistNetworkDevSnapshot;
+ private NetworkStats mLastPersistNetworkXtSnapshot;
private NetworkStats mLastPersistUidSnapshot;
/** Current counter sets for each UID. */
@@ -213,7 +217,8 @@
private final HandlerThread mHandlerThread;
private final Handler mHandler;
- private final AtomicFile mNetworkFile;
+ private final AtomicFile mNetworkDevFile;
+ private final AtomicFile mNetworkXtFile;
private final AtomicFile mUidFile;
public NetworkStatsService(
@@ -244,7 +249,8 @@
mHandlerThread.start();
mHandler = new Handler(mHandlerThread.getLooper(), mHandlerCallback);
- mNetworkFile = new AtomicFile(new File(systemDir, "netstats.bin"));
+ mNetworkDevFile = new AtomicFile(new File(systemDir, "netstats.bin"));
+ mNetworkXtFile = new AtomicFile(new File(systemDir, "netstats_xt.bin"));
mUidFile = new AtomicFile(new File(systemDir, "netstats_uid.bin"));
}
@@ -257,7 +263,8 @@
// read historical network stats from disk, since policy service
// might need them right away. we delay loading detailed UID stats
// until actually needed.
- readNetworkStatsLocked();
+ readNetworkDevStatsLocked();
+ readNetworkXtStatsLocked();
}
// watch for network interfaces to be claimed
@@ -306,11 +313,13 @@
mTeleManager.listen(mPhoneListener, LISTEN_NONE);
- writeNetworkStatsLocked();
+ writeNetworkDevStatsLocked();
+ writeNetworkXtStatsLocked();
if (mUidStatsLoaded) {
writeUidStatsLocked();
}
- mNetworkStats.clear();
+ mNetworkDevStats.clear();
+ mNetworkXtStats.clear();
mUidStats.clear();
mUidStatsLoaded = false;
}
@@ -355,14 +364,26 @@
@Override
public NetworkStatsHistory getHistoryForNetwork(NetworkTemplate template, int fields) {
mContext.enforceCallingOrSelfPermission(READ_NETWORK_USAGE_HISTORY, TAG);
+ return getHistoryForNetworkDev(template, fields);
+ }
+ private NetworkStatsHistory getHistoryForNetworkDev(NetworkTemplate template, int fields) {
+ return getHistoryForNetwork(template, fields, mNetworkDevStats);
+ }
+
+ private NetworkStatsHistory getHistoryForNetworkXt(NetworkTemplate template, int fields) {
+ return getHistoryForNetwork(template, fields, mNetworkXtStats);
+ }
+
+ private NetworkStatsHistory getHistoryForNetwork(NetworkTemplate template, int fields,
+ HashMap<NetworkIdentitySet, NetworkStatsHistory> source) {
synchronized (mStatsLock) {
// combine all interfaces that match template
final NetworkStatsHistory combined = new NetworkStatsHistory(
mSettings.getNetworkBucketDuration(), estimateNetworkBuckets(), fields);
- for (NetworkIdentitySet ident : mNetworkStats.keySet()) {
+ for (NetworkIdentitySet ident : source.keySet()) {
if (templateMatches(template, ident)) {
- final NetworkStatsHistory history = mNetworkStats.get(ident);
+ final NetworkStatsHistory history = source.get(ident);
if (history != null) {
combined.recordEntireHistory(history);
}
@@ -399,7 +420,19 @@
@Override
public NetworkStats getSummaryForNetwork(NetworkTemplate template, long start, long end) {
mContext.enforceCallingOrSelfPermission(READ_NETWORK_USAGE_HISTORY, TAG);
+ return getSummaryForNetworkDev(template, start, end);
+ }
+ private NetworkStats getSummaryForNetworkDev(NetworkTemplate template, long start, long end) {
+ return getSummaryForNetwork(template, start, end, mNetworkDevStats);
+ }
+
+ private NetworkStats getSummaryForNetworkXt(NetworkTemplate template, long start, long end) {
+ return getSummaryForNetwork(template, start, end, mNetworkXtStats);
+ }
+
+ private NetworkStats getSummaryForNetwork(NetworkTemplate template, long start, long end,
+ HashMap<NetworkIdentitySet, NetworkStatsHistory> source) {
synchronized (mStatsLock) {
// use system clock to be externally consistent
final long now = System.currentTimeMillis();
@@ -409,9 +442,9 @@
NetworkStatsHistory.Entry historyEntry = null;
// combine total from all interfaces that match template
- for (NetworkIdentitySet ident : mNetworkStats.keySet()) {
+ for (NetworkIdentitySet ident : source.keySet()) {
if (templateMatches(template, ident)) {
- final NetworkStatsHistory history = mNetworkStats.get(ident);
+ final NetworkStatsHistory history = source.get(ident);
historyEntry = history.getValues(start, end, now, historyEntry);
entry.iface = IFACE_ALL;
@@ -716,8 +749,9 @@
*/
private void bootstrapStats() {
try {
- mLastPollNetworkSnapshot = mNetworkManager.getNetworkStatsSummary();
mLastPollUidSnapshot = mNetworkManager.getNetworkStatsUidDetail(UID_ALL);
+ mLastPollNetworkDevSnapshot = mNetworkManager.getNetworkStatsSummary();
+ mLastPollNetworkXtSnapshot = computeNetworkXtSnapshotFromUid(mLastPollUidSnapshot);
mLastPollOperationsSnapshot = new NetworkStats(0L, 0);
} catch (IllegalStateException e) {
Slog.w(TAG, "problem reading network stats: " + e);
@@ -759,42 +793,56 @@
: System.currentTimeMillis();
final long threshold = mSettings.getPersistThreshold();
+ final NetworkStats uidSnapshot;
+ final NetworkStats networkXtSnapshot;
+ final NetworkStats networkDevSnapshot;
try {
- // record tethering stats; persisted during normal UID cycle below
- final String[] ifacePairs = mConnManager.getTetheredIfacePairs();
+ // collect any tethering stats
+ final String[] tetheredIfacePairs = mConnManager.getTetheredIfacePairs();
final NetworkStats tetherSnapshot = mNetworkManager.getNetworkStatsTethering(
- ifacePairs);
- performTetherPollLocked(tetherSnapshot, currentTime);
+ tetheredIfacePairs);
- // record uid stats
- final NetworkStats uidSnapshot = mNetworkManager.getNetworkStatsUidDetail(UID_ALL);
+ // record uid stats, folding in tethering stats
+ uidSnapshot = mNetworkManager.getNetworkStatsUidDetail(UID_ALL);
+ uidSnapshot.combineAllValues(tetherSnapshot);
performUidPollLocked(uidSnapshot, currentTime);
- // persist when enough network data has occurred
- final NetworkStats persistUidDelta = computeStatsDelta(
- mLastPersistUidSnapshot, uidSnapshot, true);
- final boolean uidPastThreshold = persistUidDelta.getTotalBytes() > threshold;
- if (persistForce || (persistUid && uidPastThreshold)) {
- writeUidStatsLocked();
- mLastPersistUidSnapshot = uidSnapshot;
- }
+ // record dev network stats
+ networkDevSnapshot = mNetworkManager.getNetworkStatsSummary();
+ performNetworkDevPollLocked(networkDevSnapshot, currentTime);
- // record network stats
- final NetworkStats networkSnapshot = mNetworkManager.getNetworkStatsSummary();
- performNetworkPollLocked(networkSnapshot, currentTime);
+ // record xt network stats
+ networkXtSnapshot = computeNetworkXtSnapshotFromUid(uidSnapshot);
+ performNetworkXtPollLocked(networkXtSnapshot, currentTime);
- // persist when enough network data has occurred
- final NetworkStats persistNetworkDelta = computeStatsDelta(
- mLastPersistNetworkSnapshot, networkSnapshot, true);
- final boolean networkPastThreshold = persistNetworkDelta.getTotalBytes() > threshold;
- if (persistForce || (persistNetwork && networkPastThreshold)) {
- writeNetworkStatsLocked();
- mLastPersistNetworkSnapshot = networkSnapshot;
- }
} catch (IllegalStateException e) {
Log.wtf(TAG, "problem reading network stats", e);
+ return;
} catch (RemoteException e) {
// ignored; service lives in system_server
+ return;
+ }
+
+ // persist when enough network data has occurred
+ final long persistNetworkDevDelta = computeStatsDelta(
+ mLastPersistNetworkDevSnapshot, networkDevSnapshot, true).getTotalBytes();
+ final long persistNetworkXtDelta = computeStatsDelta(
+ mLastPersistNetworkXtSnapshot, networkXtSnapshot, true).getTotalBytes();
+ final boolean networkOverThreshold = persistNetworkDevDelta > threshold
+ || persistNetworkXtDelta > threshold;
+ if (persistForce || (persistNetwork && networkOverThreshold)) {
+ writeNetworkDevStatsLocked();
+ writeNetworkXtStatsLocked();
+ mLastPersistNetworkDevSnapshot = networkDevSnapshot;
+ mLastPersistNetworkXtSnapshot = networkXtSnapshot;
+ }
+
+ // persist when enough uid data has occurred
+ final long persistUidDelta = computeStatsDelta(mLastPersistUidSnapshot, uidSnapshot, true)
+ .getTotalBytes();
+ if (persistForce || (persistUid && persistUidDelta > threshold)) {
+ writeUidStatsLocked();
+ mLastPersistUidSnapshot = uidSnapshot;
}
if (LOGV) {
@@ -802,8 +850,10 @@
Slog.v(TAG, "performPollLocked() took " + duration + "ms");
}
- // sample stats after each full poll
- performSample();
+ if (ENABLE_SAMPLE_AFTER_POLL) {
+ // sample stats after each full poll
+ performSample();
+ }
// finally, dispatch updated event to any listeners
final Intent updatedIntent = new Intent(ACTION_NETWORK_STATS_UPDATED);
@@ -812,12 +862,13 @@
}
/**
- * Update {@link #mNetworkStats} historical usage.
+ * Update {@link #mNetworkDevStats} historical usage.
*/
- private void performNetworkPollLocked(NetworkStats networkSnapshot, long currentTime) {
+ private void performNetworkDevPollLocked(NetworkStats networkDevSnapshot, long currentTime) {
final HashSet<String> unknownIface = Sets.newHashSet();
- final NetworkStats delta = computeStatsDelta(mLastPollNetworkSnapshot, networkSnapshot, false);
+ final NetworkStats delta = computeStatsDelta(
+ mLastPollNetworkDevSnapshot, networkDevSnapshot, false);
final long timeStart = currentTime - delta.getElapsedRealtime();
NetworkStats.Entry entry = null;
@@ -829,14 +880,44 @@
continue;
}
- final NetworkStatsHistory history = findOrCreateNetworkStatsLocked(ident);
+ final NetworkStatsHistory history = findOrCreateNetworkDevStatsLocked(ident);
history.recordData(timeStart, currentTime, entry);
}
- mLastPollNetworkSnapshot = networkSnapshot;
+ mLastPollNetworkDevSnapshot = networkDevSnapshot;
if (LOGD && unknownIface.size() > 0) {
- Slog.w(TAG, "unknown interfaces " + unknownIface.toString() + ", ignoring those stats");
+ Slog.w(TAG, "unknown dev interfaces " + unknownIface + ", ignoring those stats");
+ }
+ }
+
+ /**
+ * Update {@link #mNetworkXtStats} historical usage.
+ */
+ private void performNetworkXtPollLocked(NetworkStats networkXtSnapshot, long currentTime) {
+ final HashSet<String> unknownIface = Sets.newHashSet();
+
+ final NetworkStats delta = computeStatsDelta(
+ mLastPollNetworkXtSnapshot, networkXtSnapshot, false);
+ final long timeStart = currentTime - delta.getElapsedRealtime();
+
+ NetworkStats.Entry entry = null;
+ for (int i = 0; i < delta.size(); i++) {
+ entry = delta.getValues(i, entry);
+ final NetworkIdentitySet ident = mActiveIfaces.get(entry.iface);
+ if (ident == null) {
+ unknownIface.add(entry.iface);
+ continue;
+ }
+
+ final NetworkStatsHistory history = findOrCreateNetworkXtStatsLocked(ident);
+ history.recordData(timeStart, currentTime, entry);
+ }
+
+ mLastPollNetworkXtSnapshot = networkXtSnapshot;
+
+ if (LOGD && unknownIface.size() > 0) {
+ Slog.w(TAG, "unknown xt interfaces " + unknownIface + ", ignoring those stats");
}
}
@@ -882,38 +963,6 @@
}
/**
- * Update {@link #mUidStats} historical usage for
- * {@link TrafficStats#UID_TETHERING} based on tethering statistics.
- */
- private void performTetherPollLocked(NetworkStats tetherSnapshot, long currentTime) {
- ensureUidStatsLoadedLocked();
-
- final NetworkStats delta = computeStatsDelta(
- mLastPollTetherSnapshot, tetherSnapshot, false);
- final long timeStart = currentTime - delta.getElapsedRealtime();
-
- NetworkStats.Entry entry = null;
- for (int i = 0; i < delta.size(); i++) {
- entry = delta.getValues(i, entry);
- final NetworkIdentitySet ident = mActiveIfaces.get(entry.iface);
- if (ident == null) {
- if (entry.rxBytes > 0 || entry.rxPackets > 0 || entry.txBytes > 0
- || entry.txPackets > 0) {
- Log.w(TAG, "dropping tether delta from unknown iface: " + entry);
- }
- continue;
- }
-
- final NetworkStatsHistory history = findOrCreateUidStatsLocked(
- ident, UID_TETHERING, SET_DEFAULT, TAG_NONE);
- history.recordData(timeStart, currentTime, entry);
- }
-
- // normal UID poll will trim any history beyond max
- mLastPollTetherSnapshot = tetherSnapshot;
- }
-
- /**
* Sample recent statistics summary into {@link EventLog}.
*/
private void performSample() {
@@ -925,25 +974,34 @@
final long end = now - (now % largestBucketSize) + largestBucketSize;
final long start = end - largestBucketSize;
+ final long trustedTime = mTime.hasCache() ? mTime.currentTimeMillis() : -1;
+
NetworkTemplate template = null;
- NetworkStats.Entry ifaceTotal = null;
+ NetworkStats.Entry devTotal = null;
+ NetworkStats.Entry xtTotal = null;
NetworkStats.Entry uidTotal = null;
// collect mobile sample
template = buildTemplateMobileAll(getActiveSubscriberId(mContext));
- ifaceTotal = getSummaryForNetwork(template, start, end).getTotal(ifaceTotal);
+ devTotal = getSummaryForNetworkDev(template, start, end).getTotal(devTotal);
+ xtTotal = getSummaryForNetworkXt(template, start, end).getTotal(xtTotal);
uidTotal = getSummaryForAllUid(template, start, end, false).getTotal(uidTotal);
- EventLogTags.writeNetstatsMobileSample(ifaceTotal.rxBytes, ifaceTotal.rxPackets,
- ifaceTotal.txBytes, ifaceTotal.txPackets, uidTotal.rxBytes, uidTotal.rxPackets,
- uidTotal.txBytes, uidTotal.txPackets);
+ EventLogTags.writeNetstatsMobileSample(
+ devTotal.rxBytes, devTotal.rxPackets, devTotal.txBytes, devTotal.txPackets,
+ xtTotal.rxBytes, xtTotal.rxPackets, xtTotal.txBytes, xtTotal.txPackets,
+ uidTotal.rxBytes, uidTotal.rxPackets, uidTotal.txBytes, uidTotal.txPackets,
+ trustedTime);
// collect wifi sample
template = buildTemplateWifi();
- ifaceTotal = getSummaryForNetwork(template, start, end).getTotal(ifaceTotal);
+ devTotal = getSummaryForNetworkDev(template, start, end).getTotal(devTotal);
+ xtTotal = getSummaryForNetworkXt(template, start, end).getTotal(xtTotal);
uidTotal = getSummaryForAllUid(template, start, end, false).getTotal(uidTotal);
- EventLogTags.writeNetstatsWifiSample(ifaceTotal.rxBytes, ifaceTotal.rxPackets,
- ifaceTotal.txBytes, ifaceTotal.txPackets, uidTotal.rxBytes, uidTotal.rxPackets,
- uidTotal.txBytes, uidTotal.txPackets);
+ EventLogTags.writeNetstatsWifiSample(
+ devTotal.rxBytes, devTotal.rxPackets, devTotal.txBytes, devTotal.txPackets,
+ xtTotal.rxBytes, xtTotal.rxPackets, xtTotal.txBytes, xtTotal.txPackets,
+ uidTotal.rxBytes, uidTotal.rxPackets, uidTotal.txBytes, uidTotal.txPackets,
+ trustedTime);
}
/**
@@ -976,8 +1034,17 @@
writeUidStatsLocked();
}
- private NetworkStatsHistory findOrCreateNetworkStatsLocked(NetworkIdentitySet ident) {
- final NetworkStatsHistory existing = mNetworkStats.get(ident);
+ private NetworkStatsHistory findOrCreateNetworkXtStatsLocked(NetworkIdentitySet ident) {
+ return findOrCreateNetworkStatsLocked(ident, mNetworkXtStats);
+ }
+
+ private NetworkStatsHistory findOrCreateNetworkDevStatsLocked(NetworkIdentitySet ident) {
+ return findOrCreateNetworkStatsLocked(ident, mNetworkDevStats);
+ }
+
+ private NetworkStatsHistory findOrCreateNetworkStatsLocked(
+ NetworkIdentitySet ident, HashMap<NetworkIdentitySet, NetworkStatsHistory> source) {
+ final NetworkStatsHistory existing = source.get(ident);
// update when no existing, or when bucket duration changed
final long bucketDuration = mSettings.getNetworkBucketDuration();
@@ -991,7 +1058,7 @@
}
if (updated != null) {
- mNetworkStats.put(ident, updated);
+ source.put(ident, updated);
return updated;
} else {
return existing;
@@ -1024,15 +1091,24 @@
}
}
- private void readNetworkStatsLocked() {
- if (LOGV) Slog.v(TAG, "readNetworkStatsLocked()");
+ private void readNetworkDevStatsLocked() {
+ if (LOGV) Slog.v(TAG, "readNetworkDevStatsLocked()");
+ readNetworkStats(mNetworkDevFile, mNetworkDevStats);
+ }
+ private void readNetworkXtStatsLocked() {
+ if (LOGV) Slog.v(TAG, "readNetworkXtStatsLocked()");
+ readNetworkStats(mNetworkXtFile, mNetworkXtStats);
+ }
+
+ private static void readNetworkStats(
+ AtomicFile inputFile, HashMap<NetworkIdentitySet, NetworkStatsHistory> output) {
// clear any existing stats and read from disk
- mNetworkStats.clear();
+ output.clear();
DataInputStream in = null;
try {
- in = new DataInputStream(new BufferedInputStream(mNetworkFile.openRead()));
+ in = new DataInputStream(new BufferedInputStream(inputFile.openRead()));
// verify file magic header intact
final int magic = in.readInt();
@@ -1048,7 +1124,7 @@
for (int i = 0; i < size; i++) {
final NetworkIdentitySet ident = new NetworkIdentitySet(in);
final NetworkStatsHistory history = new NetworkStatsHistory(in);
- mNetworkStats.put(ident, history);
+ output.put(ident, history);
}
break;
}
@@ -1138,41 +1214,50 @@
}
}
- private void writeNetworkStatsLocked() {
- if (LOGV) Slog.v(TAG, "writeNetworkStatsLocked()");
+ private void writeNetworkDevStatsLocked() {
+ if (LOGV) Slog.v(TAG, "writeNetworkDevStatsLocked()");
+ writeNetworkStats(mNetworkDevStats, mNetworkDevFile);
+ }
+ private void writeNetworkXtStatsLocked() {
+ if (LOGV) Slog.v(TAG, "writeNetworkXtStatsLocked()");
+ writeNetworkStats(mNetworkXtStats, mNetworkXtFile);
+ }
+
+ private void writeNetworkStats(
+ HashMap<NetworkIdentitySet, NetworkStatsHistory> input, AtomicFile outputFile) {
// TODO: consider duplicating stats and releasing lock while writing
// trim any history beyond max
if (mTime.hasCache()) {
final long currentTime = mTime.currentTimeMillis();
final long maxHistory = mSettings.getNetworkMaxHistory();
- for (NetworkStatsHistory history : mNetworkStats.values()) {
+ for (NetworkStatsHistory history : input.values()) {
history.removeBucketsBefore(currentTime - maxHistory);
}
}
FileOutputStream fos = null;
try {
- fos = mNetworkFile.startWrite();
+ fos = outputFile.startWrite();
final DataOutputStream out = new DataOutputStream(new BufferedOutputStream(fos));
out.writeInt(FILE_MAGIC);
out.writeInt(VERSION_NETWORK_INIT);
- out.writeInt(mNetworkStats.size());
- for (NetworkIdentitySet ident : mNetworkStats.keySet()) {
- final NetworkStatsHistory history = mNetworkStats.get(ident);
+ out.writeInt(input.size());
+ for (NetworkIdentitySet ident : input.keySet()) {
+ final NetworkStatsHistory history = input.get(ident);
ident.writeToStream(out);
history.writeToStream(out);
}
out.flush();
- mNetworkFile.finishWrite(fos);
+ outputFile.finishWrite(fos);
} catch (IOException e) {
Log.wtf(TAG, "problem writing stats", e);
if (fos != null) {
- mNetworkFile.failWrite(fos);
+ outputFile.failWrite(fos);
}
}
}
@@ -1280,9 +1365,16 @@
pw.print(" ident="); pw.println(ident.toString());
}
- pw.println("Known historical stats:");
- for (NetworkIdentitySet ident : mNetworkStats.keySet()) {
- final NetworkStatsHistory history = mNetworkStats.get(ident);
+ pw.println("Known historical dev stats:");
+ for (NetworkIdentitySet ident : mNetworkDevStats.keySet()) {
+ final NetworkStatsHistory history = mNetworkDevStats.get(ident);
+ pw.print(" ident="); pw.println(ident.toString());
+ history.dump(" ", pw, fullHistory);
+ }
+
+ pw.println("Known historical xt stats:");
+ for (NetworkIdentitySet ident : mNetworkXtStats.keySet()) {
+ final NetworkStatsHistory history = mNetworkXtStats.get(ident);
pw.print(" ident="); pw.println(ident.toString());
history.dump(" ", pw, fullHistory);
}
@@ -1333,10 +1425,13 @@
final List<ApplicationInfo> installedApps = mContext
.getPackageManager().getInstalledApplications(0);
- mNetworkStats.clear();
+ mNetworkDevStats.clear();
+ mNetworkXtStats.clear();
mUidStats.clear();
for (NetworkIdentitySet ident : mActiveIfaces.values()) {
- findOrCreateNetworkStatsLocked(ident).generateRandom(NET_START, NET_END, NET_RX_BYTES,
+ findOrCreateNetworkDevStatsLocked(ident).generateRandom(NET_START, NET_END,
+ NET_RX_BYTES, NET_RX_PACKETS, NET_TX_BYTES, NET_TX_PACKETS, 0L);
+ findOrCreateNetworkXtStatsLocked(ident).generateRandom(NET_START, NET_END, NET_RX_BYTES,
NET_RX_PACKETS, NET_TX_BYTES, NET_TX_PACKETS, 0L);
for (ApplicationInfo info : installedApps) {
@@ -1369,6 +1464,10 @@
}
}
+ private static NetworkStats computeNetworkXtSnapshotFromUid(NetworkStats uidSnapshot) {
+ return uidSnapshot.groupedByIface();
+ }
+
private int estimateNetworkBuckets() {
return (int) (mSettings.getNetworkMaxHistory() / mSettings.getNetworkBucketDuration());
}
diff --git a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
index 2ead254..f7dff23 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
@@ -32,6 +32,7 @@
import static android.net.NetworkTemplate.buildTemplateMobileAll;
import static android.net.NetworkTemplate.buildTemplateWifi;
import static android.net.TrafficStats.UID_REMOVED;
+import static android.net.TrafficStats.UID_TETHERING;
import static android.text.format.DateUtils.DAY_IN_MILLIS;
import static android.text.format.DateUtils.HOUR_IN_MILLIS;
import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
@@ -179,6 +180,7 @@
expectNetworkState(buildWifiState());
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
+ expectNetworkStatsPoll();
replay();
mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION_IMMEDIATE));
@@ -232,6 +234,7 @@
expectNetworkState(buildWifiState());
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
+ expectNetworkStatsPoll();
replay();
mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION_IMMEDIATE));
@@ -327,6 +330,7 @@
expectNetworkState(buildWifiState());
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
+ expectNetworkStatsPoll();
replay();
mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION_IMMEDIATE));
@@ -378,6 +382,7 @@
expectNetworkState(buildMobile3gState(IMSI_1));
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
+ expectNetworkStatsPoll();
replay();
mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION_IMMEDIATE));
@@ -459,6 +464,7 @@
expectNetworkState(buildWifiState());
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
+ expectNetworkStatsPoll();
replay();
mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION_IMMEDIATE));
@@ -518,6 +524,7 @@
expectNetworkState(buildMobile3gState(IMSI_1));
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
+ expectNetworkStatsPoll();
replay();
mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION_IMMEDIATE));
@@ -585,6 +592,7 @@
expectNetworkState(buildWifiState());
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
+ expectNetworkStatsPoll();
replay();
mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION_IMMEDIATE));
@@ -648,6 +656,7 @@
expectNetworkState(buildWifiState());
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
+ expectNetworkStatsPoll();
replay();
mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION_IMMEDIATE));
@@ -705,6 +714,42 @@
verifyAndReset();
}
+ public void testTethering() throws Exception {
+ // pretend first mobile network comes online
+ expectCurrentTime();
+ expectDefaultSettings();
+ expectNetworkState(buildMobile3gState(IMSI_1));
+ expectNetworkStatsSummary(buildEmptyStats());
+ expectNetworkStatsUidDetail(buildEmptyStats());
+ expectNetworkStatsPoll();
+
+ replay();
+ mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION_IMMEDIATE));
+ verifyAndReset();
+
+ // create some tethering traffic
+ incrementCurrentTime(HOUR_IN_MILLIS);
+ expectCurrentTime();
+ expectDefaultSettings();
+ expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
+ .addIfaceValues(TEST_IFACE, 2048L, 16L, 512L, 4L));
+ expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
+ .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 128L, 2L, 128L, 2L, 0L));
+ final String[] tetherIfacePairs = new String[] { TEST_IFACE, "wlan0" };
+ expectNetworkStatsPoll(tetherIfacePairs, new NetworkStats(getElapsedRealtime(), 1)
+ .addValues(TEST_IFACE, UID_TETHERING, SET_DEFAULT, TAG_NONE, 1920L, 14L, 384L, 2L, 0L));
+
+ replay();
+ mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL));
+
+ // verify service recorded history
+ assertNetworkTotal(sTemplateImsi1, 2048L, 16L, 512L, 4L, 0);
+ assertUidTotal(sTemplateImsi1, UID_RED, 128L, 2L, 128L, 2L, 0);
+ assertUidTotal(sTemplateImsi1, UID_TETHERING, 1920L, 14L, 384L, 2L, 0);
+ verifyAndReset();
+
+ }
+
private void assertNetworkTotal(NetworkTemplate template, long rxBytes, long rxPackets,
long txBytes, long txPackets, int operations) {
final NetworkStatsHistory history = mService.getHistoryForNetwork(template, FIELD_ALL);
@@ -774,9 +819,16 @@
}
private void expectNetworkStatsPoll() throws Exception {
+ expectNetworkStatsPoll(new String[0], new NetworkStats(getElapsedRealtime(), 0));
+ }
+
+ private void expectNetworkStatsPoll(String[] tetherIfacePairs, NetworkStats tetherStats)
+ throws Exception {
mNetManager.setGlobalAlert(anyLong());
expectLastCall().anyTimes();
- expect(mConnManager.getTetheredIfacePairs()).andReturn(null).anyTimes();
+ expect(mConnManager.getTetheredIfacePairs()).andReturn(tetherIfacePairs).anyTimes();
+ expect(mNetManager.getNetworkStatsTethering(eq(tetherIfacePairs)))
+ .andReturn(tetherStats).anyTimes();
}
private void assertStatsFilesExist(boolean exist) {