diff options
9 files changed, 88 insertions, 26 deletions
diff --git a/core/java/android/net/ITetheringStatsProvider.aidl b/core/java/android/net/ITetheringStatsProvider.aidl index 1aeabc1e62de..da0bf4c47b94 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 be9e80973875..171adc054bbe 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 e6e1da4a71c7..05627160cc3a 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 7f67a3ab1432..a678cb368186 100644 --- a/services/core/java/com/android/server/NetworkManagementService.java +++ b/services/core/java/com/android/server/NetworkManagementService.java @@ -34,6 +34,7 @@ import static android.net.NetworkPolicyManager.FIREWALL_RULE_DENY; 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; @@ -1863,7 +1864,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"); @@ -1906,14 +1913,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 ad661d79767d..4393e3527e4d 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 8209adeb8947..b14aa13bfb28 100644 --- a/services/core/java/com/android/server/net/NetworkStatsService.java +++ b/services/core/java/com/android/server/net/NetworkStatsService.java @@ -29,6 +29,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; @@ -1041,6 +1043,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. @@ -1371,14 +1378,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); } @@ -1397,7 +1404,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); } @@ -1409,9 +1416,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 3797cfc49095..b3c80ed5748f 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -6741,11 +6741,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 983e04d9930d..53a9b5a6befb 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; @@ -373,7 +376,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 @@ -412,20 +414,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 feb46d39b563..fa997958ba6d 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 expectNetworkStatsTethering(int how, NetworkStats stats) + throws Exception { + when(mNetManager.getNetworkStatsTethering(how)).thenReturn(stats); + } + private void expectNetworkStatsUidDetail(NetworkStats detail) throws Exception { - expectNetworkStatsUidDetail(detail, new String[0], new NetworkStats(0L, 0)); + expectNetworkStatsUidDetail(detail, new NetworkStats(0L, 0)); } - private void expectNetworkStatsUidDetail( - NetworkStats detail, String[] tetherIfacePairs, NetworkStats tetherStats) + 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 { |