Loading core/java/android/net/ITetheringStatsProvider.aidl +4 −1 Original line number Diff line number Diff line Loading @@ -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 Loading core/java/android/net/NetworkStats.java +5 −0 Original line number Diff line number Diff line Loading @@ -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 /** Loading core/java/android/os/INetworkManagementService.aidl +16 −1 Original line number Diff line number Diff line Loading @@ -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 **/ Loading Loading @@ -266,7 +281,7 @@ interface INetworkManagementService /** * Return summary of network statistics all tethering interfaces. */ NetworkStats getNetworkStatsTethering(); NetworkStats getNetworkStatsTethering(int how); /** * Set quota for an interface. Loading services/core/java/com/android/server/NetworkManagementService.java +22 −3 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -550,6 +551,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, String name) { SparseIntArray rules; Loading Loading @@ -1851,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"); Loading Loading @@ -1894,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); Loading services/core/java/com/android/server/connectivity/tethering/OffloadController.java +31 −5 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -60,6 +62,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; Loading Loading @@ -89,13 +93,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); } Loading Loading @@ -150,7 +155,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 Loading Loading @@ -180,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(); Loading Loading
core/java/android/net/ITetheringStatsProvider.aidl +4 −1 Original line number Diff line number Diff line Loading @@ -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 Loading
core/java/android/net/NetworkStats.java +5 −0 Original line number Diff line number Diff line Loading @@ -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 /** Loading
core/java/android/os/INetworkManagementService.aidl +16 −1 Original line number Diff line number Diff line Loading @@ -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 **/ Loading Loading @@ -266,7 +281,7 @@ interface INetworkManagementService /** * Return summary of network statistics all tethering interfaces. */ NetworkStats getNetworkStatsTethering(); NetworkStats getNetworkStatsTethering(int how); /** * Set quota for an interface. Loading
services/core/java/com/android/server/NetworkManagementService.java +22 −3 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -550,6 +551,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, String name) { SparseIntArray rules; Loading Loading @@ -1851,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"); Loading Loading @@ -1894,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); Loading
services/core/java/com/android/server/connectivity/tethering/OffloadController.java +31 −5 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -60,6 +62,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; Loading Loading @@ -89,13 +93,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); } Loading Loading @@ -150,7 +155,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 Loading Loading @@ -180,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(); Loading