diff options
6 files changed, 370 insertions, 122 deletions
diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java index f3be39ca2b76..e5549753cc05 100644 --- a/core/java/android/net/NetworkStats.java +++ b/core/java/android/net/NetworkStats.java @@ -272,6 +272,17 @@ public class NetworkStats implements Parcelable { } /** + * 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 @@ public class NetworkStats implements Parcelable { 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 c36685d0da73..d78d2effd4c8 100644 --- a/core/tests/coretests/src/android/net/NetworkStatsTest.java +++ b/core/tests/coretests/src/android/net/NetworkStatsTest.java @@ -18,6 +18,8 @@ package android.net; 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 @@ public class NetworkStatsTest extends TestCase { 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 99f6b8ef3eb5..a7eff935c99c 100644 --- a/services/java/com/android/server/EventLogTags.logtags +++ b/services/java/com/android/server/EventLogTags.logtags @@ -142,5 +142,5 @@ option java_package com.android.server # --------------------------- # 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 aacaa6a5a09e..6baf5fe8ffc3 100644 --- a/services/java/com/android/server/NetworkManagementService.java +++ b/services/java/com/android/server/NetworkManagementService.java @@ -21,6 +21,7 @@ import static android.Manifest.permission.MANAGE_NETWORK_POLICY; 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 @@ public class NetworkManagementService extends INetworkManagementService.Stub 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 aa46795b4fb9..e5f0a77974b8 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.NetworkStats.UID_ALL; 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.NetworkState; 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 @@ public class NetworkStatsService extends INetworkStatsService.Stub { 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 @@ public class NetworkStatsService extends INetworkStatsService.Stub { /** 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 @@ public class NetworkStatsService extends INetworkStatsService.Stub { 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 @@ public class NetworkStatsService extends INetworkStatsService.Stub { 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 @@ public class NetworkStatsService extends INetworkStatsService.Stub { // 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 @@ public class NetworkStatsService extends INetworkStatsService.Stub { 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 @@ public class NetworkStatsService extends INetworkStatsService.Stub { @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 @@ public class NetworkStatsService extends INetworkStatsService.Stub { @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 @@ public class NetworkStatsService extends INetworkStatsService.Stub { 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 @@ public class NetworkStatsService extends INetworkStatsService.Stub { */ 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 @@ public class NetworkStatsService extends INetworkStatsService.Stub { : 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 xt network stats + networkXtSnapshot = computeNetworkXtSnapshotFromUid(uidSnapshot); + performNetworkXtPollLocked(networkXtSnapshot, currentTime); - // record network stats - final NetworkStats networkSnapshot = mNetworkManager.getNetworkStatsSummary(); - performNetworkPollLocked(networkSnapshot, 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 @@ public class NetworkStatsService extends INetworkStatsService.Stub { 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 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } /** - * 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,88 +880,86 @@ public class NetworkStatsService extends INetworkStatsService.Stub { 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 #mUidStats} historical usage. + * Update {@link #mNetworkXtStats} historical usage. */ - private void performUidPollLocked(NetworkStats uidSnapshot, long currentTime) { - ensureUidStatsLoadedLocked(); + private void performNetworkXtPollLocked(NetworkStats networkXtSnapshot, long currentTime) { + final HashSet<String> unknownIface = Sets.newHashSet(); - final NetworkStats delta = computeStatsDelta(mLastPollUidSnapshot, uidSnapshot, false); - final NetworkStats operationsDelta = computeStatsDelta( - mLastPollOperationsSnapshot, mOperations, false); + final NetworkStats delta = computeStatsDelta( + mLastPollNetworkXtSnapshot, networkXtSnapshot, false); final long timeStart = currentTime - delta.getElapsedRealtime(); NetworkStats.Entry entry = null; - NetworkStats.Entry operationsEntry = 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 UID delta from unknown iface: " + entry); - } + unknownIface.add(entry.iface); continue; } - // splice in operation counts since last poll - final int j = operationsDelta.findIndex(IFACE_ALL, entry.uid, entry.set, entry.tag); - if (j != -1) { - operationsEntry = operationsDelta.getValues(j, operationsEntry); - entry.operations = operationsEntry.operations; - } - - final NetworkStatsHistory history = findOrCreateUidStatsLocked( - ident, entry.uid, entry.set, entry.tag); + final NetworkStatsHistory history = findOrCreateNetworkXtStatsLocked(ident); history.recordData(timeStart, currentTime, entry); } - mLastPollUidSnapshot = uidSnapshot; - mLastPollOperationsSnapshot = mOperations; - mOperations = new NetworkStats(0L, 10); + mLastPollNetworkXtSnapshot = networkXtSnapshot; + + if (LOGD && unknownIface.size() > 0) { + Slog.w(TAG, "unknown xt interfaces " + unknownIface + ", ignoring those stats"); + } } /** - * Update {@link #mUidStats} historical usage for - * {@link TrafficStats#UID_TETHERING} based on tethering statistics. + * Update {@link #mUidStats} historical usage. */ - private void performTetherPollLocked(NetworkStats tetherSnapshot, long currentTime) { + private void performUidPollLocked(NetworkStats uidSnapshot, long currentTime) { ensureUidStatsLoadedLocked(); - final NetworkStats delta = computeStatsDelta( - mLastPollTetherSnapshot, tetherSnapshot, false); + final NetworkStats delta = computeStatsDelta(mLastPollUidSnapshot, uidSnapshot, false); + final NetworkStats operationsDelta = computeStatsDelta( + mLastPollOperationsSnapshot, mOperations, false); final long timeStart = currentTime - delta.getElapsedRealtime(); NetworkStats.Entry entry = null; + NetworkStats.Entry operationsEntry = 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); + Log.w(TAG, "dropping UID delta from unknown iface: " + entry); } continue; } + // splice in operation counts since last poll + final int j = operationsDelta.findIndex(IFACE_ALL, entry.uid, entry.set, entry.tag); + if (j != -1) { + operationsEntry = operationsDelta.getValues(j, operationsEntry); + entry.operations = operationsEntry.operations; + } + final NetworkStatsHistory history = findOrCreateUidStatsLocked( - ident, UID_TETHERING, SET_DEFAULT, TAG_NONE); + ident, entry.uid, entry.set, entry.tag); history.recordData(timeStart, currentTime, entry); } - // normal UID poll will trim any history beyond max - mLastPollTetherSnapshot = tetherSnapshot; + mLastPollUidSnapshot = uidSnapshot; + mLastPollOperationsSnapshot = mOperations; + mOperations = new NetworkStats(0L, 10); } /** @@ -925,25 +974,34 @@ public class NetworkStatsService extends INetworkStatsService.Stub { 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 @@ public class NetworkStatsService extends INetworkStatsService.Stub { 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 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } if (updated != null) { - mNetworkStats.put(ident, updated); + source.put(ident, updated); return updated; } else { return existing; @@ -1024,15 +1091,24 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } } - 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 @@ public class NetworkStatsService extends INetworkStatsService.Stub { 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 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } } - 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 @@ public class NetworkStatsService extends INetworkStatsService.Stub { 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 @@ public class NetworkStatsService extends INetworkStatsService.Stub { 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 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } } + 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 2ead254af18c..f7dff2358a80 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.NetworkStatsHistory.FIELD_ALL; 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 @@ public class NetworkStatsServiceTest extends AndroidTestCase { expectNetworkState(buildWifiState()); expectNetworkStatsSummary(buildEmptyStats()); expectNetworkStatsUidDetail(buildEmptyStats()); + expectNetworkStatsPoll(); replay(); mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION_IMMEDIATE)); @@ -232,6 +234,7 @@ public class NetworkStatsServiceTest extends AndroidTestCase { expectNetworkState(buildWifiState()); expectNetworkStatsSummary(buildEmptyStats()); expectNetworkStatsUidDetail(buildEmptyStats()); + expectNetworkStatsPoll(); replay(); mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION_IMMEDIATE)); @@ -327,6 +330,7 @@ public class NetworkStatsServiceTest extends AndroidTestCase { expectNetworkState(buildWifiState()); expectNetworkStatsSummary(buildEmptyStats()); expectNetworkStatsUidDetail(buildEmptyStats()); + expectNetworkStatsPoll(); replay(); mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION_IMMEDIATE)); @@ -378,6 +382,7 @@ public class NetworkStatsServiceTest extends AndroidTestCase { expectNetworkState(buildMobile3gState(IMSI_1)); expectNetworkStatsSummary(buildEmptyStats()); expectNetworkStatsUidDetail(buildEmptyStats()); + expectNetworkStatsPoll(); replay(); mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION_IMMEDIATE)); @@ -459,6 +464,7 @@ public class NetworkStatsServiceTest extends AndroidTestCase { expectNetworkState(buildWifiState()); expectNetworkStatsSummary(buildEmptyStats()); expectNetworkStatsUidDetail(buildEmptyStats()); + expectNetworkStatsPoll(); replay(); mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION_IMMEDIATE)); @@ -518,6 +524,7 @@ public class NetworkStatsServiceTest extends AndroidTestCase { expectNetworkState(buildMobile3gState(IMSI_1)); expectNetworkStatsSummary(buildEmptyStats()); expectNetworkStatsUidDetail(buildEmptyStats()); + expectNetworkStatsPoll(); replay(); mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION_IMMEDIATE)); @@ -585,6 +592,7 @@ public class NetworkStatsServiceTest extends AndroidTestCase { expectNetworkState(buildWifiState()); expectNetworkStatsSummary(buildEmptyStats()); expectNetworkStatsUidDetail(buildEmptyStats()); + expectNetworkStatsPoll(); replay(); mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION_IMMEDIATE)); @@ -648,6 +656,7 @@ public class NetworkStatsServiceTest extends AndroidTestCase { expectNetworkState(buildWifiState()); expectNetworkStatsSummary(buildEmptyStats()); expectNetworkStatsUidDetail(buildEmptyStats()); + expectNetworkStatsPoll(); replay(); mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION_IMMEDIATE)); @@ -705,6 +714,42 @@ public class NetworkStatsServiceTest extends AndroidTestCase { 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 @@ public class NetworkStatsServiceTest extends AndroidTestCase { } 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) { |