From 9f0baa94ca28f67e162d3ad04ca6d0e17ee5e314 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Tue, 15 Aug 2017 19:25:51 +0900 Subject: [PATCH 1/2] Tell the system when tethering offload hits a limit. Add a new tetherLimitReached method to INetworkManagementService, and call it when the HAL notifies OffloadController because the limit has been reached. Bug: 29337859 Bug: 32163131 Test: builds Test: OffloadControllerTest passes (cherry picked from commit d66cf56ba662f10f2da1d0f844116632ad0a0dbb) Change-Id: I89719fe7ec8bfd3c85d6cdca9c0d449aea86ef9d Merged-In: I026e6aa9e7b371f316c0d97c3cf5e78abc1f5263 --- .../android/os/INetworkManagementService.aidl | 15 ++++++++++ .../server/NetworkManagementService.java | 12 ++++++++ .../tethering/OffloadController.java | 28 +++++++++++++++++-- .../tethering/OffloadControllerTest.java | 17 ++++++++++- 4 files changed, 68 insertions(+), 4 deletions(-) diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl index 3de217494ac52..316ef11f50ded 100644 --- a/core/java/android/os/INetworkManagementService.aidl +++ b/core/java/android/os/INetworkManagementService.aidl @@ -219,6 +219,21 @@ interface INetworkManagementService */ void unregisterTetheringStatsProvider(ITetheringStatsProvider provider); + /** + * Reports that a tethering provider has reached a data limit. + * + * Currently triggers a global alert, which causes NetworkStatsService to poll counters and + * re-evaluate data usage. + * + * This does not take an interface name because: + * 1. The tethering offload stats provider cannot reliably determine the interface on which the + * limit was reached, because the HAL does not provide it. + * 2. Firing an interface-specific alert instead of a global alert isn't really useful since in + * all cases of interest, the system responds to both in the same way - it polls stats, and + * then notifies NetworkPolicyManagerService of the fact. + */ + void tetherLimitReached(ITetheringStatsProvider provider); + /** ** PPPD **/ diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java index 3d638be3e82d4..51e2a70ce2703 100644 --- a/services/core/java/com/android/server/NetworkManagementService.java +++ b/services/core/java/com/android/server/NetworkManagementService.java @@ -529,6 +529,18 @@ public class NetworkManagementService extends INetworkManagementService.Stub } } + @Override + public void tetherLimitReached(ITetheringStatsProvider provider) { + mContext.enforceCallingOrSelfPermission(NETWORK_STACK, TAG); + synchronized(mTetheringStatsProviders) { + if (!mTetheringStatsProviders.containsKey(provider)) { + return; + } + // No current code examines the interface parameter in a global alert. Just pass null. + notifyLimitReached(LIMIT_GLOBAL_ALERT, null); + } + } + // Sync the state of the given chain with the native daemon. private void syncFirewallChainLocked(int chain, SparseIntArray uidFirewallRules, String name) { int size = uidFirewallRules.size(); diff --git a/services/core/java/com/android/server/connectivity/tethering/OffloadController.java b/services/core/java/com/android/server/connectivity/tethering/OffloadController.java index 55e290a4215e2..ad661d79767df 100644 --- a/services/core/java/com/android/server/connectivity/tethering/OffloadController.java +++ b/services/core/java/com/android/server/connectivity/tethering/OffloadController.java @@ -60,6 +60,8 @@ public class OffloadController { private final Handler mHandler; private final OffloadHardwareInterface mHwInterface; private final ContentResolver mContentResolver; + private final INetworkManagementService mNms; + private final ITetheringStatsProvider mStatsProvider; private final SharedLog mLog; private boolean mConfigInitialized; private boolean mControlInitialized; @@ -89,13 +91,14 @@ public class OffloadController { mHandler = h; mHwInterface = hwi; mContentResolver = contentResolver; + mNms = nms; + mStatsProvider = new OffloadTetheringStatsProvider(); mLog = log.forSubComponent(TAG); mExemptPrefixes = new HashSet<>(); mLastLocalPrefixStrs = new HashSet<>(); try { - nms.registerTetheringStatsProvider( - new OffloadTetheringStatsProvider(), getClass().getSimpleName()); + mNms.registerTetheringStatsProvider(mStatsProvider, getClass().getSimpleName()); } catch (RemoteException e) { mLog.e("Cannot register offload stats provider: " + e); } @@ -150,7 +153,26 @@ public class OffloadController { @Override public void onStoppedLimitReached() { mLog.log("onStoppedLimitReached"); - // Poll for statistics and notify NetworkStats + + // We cannot reliably determine on which interface the limit was reached, + // because the HAL interface does not specify it. We cannot just use the + // current upstream, because that might have changed since the time that + // the HAL queued the callback. + // TODO: rev the HAL so that it provides an interface name. + + // Fetch current stats, so that when our notification reaches + // NetworkStatsService and triggers a poll, we will respond with + // current data (which will be above the limit that was reached). + // Note that if we just changed upstream, this is unnecessary but harmless. + // The stats for the previous upstream were already updated on this thread + // just after the upstream was changed, so they are also up-to-date. + updateStatsForCurrentUpstream(); + + try { + mNms.tetherLimitReached(mStatsProvider); + } catch (RemoteException e) { + mLog.e("Cannot report data limit reached: " + e); + } } @Override diff --git a/tests/net/java/com/android/server/connectivity/tethering/OffloadControllerTest.java b/tests/net/java/com/android/server/connectivity/tethering/OffloadControllerTest.java index d29a94b9e0660..54addb15c246e 100644 --- a/tests/net/java/com/android/server/connectivity/tethering/OffloadControllerTest.java +++ b/tests/net/java/com/android/server/connectivity/tethering/OffloadControllerTest.java @@ -85,6 +85,8 @@ public class OffloadControllerTest { ArgumentCaptor.forClass(ArrayList.class); private final ArgumentCaptor mTetherStatsProviderCaptor = ArgumentCaptor.forClass(ITetheringStatsProvider.Stub.class); + private final ArgumentCaptor mControlCallbackCaptor = + ArgumentCaptor.forClass(OffloadHardwareInterface.ControlCallback.class); private MockContentResolver mContentResolver; @Before public void setUp() { @@ -105,7 +107,7 @@ public class OffloadControllerTest { private void setupFunctioningHardwareInterface() { when(mHardware.initOffloadConfig()).thenReturn(true); - when(mHardware.initOffloadControl(any(OffloadHardwareInterface.ControlCallback.class))) + when(mHardware.initOffloadControl(mControlCallbackCaptor.capture())) .thenReturn(true); when(mHardware.getForwardedStats(any())).thenReturn(new ForwardedStats()); } @@ -493,4 +495,17 @@ public class OffloadControllerTest { waitForIdle(); inOrder.verify(mHardware).stopOffloadControl(); } + + @Test + public void testDataLimitCallback() throws Exception { + setupFunctioningHardwareInterface(); + enableOffload(); + + final OffloadController offload = makeOffloadController(); + offload.start(); + + OffloadHardwareInterface.ControlCallback callback = mControlCallbackCaptor.getValue(); + callback.onStoppedLimitReached(); + verify(mNMService, times(1)).tetherLimitReached(mTetherStatsProviderCaptor.getValue()); + } } From f1912ca49ac0f5bcdad063a7c042153ba791145d Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Thu, 17 Aug 2017 19:23:08 +0900 Subject: [PATCH 2/2] Add tether offload traffic to interface stats as well. Currently, we only count add tethering traffic to per-UID stats, but not to total data usage (i.e., dev and XT stats). This is correct for software tethering, because all software forwarded packets are already included in interface counters, but it is incorrect for hardware offload, because such packets do not increment interface counters. To fix this: 1. Add an argument to ITetheringStatsProvider#getTetherStats to indicate whether per-UID stats are requested. For clarity, define integer constants STATS_PER_IFACE and STATS_PER_UID to represent these operations. 2. Make NetdTetheringStatsProvider return stats only if per-UID stats are requested. (Otherwise tethering traffic would be double-counted). 3. Make OffloadController's stats provider return the same stats regardless of whether per-UID stats were requested or not. 4. Make NetworkStatsService add non-per-UID tethering stats to the dev and XT snapshots. The per-UID snapshots were already correctly adding in per-UID stats. (cherry picked from commit 5356a35c3bcfcdf2d184c620af6bfbf9bddf35c5) Bug: 29337859 Bug: 32163131 Test: runtest frameworks-net Test: runtest frameworks-telephony Change-Id: I325b13d50e88841dfb0db4c35e7e27f163ee72fe Merged-In: I4e8e923d68dce1a4a68608dbd6c75a91165aa4ee --- .../android/net/ITetheringStatsProvider.aidl | 5 ++- core/java/android/net/NetworkStats.java | 5 +++ .../android/os/INetworkManagementService.aidl | 2 +- .../server/NetworkManagementService.java | 13 ++++-- .../tethering/OffloadController.java | 8 +++- .../server/net/NetworkStatsService.java | 17 +++++--- .../android/telephony/TelephonyManager.java | 6 ++- .../tethering/OffloadControllerTest.java | 19 ++++++++- .../server/net/NetworkStatsServiceTest.java | 41 ++++++++++++++----- 9 files changed, 89 insertions(+), 27 deletions(-) diff --git a/core/java/android/net/ITetheringStatsProvider.aidl b/core/java/android/net/ITetheringStatsProvider.aidl index 1aeabc1e62de2..da0bf4c47b94b 100644 --- a/core/java/android/net/ITetheringStatsProvider.aidl +++ b/core/java/android/net/ITetheringStatsProvider.aidl @@ -30,7 +30,10 @@ import android.net.NetworkStats; */ interface ITetheringStatsProvider { // Returns cumulative statistics for all tethering sessions since boot, on all upstreams. - NetworkStats getTetherStats(); + // @code {how} is one of the NetworkStats.STATS_PER_* constants. If {@code how} is + // {@code STATS_PER_IFACE}, the provider should not include any traffic that is already + // counted by kernel interface counters. + NetworkStats getTetherStats(int how); // Sets the interface quota for the specified upstream interface. This is defined as the number // of bytes, starting from zero and counting from now, after which data should stop being diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java index 77ce65b0815a5..0780af602be2c 100644 --- a/core/java/android/net/NetworkStats.java +++ b/core/java/android/net/NetworkStats.java @@ -82,6 +82,11 @@ public class NetworkStats implements Parcelable { /** {@link #roaming} value where roaming data is accounted. */ public static final int ROAMING_YES = 1; + /** Denotes a request for stats at the interface level. */ + public static final int STATS_PER_IFACE = 0; + /** Denotes a request for stats at the interface and UID level. */ + public static final int STATS_PER_UID = 1; + // TODO: move fields to "mVariable" notation /** diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl index 316ef11f50ded..b63e3023565f3 100644 --- a/core/java/android/os/INetworkManagementService.aidl +++ b/core/java/android/os/INetworkManagementService.aidl @@ -281,7 +281,7 @@ interface INetworkManagementService /** * Return summary of network statistics all tethering interfaces. */ - NetworkStats getNetworkStatsTethering(); + NetworkStats getNetworkStatsTethering(int how); /** * Set quota for an interface. diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java index 51e2a70ce2703..643ccc13f1cab 100644 --- a/services/core/java/com/android/server/NetworkManagementService.java +++ b/services/core/java/com/android/server/NetworkManagementService.java @@ -32,6 +32,7 @@ import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT; import static android.net.NetworkPolicyManager.FIREWALL_TYPE_BLACKLIST; import static android.net.NetworkPolicyManager.FIREWALL_TYPE_WHITELIST; import static android.net.NetworkStats.SET_DEFAULT; +import static android.net.NetworkStats.STATS_PER_UID; import static android.net.NetworkStats.TAG_ALL; import static android.net.NetworkStats.TAG_NONE; import static android.net.NetworkStats.UID_ALL; @@ -1822,7 +1823,13 @@ public class NetworkManagementService extends INetworkManagementService.Stub private class NetdTetheringStatsProvider extends ITetheringStatsProvider.Stub { @Override - public NetworkStats getTetherStats() { + public NetworkStats getTetherStats(int how) { + // We only need to return per-UID stats. Per-device stats are already counted by + // interface counters. + if (how != STATS_PER_UID) { + return new NetworkStats(SystemClock.elapsedRealtime(), 0); + } + final NativeDaemonEvent[] events; try { events = mConnector.executeForList("bandwidth", "gettetherstats"); @@ -1865,14 +1872,14 @@ public class NetworkManagementService extends INetworkManagementService.Stub } @Override - public NetworkStats getNetworkStatsTethering() { + public NetworkStats getNetworkStatsTethering(int how) { mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 1); synchronized (mTetheringStatsProviders) { for (ITetheringStatsProvider provider: mTetheringStatsProviders.keySet()) { try { - stats.combineAllValues(provider.getTetherStats()); + stats.combineAllValues(provider.getTetherStats(how)); } catch (RemoteException e) { Log.e(TAG, "Problem reading tethering stats from " + mTetheringStatsProviders.get(provider) + ": " + e); diff --git a/services/core/java/com/android/server/connectivity/tethering/OffloadController.java b/services/core/java/com/android/server/connectivity/tethering/OffloadController.java index ad661d79767df..4393e3527e4d2 100644 --- a/services/core/java/com/android/server/connectivity/tethering/OffloadController.java +++ b/services/core/java/com/android/server/connectivity/tethering/OffloadController.java @@ -17,7 +17,9 @@ package com.android.server.connectivity.tethering; import static android.net.NetworkStats.SET_DEFAULT; +import static android.net.NetworkStats.STATS_PER_UID; import static android.net.NetworkStats.TAG_NONE; +import static android.net.NetworkStats.UID_ALL; import static android.net.TrafficStats.UID_TETHERING; import static android.provider.Settings.Global.TETHER_OFFLOAD_DISABLED; @@ -202,16 +204,18 @@ public class OffloadController { private class OffloadTetheringStatsProvider extends ITetheringStatsProvider.Stub { @Override - public NetworkStats getTetherStats() { + public NetworkStats getTetherStats(int how) { NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 0); // We can't just post to mHandler because we are mostly (but not always) called by // NetworkStatsService#performPollLocked, which is (currently) on the same thread as us. mHandler.runWithScissors(() -> { + // We have to report both per-interface and per-UID stats, because offloaded traffic + // is not seen by kernel interface counters. NetworkStats.Entry entry = new NetworkStats.Entry(); entry.set = SET_DEFAULT; entry.tag = TAG_NONE; - entry.uid = UID_TETHERING; + entry.uid = (how == STATS_PER_UID) ? UID_TETHERING : UID_ALL; updateStatsForCurrentUpstream(); diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java index 4152070dc61be..ef96de74cbf2f 100644 --- a/services/core/java/com/android/server/net/NetworkStatsService.java +++ b/services/core/java/com/android/server/net/NetworkStatsService.java @@ -31,6 +31,8 @@ import static android.net.NetworkStats.IFACE_ALL; import static android.net.NetworkStats.SET_ALL; import static android.net.NetworkStats.SET_DEFAULT; import static android.net.NetworkStats.SET_FOREGROUND; +import static android.net.NetworkStats.STATS_PER_IFACE; +import static android.net.NetworkStats.STATS_PER_UID; import static android.net.NetworkStats.TAG_NONE; import static android.net.NetworkStats.UID_ALL; import static android.net.NetworkTemplate.buildTemplateMobileWildcard; @@ -1032,6 +1034,11 @@ public class NetworkStatsService extends INetworkStatsService.Stub { final NetworkStats xtSnapshot = getNetworkStatsXt(); final NetworkStats devSnapshot = mNetworkManager.getNetworkStatsSummaryDev(); + // Tethering snapshot for dev and xt stats. Counts per-interface data from tethering stats + // providers that isn't already counted by dev and XT stats. + final NetworkStats tetherSnapshot = getNetworkStatsTethering(STATS_PER_IFACE); + xtSnapshot.combineAllValues(tetherSnapshot); + devSnapshot.combineAllValues(tetherSnapshot); // For xt/dev, we pass a null VPN array because usage is aggregated by UID, so VPN traffic // can't be reattributed to responsible apps. @@ -1328,14 +1335,14 @@ public class NetworkStatsService extends INetworkStatsService.Stub { final NetworkStats uidSnapshot = mNetworkManager.getNetworkStatsUidDetail(UID_ALL); // fold tethering stats and operations into uid snapshot - final NetworkStats tetherSnapshot = getNetworkStatsTethering(); + final NetworkStats tetherSnapshot = getNetworkStatsTethering(STATS_PER_UID); uidSnapshot.combineAllValues(tetherSnapshot); final TelephonyManager telephonyManager = (TelephonyManager) mContext.getSystemService( Context.TELEPHONY_SERVICE); // fold video calling data usage stats into uid snapshot - final NetworkStats vtStats = telephonyManager.getVtDataUsage(true); + final NetworkStats vtStats = telephonyManager.getVtDataUsage(STATS_PER_UID); if (vtStats != null) { uidSnapshot.combineAllValues(vtStats); } @@ -1354,7 +1361,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { Context.TELEPHONY_SERVICE); // Merge video calling data usage into XT - final NetworkStats vtSnapshot = telephonyManager.getVtDataUsage(false); + final NetworkStats vtSnapshot = telephonyManager.getVtDataUsage(STATS_PER_IFACE); if (vtSnapshot != null) { xtSnapshot.combineAllValues(vtSnapshot); } @@ -1366,9 +1373,9 @@ public class NetworkStatsService extends INetworkStatsService.Stub { * Return snapshot of current tethering statistics. Will return empty * {@link NetworkStats} if any problems are encountered. */ - private NetworkStats getNetworkStatsTethering() throws RemoteException { + private NetworkStats getNetworkStatsTethering(int how) throws RemoteException { try { - return mNetworkManager.getNetworkStatsTethering(); + return mNetworkManager.getNetworkStatsTethering(how); } catch (IllegalStateException e) { Log.wtf(TAG, "problem reading network stats", e); return new NetworkStats(0L, 10); diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 769894fa76807..86ad81a66ac68 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -6508,11 +6508,13 @@ public class TelephonyManager { * Get aggregated video call data usage since boot. * Permissions android.Manifest.permission.READ_NETWORK_USAGE_HISTORY is required. * - * @param perUidStats True if requesting data usage per uid, otherwise overall usage. + * @param how one of the NetworkStats.STATS_PER_* constants depending on whether the request is + * for data usage per uid or overall usage. * @return Snapshot of video call data usage * @hide */ - public NetworkStats getVtDataUsage(boolean perUidStats) { + public NetworkStats getVtDataUsage(int how) { + boolean perUidStats = (how == NetworkStats.STATS_PER_UID); try { ITelephony service = getITelephony(); if (service != null) { diff --git a/tests/net/java/com/android/server/connectivity/tethering/OffloadControllerTest.java b/tests/net/java/com/android/server/connectivity/tethering/OffloadControllerTest.java index 54addb15c246e..360e5f6ead099 100644 --- a/tests/net/java/com/android/server/connectivity/tethering/OffloadControllerTest.java +++ b/tests/net/java/com/android/server/connectivity/tethering/OffloadControllerTest.java @@ -17,7 +17,10 @@ package com.android.server.connectivity.tethering; import static android.net.NetworkStats.SET_DEFAULT; +import static android.net.NetworkStats.STATS_PER_IFACE; +import static android.net.NetworkStats.STATS_PER_UID; import static android.net.NetworkStats.TAG_NONE; +import static android.net.NetworkStats.UID_ALL; import static android.net.TrafficStats.UID_TETHERING; import static android.provider.Settings.Global.TETHER_OFFLOAD_DISABLED; import static com.android.server.connectivity.tethering.OffloadHardwareInterface.ForwardedStats; @@ -377,7 +380,6 @@ public class OffloadControllerTest { assertEquals(stats.txBytes, entry.txBytes); assertEquals(SET_DEFAULT, entry.set); assertEquals(TAG_NONE, entry.tag); - assertEquals(UID_TETHERING, entry.uid); } @Test @@ -416,20 +418,33 @@ public class OffloadControllerTest { ethernetStats.txBytes = 100000; offload.setUpstreamLinkProperties(null); - NetworkStats stats = mTetherStatsProviderCaptor.getValue().getTetherStats(); + ITetheringStatsProvider provider = mTetherStatsProviderCaptor.getValue(); + NetworkStats stats = provider.getTetherStats(STATS_PER_IFACE); + NetworkStats perUidStats = provider.getTetherStats(STATS_PER_UID); + assertEquals(2, stats.size()); + assertEquals(2, perUidStats.size()); NetworkStats.Entry entry = null; + for (int i = 0; i < stats.size(); i++) { + assertEquals(UID_ALL, stats.getValues(i, entry).uid); + assertEquals(UID_TETHERING, perUidStats.getValues(i, entry).uid); + } + int ethernetPosition = ethernetIface.equals(stats.getValues(0, entry).iface) ? 0 : 1; int mobilePosition = 1 - ethernetPosition; entry = stats.getValues(mobilePosition, entry); assertNetworkStats(mobileIface, mobileStats, entry); + entry = perUidStats.getValues(mobilePosition, entry); + assertNetworkStats(mobileIface, mobileStats, entry); ethernetStats.rxBytes = 12345 + 100000; ethernetStats.txBytes = 54321 + 100000; entry = stats.getValues(ethernetPosition, entry); assertNetworkStats(ethernetIface, ethernetStats, entry); + entry = perUidStats.getValues(ethernetPosition, entry); + assertNetworkStats(ethernetIface, ethernetStats, entry); } @Test diff --git a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java index feb46d39b5633..fa997958ba6de 100644 --- a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java +++ b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java @@ -31,6 +31,8 @@ import static android.net.NetworkStats.ROAMING_YES; import static android.net.NetworkStats.SET_ALL; import static android.net.NetworkStats.SET_DEFAULT; import static android.net.NetworkStats.SET_FOREGROUND; +import static android.net.NetworkStats.STATS_PER_IFACE; +import static android.net.NetworkStats.STATS_PER_UID; import static android.net.NetworkStats.TAG_NONE; import static android.net.NetworkStats.UID_ALL; import static android.net.NetworkStatsHistory.FIELD_ALL; @@ -823,17 +825,24 @@ public class NetworkStatsServiceTest { incrementCurrentTime(HOUR_IN_MILLIS); expectCurrentTime(); expectDefaultSettings(); - expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1) - .addIfaceValues(TEST_IFACE, 2048L, 16L, 512L, 4L)); + // Traffic seen by kernel counters (includes software tethering). + final NetworkStats ifaceStats = new NetworkStats(getElapsedRealtime(), 1) + .addIfaceValues(TEST_IFACE, 1536L, 12L, 384L, 3L); + // Hardware tethering traffic, not seen by kernel counters. + final NetworkStats tetherStatsHardware = new NetworkStats(getElapsedRealtime(), 1) + .addIfaceValues(TEST_IFACE, 512L, 4L, 128L, 1L); + + // Traffic for UID_RED. final NetworkStats uidStats = 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" }; + // All tethering traffic, both hardware and software. final NetworkStats tetherStats = new NetworkStats(getElapsedRealtime(), 1) .addValues(TEST_IFACE, UID_TETHERING, SET_DEFAULT, TAG_NONE, 1920L, 14L, 384L, 2L, 0L); - expectNetworkStatsUidDetail(uidStats, tetherIfacePairs, tetherStats); + expectNetworkStatsSummary(ifaceStats, tetherStatsHardware); + expectNetworkStatsUidDetail(uidStats, tetherStats); forcePollAndWaitForIdle(); // verify service recorded history @@ -1013,10 +1022,16 @@ public class NetworkStatsServiceTest { } private void expectNetworkStatsSummary(NetworkStats summary) throws Exception { + expectNetworkStatsSummary(summary, new NetworkStats(0L, 0)); + } + + private void expectNetworkStatsSummary(NetworkStats summary, NetworkStats tetherStats) + throws Exception { when(mConnManager.getAllVpnInfo()).thenReturn(new VpnInfo[0]); - expectNetworkStatsSummaryDev(summary); - expectNetworkStatsSummaryXt(summary); + expectNetworkStatsTethering(STATS_PER_IFACE, tetherStats); + expectNetworkStatsSummaryDev(summary.clone()); + expectNetworkStatsSummaryXt(summary.clone()); } private void expectNetworkStatsSummaryDev(NetworkStats summary) throws Exception { @@ -1027,17 +1042,21 @@ public class NetworkStatsServiceTest { when(mNetManager.getNetworkStatsSummaryXt()).thenReturn(summary); } - private void expectNetworkStatsUidDetail(NetworkStats detail) throws Exception { - expectNetworkStatsUidDetail(detail, new String[0], new NetworkStats(0L, 0)); + private void expectNetworkStatsTethering(int how, NetworkStats stats) + throws Exception { + when(mNetManager.getNetworkStatsTethering(how)).thenReturn(stats); } - private void expectNetworkStatsUidDetail( - NetworkStats detail, String[] tetherIfacePairs, NetworkStats tetherStats) + private void expectNetworkStatsUidDetail(NetworkStats detail) throws Exception { + expectNetworkStatsUidDetail(detail, new NetworkStats(0L, 0)); + } + + private void expectNetworkStatsUidDetail(NetworkStats detail, NetworkStats tetherStats) throws Exception { when(mNetManager.getNetworkStatsUidDetail(UID_ALL)).thenReturn(detail); // also include tethering details, since they are folded into UID - when(mNetManager.getNetworkStatsTethering()).thenReturn(tetherStats); + when(mNetManager.getNetworkStatsTethering(STATS_PER_UID)).thenReturn(tetherStats); } private void expectDefaultSettings() throws Exception {