Skip to content

Commit cdd02c5

Browse files
committed
Collect and persist tethering stats.
Use new "gettetherstats" netd command to retrieve statistics for active tethering connections. Keep tethering poll events separate from UID poll, even though they end up same historical structures. Bug: 5244846 Change-Id: Ia0c5165f6712c12b51586f86c331a2aad4ad6afb
1 parent f79ec36 commit cdd02c5

File tree

7 files changed

+186
-10
lines changed

7 files changed

+186
-10
lines changed

core/java/android/net/IConnectivityManager.aidl

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,12 @@ interface IConnectivityManager
8383

8484
String[] getTetheredIfaces();
8585

86+
/**
87+
* Return list of interface pairs that are actively tethered. Even indexes are
88+
* remote interface, and odd indexes are corresponding local interfaces.
89+
*/
90+
String[] getTetheredIfacePairs();
91+
8692
String[] getTetheringErroredIfaces();
8793

8894
String[] getTetherableUsbRegexs();

core/java/android/net/TrafficStats.java

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,26 +52,34 @@ public class TrafficStats {
5252
*/
5353
public static final int UID_REMOVED = -4;
5454

55+
/**
56+
* Special UID value used when collecting {@link NetworkStatsHistory} for
57+
* tethering traffic.
58+
*
59+
* @hide
60+
*/
61+
public static final int UID_TETHERING = -5;
62+
5563
/**
5664
* Default tag value for {@link DownloadManager} traffic.
5765
*
5866
* @hide
5967
*/
60-
public static final int TAG_SYSTEM_DOWNLOAD = 0xFFFF0001;
68+
public static final int TAG_SYSTEM_DOWNLOAD = 0xFFFFFF01;
6169

6270
/**
6371
* Default tag value for {@link MediaPlayer} traffic.
6472
*
6573
* @hide
6674
*/
67-
public static final int TAG_SYSTEM_MEDIA = 0xFFFF0002;
75+
public static final int TAG_SYSTEM_MEDIA = 0xFFFFFF02;
6876

6977
/**
7078
* Default tag value for {@link BackupManager} traffic.
7179
*
7280
* @hide
7381
*/
74-
public static final int TAG_SYSTEM_BACKUP = 0xFFFF0003;
82+
public static final int TAG_SYSTEM_BACKUP = 0xFFFFFF03;
7583

7684
/**
7785
* Snapshot of {@link NetworkStats} when the currently active profiling
@@ -90,6 +98,10 @@ public class TrafficStats {
9098
* <p>
9199
* Changes only take effect during subsequent calls to
92100
* {@link #tagSocket(Socket)}.
101+
* <p>
102+
* Tags between {@code 0xFFFFFF00} and {@code 0xFFFFFFFF} are reserved and
103+
* used internally by system services like {@link DownloadManager} when
104+
* performing traffic on behalf of an application.
93105
*/
94106
public static void setThreadStatsTag(int tag) {
95107
NetworkManagementSocketTagger.setThreadSocketStatsTag(tag);

core/java/android/os/INetworkManagementService.aidl

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,13 @@ interface INetworkManagementService
230230
*/
231231
NetworkStats getNetworkStatsUidDetail(int uid);
232232

233+
/**
234+
* Return summary of network statistics for the requested pairs of
235+
* tethering interfaces. Even indexes are remote interface, and odd
236+
* indexes are corresponding local interfaces.
237+
*/
238+
NetworkStats getNetworkStatsTethering(in String[] ifacePairs);
239+
233240
/**
234241
* Set quota for an interface.
235242
*/

services/java/com/android/server/ConnectivityService.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2394,6 +2394,12 @@ public String[] getTetheredIfaces() {
23942394
return mTethering.getTetheredIfaces();
23952395
}
23962396

2397+
@Override
2398+
public String[] getTetheredIfacePairs() {
2399+
enforceTetherAccessPermission();
2400+
return mTethering.getTetheredIfacePairs();
2401+
}
2402+
23972403
public String[] getTetheringErroredIfaces() {
23982404
enforceTetherAccessPermission();
23992405
return mTethering.getErroredIfaces();

services/java/com/android/server/NetworkManagementService.java

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,8 @@ class NetdResponseCode {
123123
public static final int InterfaceTxCounterResult = 217;
124124
public static final int InterfaceRxThrottleResult = 218;
125125
public static final int InterfaceTxThrottleResult = 219;
126+
public static final int QuotaCounterResult = 220;
127+
public static final int TetheringStatsResult = 221;
126128

127129
public static final int InterfaceChange = 600;
128130
public static final int BandwidthControl = 601;
@@ -1443,6 +1445,73 @@ private NetworkStats getNetworkStatsDetailUidstat(int limitUid) {
14431445
return stats;
14441446
}
14451447

1448+
@Override
1449+
public NetworkStats getNetworkStatsTethering(String[] ifacePairs) {
1450+
mContext.enforceCallingOrSelfPermission(
1451+
android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
1452+
1453+
if (ifacePairs.length % 2 != 0) {
1454+
throw new IllegalArgumentException(
1455+
"unexpected ifacePairs; length=" + ifacePairs.length);
1456+
}
1457+
1458+
final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 1);
1459+
for (int i = 0; i < ifacePairs.length; i += 2) {
1460+
final String ifaceIn = ifacePairs[i];
1461+
final String ifaceOut = ifacePairs[i + 1];
1462+
if (ifaceIn != null && ifaceOut != null) {
1463+
stats.combineValues(getNetworkStatsTethering(ifaceIn, ifaceOut));
1464+
}
1465+
}
1466+
return stats;
1467+
}
1468+
1469+
private NetworkStats.Entry getNetworkStatsTethering(String ifaceIn, String ifaceOut) {
1470+
final StringBuilder command = new StringBuilder();
1471+
command.append("bandwidth gettetherstats ").append(ifaceIn).append(" ").append(ifaceOut);
1472+
1473+
final String rsp;
1474+
try {
1475+
rsp = mConnector.doCommand(command.toString()).get(0);
1476+
} catch (NativeDaemonConnectorException e) {
1477+
throw new IllegalStateException("Error communicating to native daemon", e);
1478+
}
1479+
1480+
final String[] tok = rsp.split(" ");
1481+
/* Expecting: "code ifaceIn ifaceOut rx_bytes rx_packets tx_bytes tx_packets" */
1482+
if (tok.length != 7) {
1483+
throw new IllegalStateException("Native daemon returned unexpected result: " + rsp);
1484+
}
1485+
1486+
final int code;
1487+
try {
1488+
code = Integer.parseInt(tok[0]);
1489+
} catch (NumberFormatException e) {
1490+
throw new IllegalStateException(
1491+
"Failed to parse native daemon return code for " + ifaceIn + " " + ifaceOut);
1492+
}
1493+
if (code != NetdResponseCode.TetheringStatsResult) {
1494+
throw new IllegalStateException(
1495+
"Unexpected return code from native daemon for " + ifaceIn + " " + ifaceOut);
1496+
}
1497+
1498+
try {
1499+
final NetworkStats.Entry entry = new NetworkStats.Entry();
1500+
entry.iface = ifaceIn;
1501+
entry.uid = UID_ALL;
1502+
entry.set = SET_DEFAULT;
1503+
entry.tag = TAG_NONE;
1504+
entry.rxBytes = Long.parseLong(tok[3]);
1505+
entry.rxPackets = Long.parseLong(tok[4]);
1506+
entry.txBytes = Long.parseLong(tok[5]);
1507+
entry.txPackets = Long.parseLong(tok[6]);
1508+
return entry;
1509+
} catch (NumberFormatException e) {
1510+
throw new IllegalStateException(
1511+
"problem parsing tethering stats for " + ifaceIn + " " + ifaceOut + ": " + e);
1512+
}
1513+
}
1514+
14461515
public void setInterfaceThrottle(String iface, int rxKbps, int txKbps) {
14471516
mContext.enforceCallingOrSelfPermission(
14481517
android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");

services/java/com/android/server/connectivity/Tethering.java

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
import android.app.Notification;
2020
import android.app.NotificationManager;
2121
import android.app.PendingIntent;
22-
import android.bluetooth.BluetoothPan;
2322
import android.content.BroadcastReceiver;
2423
import android.content.Context;
2524
import android.content.Intent;
@@ -28,15 +27,14 @@
2827
import android.content.res.Resources;
2928
import android.hardware.usb.UsbManager;
3029
import android.net.ConnectivityManager;
31-
import android.net.InterfaceConfiguration;
3230
import android.net.IConnectivityManager;
3331
import android.net.INetworkManagementEventObserver;
32+
import android.net.InterfaceConfiguration;
3433
import android.net.LinkAddress;
3534
import android.net.LinkProperties;
3635
import android.net.NetworkInfo;
3736
import android.net.NetworkUtils;
3837
import android.os.Binder;
39-
import android.os.Handler;
4038
import android.os.HandlerThread;
4139
import android.os.IBinder;
4240
import android.os.INetworkManagementService;
@@ -51,6 +49,7 @@
5149
import com.android.internal.util.IState;
5250
import com.android.internal.util.State;
5351
import com.android.internal.util.StateMachine;
52+
import com.google.android.collect.Lists;
5453

5554
import java.io.FileDescriptor;
5655
import java.io.PrintWriter;
@@ -59,16 +58,15 @@
5958
import java.util.Collection;
6059
import java.util.HashMap;
6160
import java.util.Iterator;
62-
import java.util.LinkedList;
6361
import java.util.Set;
62+
6463
/**
6564
* @hide
6665
*
6766
* Timeout
6867
*
6968
* TODO - look for parent classes and code sharing
7069
*/
71-
7270
public class Tethering extends INetworkManagementEventObserver.Stub {
7371

7472
private Context mContext;
@@ -629,6 +627,19 @@ public String[] getTetheredIfaces() {
629627
return retVal;
630628
}
631629

630+
public String[] getTetheredIfacePairs() {
631+
final ArrayList<String> list = Lists.newArrayList();
632+
synchronized (mIfaces) {
633+
for (TetherInterfaceSM sm : mIfaces.values()) {
634+
if (sm.isTethered()) {
635+
list.add(sm.mMyUpstreamIfaceName);
636+
list.add(sm.mIfaceName);
637+
}
638+
}
639+
}
640+
return list.toArray(new String[list.size()]);
641+
}
642+
632643
public String[] getTetherableIfaces() {
633644
ArrayList<String> list = new ArrayList<String>();
634645
synchronized (mIfaces) {

services/java/com/android/server/net/NetworkStatsService.java

Lines changed: 67 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import static android.content.Intent.ACTION_UID_REMOVED;
2626
import static android.content.Intent.EXTRA_UID;
2727
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION_IMMEDIATE;
28+
import static android.net.ConnectivityManager.ACTION_TETHER_STATE_CHANGED;
2829
import static android.net.NetworkStats.IFACE_ALL;
2930
import static android.net.NetworkStats.SET_ALL;
3031
import static android.net.NetworkStats.SET_DEFAULT;
@@ -34,6 +35,7 @@
3435
import static android.net.NetworkTemplate.buildTemplateMobileAll;
3536
import static android.net.NetworkTemplate.buildTemplateWifi;
3637
import static android.net.TrafficStats.UID_REMOVED;
38+
import static android.net.TrafficStats.UID_TETHERING;
3739
import static android.provider.Settings.Secure.NETSTATS_FORCE_COMPLETE_POLL;
3840
import static android.provider.Settings.Secure.NETSTATS_NETWORK_BUCKET_DURATION;
3941
import static android.provider.Settings.Secure.NETSTATS_NETWORK_MAX_HISTORY;
@@ -68,6 +70,7 @@
6870
import android.net.NetworkStats;
6971
import android.net.NetworkStatsHistory;
7072
import android.net.NetworkTemplate;
73+
import android.net.TrafficStats;
7174
import android.os.Binder;
7275
import android.os.Environment;
7376
import android.os.Handler;
@@ -89,6 +92,7 @@
8992
import com.android.internal.os.AtomicFile;
9093
import com.android.internal.util.Objects;
9194
import com.android.server.EventLogTags;
95+
import com.android.server.connectivity.Tethering;
9296
import com.google.android.collect.Lists;
9397
import com.google.android.collect.Maps;
9498
import com.google.android.collect.Sets;
@@ -134,11 +138,12 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
134138
/** Flags to control detail level of poll event. */
135139
private static final int FLAG_POLL_NETWORK = 0x1;
136140
private static final int FLAG_POLL_UID = 0x2;
141+
private static final int FLAG_POLL_TETHER = 0x3;
137142
private static final int FLAG_PERSIST_NETWORK = 0x10;
138143
private static final int FLAG_PERSIST_UID = 0x20;
139144
private static final int FLAG_FORCE_PERSIST = 0x100;
140145

141-
private static final int FLAG_POLL_ALL = FLAG_POLL_NETWORK | FLAG_POLL_UID;
146+
private static final int FLAG_POLL_ALL = FLAG_POLL_NETWORK | FLAG_POLL_UID | FLAG_POLL_TETHER;
142147
private static final int FLAG_PERSIST_ALL = FLAG_PERSIST_NETWORK | FLAG_PERSIST_UID;
143148

144149
private final Context mContext;
@@ -195,6 +200,7 @@ public interface NetworkStatsSettings {
195200
private NetworkStats mLastPollNetworkSnapshot;
196201
private NetworkStats mLastPollUidSnapshot;
197202
private NetworkStats mLastPollOperationsSnapshot;
203+
private NetworkStats mLastPollTetherSnapshot;
198204

199205
private NetworkStats mLastPersistNetworkSnapshot;
200206
private NetworkStats mLastPersistUidSnapshot;
@@ -258,6 +264,10 @@ public void systemReady() {
258264
final IntentFilter connFilter = new IntentFilter(CONNECTIVITY_ACTION_IMMEDIATE);
259265
mContext.registerReceiver(mConnReceiver, connFilter, CONNECTIVITY_INTERNAL, mHandler);
260266

267+
// watch for tethering changes
268+
final IntentFilter tetherFilter = new IntentFilter(ACTION_TETHER_STATE_CHANGED);
269+
mContext.registerReceiver(mTetherReceiver, tetherFilter, CONNECTIVITY_INTERNAL, mHandler);
270+
261271
// listen for periodic polling events
262272
final IntentFilter pollFilter = new IntentFilter(ACTION_NETWORK_STATS_POLL);
263273
mContext.registerReceiver(mPollReceiver, pollFilter, READ_NETWORK_USAGE_HISTORY, mHandler);
@@ -543,6 +553,18 @@ public void onReceive(Context context, Intent intent) {
543553
}
544554
};
545555

556+
/**
557+
* Receiver that watches for {@link Tethering} to claim interface pairs.
558+
*/
559+
private BroadcastReceiver mTetherReceiver = new BroadcastReceiver() {
560+
@Override
561+
public void onReceive(Context context, Intent intent) {
562+
// on background handler thread, and verified CONNECTIVITY_INTERNAL
563+
// permission above.
564+
performPoll(FLAG_POLL_TETHER);
565+
}
566+
};
567+
546568
private BroadcastReceiver mPollReceiver = new BroadcastReceiver() {
547569
@Override
548570
public void onReceive(Context context, Intent intent) {
@@ -686,12 +708,14 @@ private void performPollLocked(int flags) {
686708

687709
boolean pollNetwork = (flags & FLAG_POLL_NETWORK) != 0;
688710
boolean pollUid = (flags & FLAG_POLL_UID) != 0;
711+
boolean pollTether = (flags & FLAG_POLL_TETHER) != 0;
689712

690713
// when complete poll requested, any partial poll enables everything
691714
final boolean forceCompletePoll = mSettings.getForceCompletePoll();
692-
if (forceCompletePoll && (pollNetwork || pollUid)) {
715+
if (forceCompletePoll && (pollNetwork || pollUid || pollTether)) {
693716
pollNetwork = true;
694717
pollUid = true;
718+
pollTether = true;
695719
}
696720

697721
final boolean persistNetwork = (flags & FLAG_PERSIST_NETWORK) != 0;
@@ -723,6 +747,15 @@ private void performPollLocked(int flags) {
723747
}
724748
}
725749

750+
if (pollTether) {
751+
final String[] ifacePairs = mConnManager.getTetheredIfacePairs();
752+
final NetworkStats tetherSnapshot = mNetworkManager.getNetworkStatsTethering(
753+
ifacePairs);
754+
performTetherPollLocked(tetherSnapshot, currentTime);
755+
756+
// persisted during normal UID cycle below
757+
}
758+
726759
if (pollUid) {
727760
final NetworkStats uidSnapshot = mNetworkManager.getNetworkStatsUidDetail(UID_ALL);
728761
performUidPollLocked(uidSnapshot, currentTime);
@@ -848,6 +881,38 @@ private void performUidPollLocked(NetworkStats uidSnapshot, long currentTime) {
848881
mOperations = new NetworkStats(0L, 10);
849882
}
850883

884+
/**
885+
* Update {@link #mUidStats} historical usage for
886+
* {@link TrafficStats#UID_TETHERING} based on tethering statistics.
887+
*/
888+
private void performTetherPollLocked(NetworkStats tetherSnapshot, long currentTime) {
889+
ensureUidStatsLoadedLocked();
890+
891+
final NetworkStats delta = computeStatsDelta(
892+
mLastPollTetherSnapshot, tetherSnapshot, false);
893+
final long timeStart = currentTime - delta.getElapsedRealtime();
894+
895+
NetworkStats.Entry entry = null;
896+
for (int i = 0; i < delta.size(); i++) {
897+
entry = delta.getValues(i, entry);
898+
final NetworkIdentitySet ident = mActiveIfaces.get(entry.iface);
899+
if (ident == null) {
900+
if (entry.rxBytes > 0 || entry.rxPackets > 0 || entry.txBytes > 0
901+
|| entry.txPackets > 0) {
902+
Log.w(TAG, "dropping tether delta from unknown iface: " + entry);
903+
}
904+
continue;
905+
}
906+
907+
final NetworkStatsHistory history = findOrCreateUidStatsLocked(
908+
ident, UID_TETHERING, SET_DEFAULT, TAG_NONE);
909+
history.recordData(timeStart, currentTime, entry);
910+
}
911+
912+
// normal UID poll will trim any history beyond max
913+
mLastPollTetherSnapshot = tetherSnapshot;
914+
}
915+
851916
/**
852917
* Sample recent statistics summary into {@link EventLog}.
853918
*/

0 commit comments

Comments
 (0)