/**
* Return number of packets transmitted since device boot. Counts packets
* across all network interfaces, and always increases monotonically since
* device boot. Statistics are measured at the network layer, so they
* include both TCP and UDP usage.
*/
public static long getTotalTxPackets()
/**
* Return number of packets received since device boot. Counts packets
* across all network interfaces, and always increases monotonically since
* device boot. Statistics are measured at the network layer, so they
* include both TCP and UDP usage.
*/
public static long getTotalRxPackets()
/**
* Return number of bytes transmitted since device boot. Counts packets
* across all network interfaces, and always increases monotonically since
* device boot. Statistics are measured at the network layer, so they
* include both TCP and UDP usage.
*/
public static long getTotalTxBytes()
/**
* Return number of bytes received since device boot. Counts packets across
* all network interfaces, and always increases monotonically since device
* boot. Statistics are measured at the network layer, so they include both
* TCP and UDP usage.
*/
public static long getTotalRxBytes()
2 统计移动网络流量
/**
* Return number of packets transmitted across mobile networks since device
* boot. Counts packets across all mobile network interfaces, and always
* increases monotonically since device boot. Statistics are measured at the
* network layer, so they include both TCP and UDP usage.
*/
public static long getMobileTxPackets()
/**
* Return number of packets received across mobile networks since device
* boot. Counts packets across all mobile network interfaces, and always
* increases monotonically since device boot. Statistics are measured at the
* network layer, so they include both TCP and UDP usage.
*/
public static long getMobileRxPackets()
/**
* Return number of bytes transmitted across mobile networks since device
* boot. Counts packets across all mobile network interfaces, and always
* increases monotonically since device boot. Statistics are measured at the
* network layer, so they include both TCP and UDP usage.
*/
public static long getMobileTxBytes()
/**
* Return number of bytes received across mobile networks since device boot.
* Counts packets across all mobile network interfaces, and always increases
* monotonically since device boot. Statistics are measured at the network
* layer, so they include both TCP and UDP usage.
*/
public static long getMobileRxBytes()
3 按接口统计流量
/**
* Return the number of packets transmitted on the specified interface since the interface
* was created. Statistics are measured at the network layer, so both TCP and
* UDP usage are included.
*
* Note that the returned values are partial statistics that do not count data from several
* sources and do not apply several adjustments that are necessary for correctness, such
* as adjusting for VPN apps, IPv6-in-IPv4 translation, etc. These values can be used to
* determine whether traffic is being transferred on the specific interface but are not a
* substitute for the more accurate statistics provided by the {@link NetworkStatsManager}
* APIs.
*/
public static long getTxPackets(@NonNull String iface)
/**
* Return the number of packets received on the specified interface since the interface was
* created. Statistics are measured at the network layer, so both TCP
* and UDP usage are included.
*
* Note that the returned values are partial statistics that do not count data from several
* sources and do not apply several adjustments that are necessary for correctness, such
* as adjusting for VPN apps, IPv6-in-IPv4 translation, etc. These values can be used to
* determine whether traffic is being transferred on the specific interface but are not a
* substitute for the more accurate statistics provided by the {@link NetworkStatsManager}
* APIs.
*/
public static long getRxPackets(@NonNull String iface)
/**
* Return the number of bytes transmitted on the specified interface since the interface
* was created. Statistics are measured at the network layer, so both TCP and
* UDP usage are included.
*
* Note that the returned values are partial statistics that do not count data from several
* sources and do not apply several adjustments that are necessary for correctness, such
* as adjusting for VPN apps, IPv6-in-IPv4 translation, etc. These values can be used to
* determine whether traffic is being transferred on the specific interface but are not a
* substitute for the more accurate statistics provided by the {@link NetworkStatsManager}
* APIs.
*/
public static long getTxBytes(@NonNull String iface)
/**
* Return the number of bytes received on the specified interface since the interface
* was created. Statistics are measured at the network layer, so both TCP
* and UDP usage are included.
*
* Note that the returned values are partial statistics that do not count data from several
* sources and do not apply several adjustments that are necessary for correctness, such
* as adjusting for VPN apps, IPv6-in-IPv4 translation, etc. These values can be used to
* determine whether traffic is being transferred on the specific interface but are not a
* substitute for the more accurate statistics provided by the {@link NetworkStatsManager}
* APIs.
*/
public static long getRxBytes(@NonNull String iface)
4 按UID统计流量
/**
* Return number of bytes transmitted by the given UID since device boot.
* Counts packets across all network interfaces, and always increases
* monotonically since device boot. Statistics are measured at the network
* layer, so they include both TCP and UDP usage.
*/
public static long getUidTxBytes(int uid)
/**
* Return number of bytes received by the given UID since device boot.
* Counts packets across all network interfaces, and always increases
* monotonically since device boot. Statistics are measured at the network
* layer, so they include both TCP and UDP usage.
*/
public static long getUidRxBytes(int uid)
/**
* Return number of packets transmitted by the given UID since device boot.
* Counts packets across all network interfaces, and always increases
* monotonically since device boot. Statistics are measured at the network
* layer, so they include both TCP and UDP usage.
*/
public static long getUidTxPackets(int uid)
/**
* Return number of packets received by the given UID since device boot.
* Counts packets across all network interfaces, and always increases
* monotonically since device boot. Statistics are measured at the network
* layer, so they include both TCP and UDP usage.
*/
public static long getUidRxPackets(int uid)
5 按时间段统计流量
/**
* Query network usage statistics summaries. Result is summarised data usage for the whole
* device. Result is a single Bucket aggregated over time, state, uid, tag, metered, and
* roaming. This means the bucket's start and end timestamp are going to be the same as the
* 'startTime' and 'endTime' parameters. State is going to be
* {@link NetworkStats.Bucket#STATE_ALL}, uid {@link NetworkStats.Bucket#UID_ALL},
* tag {@link NetworkStats.Bucket#TAG_NONE},
* default network {@link NetworkStats.Bucket#DEFAULT_NETWORK_ALL},
* metered {@link NetworkStats.Bucket#METERED_ALL},
* and roaming {@link NetworkStats.Bucket#ROAMING_ALL}.
* This may take a long time, and apps should avoid calling this on their main thread.
*
* @param networkType As defined in {@link ConnectivityManager}, e.g.
* {@link ConnectivityManager#TYPE_MOBILE}, {@link ConnectivityManager#TYPE_WIFI}
* etc.
* @param subscriberId If applicable, the subscriber id of the network interface.
* <p>Starting with API level 29, the {@code subscriberId} is guarded by
* additional restrictions. Calling apps that do not meet the new
* requirements to access the {@code subscriberId} can provide a {@code
* null} value when querying for the mobile network type to receive usage
* for all mobile networks. For additional details see {@link
* TelephonyManager#getSubscriberId()}.
* <p>Starting with API level 31, calling apps can provide a
* {@code subscriberId} with wifi network type to receive usage for
* wifi networks which is under the given subscription if applicable.
* Otherwise, pass {@code null} when querying all wifi networks.
* @param startTime Start of period. Defined in terms of "Unix time", see
* {@link java.lang.System#currentTimeMillis}.
* @param endTime End of period. Defined in terms of "Unix time", see
* {@link java.lang.System#currentTimeMillis}.
* @return Bucket object or null if permissions are insufficient or error happened during
* statistics collection.
*/
@WorkerThread
public Bucket querySummaryForDevice(int networkType, String subscriberId,
long startTime, long endTime)
/**
* Query network usage statistics summaries. Result is summarised data usage for all uids
* belonging to calling user. Result is a single Bucket aggregated over time, state and uid.
* This means the bucket's start and end timestamp are going to be the same as the 'startTime'
* and 'endTime' parameters. State is going to be {@link NetworkStats.Bucket#STATE_ALL},
* uid {@link NetworkStats.Bucket#UID_ALL}, tag {@link NetworkStats.Bucket#TAG_NONE},
* metered {@link NetworkStats.Bucket#METERED_ALL}, and roaming
* {@link NetworkStats.Bucket#ROAMING_ALL}.
* This may take a long time, and apps should avoid calling this on their main thread.
*/
@WorkerThread
public Bucket querySummaryForUser(int networkType, String subscriberId, long startTime,
long endTime)
/**
* Query network usage statistics summaries. Result filtered to include only uids belonging to
* calling user. Result is aggregated over time, hence all buckets will have the same start and
* end timestamps. Not aggregated over state, uid, default network, metered, or roaming. This
* means buckets' start and end timestamps are going to be the same as the 'startTime' and
* 'endTime' parameters. State, uid, metered, and roaming are going to vary, and tag is going to
* be the same.
* This may take a long time, and apps should avoid calling this on their main thread.
*/
@WorkerThread
public NetworkStats querySummary(int networkType, String subscriberId, long startTime,
long endTime)
/**
* Query network usage statistics details. Result filtered to include only uids belonging to
* calling user. Result is aggregated over state but not aggregated over time, uid, tag,
* metered, nor roaming. This means buckets' start and end timestamps are going to be between
* 'startTime' and 'endTime' parameters. State is going to be
* {@link NetworkStats.Bucket#STATE_ALL}, uid will vary,
* tag {@link NetworkStats.Bucket#TAG_NONE},
* default network is going to be {@link NetworkStats.Bucket#DEFAULT_NETWORK_ALL},
* metered is going to be {@link NetworkStats.Bucket#METERED_ALL},
* and roaming is going to be {@link NetworkStats.Bucket#ROAMING_ALL}.
* <p>Only includes buckets that atomically occur in the inclusive time range. Doesn't
* interpolate across partial buckets. Since bucket length is in the order of hours, this
* method cannot be used to measure data usage on a fine grained time scale.
* This may take a long time, and apps should avoid calling this on their main thread.
*/
@WorkerThread
public NetworkStats queryDetails(int networkType, String subscriberId, long startTime,
long endTime)
6 按时间段+UID/tag/state统计流量
/**
* Query network usage statistics details for a given uid.
* This may take a long time, and apps should avoid calling this on their main thread.
*
* @see #queryDetailsForUidTagState(int, String, long, long, int, int, int)
*/
@WorkerThread
public NetworkStats queryDetailsForUid(int networkType, String subscriberId,
long startTime, long endTime, int uid)
/**
* Query network usage statistics details for a given uid and tag.
* This may take a long time, and apps should avoid calling this on their main thread.
*
* @see #queryDetailsForUidTagState(int, String, long, long, int, int, int)
*/
@WorkerThread
public NetworkStats queryDetailsForUidTag(int networkType, String subscriberId,
long startTime, long endTime, int uid, int tag)
/**
* Query network usage statistics details for a given uid, tag, and state. Only usable for uids
* belonging to calling user. Result is not aggregated over time. This means buckets' start and
* end timestamps are going to be between 'startTime' and 'endTime' parameters. The uid is going
* to be the same as the 'uid' parameter, the tag the same as the 'tag' parameter, and the state
* the same as the 'state' parameter.
* defaultNetwork is going to be {@link NetworkStats.Bucket#DEFAULT_NETWORK_ALL},
* metered is going to be {@link NetworkStats.Bucket#METERED_ALL}, and
* roaming is going to be {@link NetworkStats.Bucket#ROAMING_ALL}.
* <p>Only includes buckets that atomically occur in the inclusive time range. Doesn't
* interpolate across partial buckets. Since bucket length is in the order of hours, this
* method cannot be used to measure data usage on a fine grained time scale.
* This may take a long time, and apps should avoid calling this on their main thread.
*/
@WorkerThread
public NetworkStats queryDetailsForUidTagState(int networkType, String subscriberId,
long startTime, long endTime, int uid, int tag, int state)
3 Test
1 NetworkStatsManagerTest
public class NetworkStatsManagerTest {
private static final String TEST_SUBSCRIBER_ID = "subid";
private @Mock INetworkStatsService mService;
private @Mock INetworkStatsSession mStatsSession;
private NetworkStatsManager mManager;
// TODO: change to NetworkTemplate.MATCH_MOBILE once internal constant rename is merged to aosp.
private static final int MATCH_MOBILE_ALL = 1;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mManager = new NetworkStatsManager(InstrumentationRegistry.getContext(), mService);
}
@Test
public void testQueryDetails() throws RemoteException {
final long startTime = 1;
final long endTime = 100;
final int uid1 = 10001;
final int uid2 = 10002;
final int uid3 = 10003;
Entry uid1Entry1 = new Entry("if1", uid1,
android.net.NetworkStats.SET_DEFAULT, android.net.NetworkStats.TAG_NONE,
100, 10, 200, 20, 0);
Entry uid1Entry2 = new Entry(
"if2", uid1,
android.net.NetworkStats.SET_DEFAULT, android.net.NetworkStats.TAG_NONE,
100, 10, 200, 20, 0);
Entry uid2Entry1 = new Entry("if1", uid2,
android.net.NetworkStats.SET_DEFAULT, android.net.NetworkStats.TAG_NONE,
150, 10, 250, 20, 0);
Entry uid2Entry2 = new Entry(
"if2", uid2,
android.net.NetworkStats.SET_DEFAULT, android.net.NetworkStats.TAG_NONE,
150, 10, 250, 20, 0);
NetworkStatsHistory history1 = new NetworkStatsHistory(10, 2);
history1.recordData(10, 20, uid1Entry1);
history1.recordData(20, 30, uid1Entry2);
NetworkStatsHistory history2 = new NetworkStatsHistory(10, 2);
history1.recordData(30, 40, uid2Entry1);
history1.recordData(35, 45, uid2Entry2);
when(mService.openSessionForUsageStats(anyInt(), anyString())).thenReturn(mStatsSession);
when(mStatsSession.getRelevantUids()).thenReturn(new int[] { uid1, uid2, uid3 });
when(mStatsSession.getHistoryIntervalForUid(any(NetworkTemplate.class),
eq(uid1), eq(android.net.NetworkStats.SET_ALL),
eq(android.net.NetworkStats.TAG_NONE),
eq(NetworkStatsHistory.FIELD_ALL), eq(startTime), eq(endTime)))
.then((InvocationOnMock inv) -> {
NetworkTemplate template = inv.getArgument(0);
assertEquals(MATCH_MOBILE_ALL, template.getMatchRule());
assertEquals(TEST_SUBSCRIBER_ID, template.getSubscriberId());
return history1;
});
when(mStatsSession.getHistoryIntervalForUid(any(NetworkTemplate.class),
eq(uid2), eq(android.net.NetworkStats.SET_ALL),
eq(android.net.NetworkStats.TAG_NONE),
eq(NetworkStatsHistory.FIELD_ALL), eq(startTime), eq(endTime)))
.then((InvocationOnMock inv) -> {
NetworkTemplate template = inv.getArgument(0);
assertEquals(MATCH_MOBILE_ALL, template.getMatchRule());
assertEquals(TEST_SUBSCRIBER_ID, template.getSubscriberId());
return history2;
});
NetworkStats stats = mManager.queryDetails(
ConnectivityManager.TYPE_MOBILE, TEST_SUBSCRIBER_ID, startTime, endTime);
NetworkStats.Bucket bucket = new NetworkStats.Bucket();
// First 2 buckets exactly match entry timings
assertTrue(stats.getNextBucket(bucket));
assertEquals(10, bucket.getStartTimeStamp());
assertEquals(20, bucket.getEndTimeStamp());
assertBucketMatches(uid1Entry1, bucket);
assertTrue(stats.getNextBucket(bucket));
assertEquals(20, bucket.getStartTimeStamp());
assertEquals(30, bucket.getEndTimeStamp());
assertBucketMatches(uid1Entry2, bucket);
// 30 -> 40: contains uid2Entry1 and half of uid2Entry2
assertTrue(stats.getNextBucket(bucket));
assertEquals(30, bucket.getStartTimeStamp());
assertEquals(40, bucket.getEndTimeStamp());
assertEquals(225, bucket.getRxBytes());
assertEquals(15, bucket.getRxPackets());
assertEquals(375, bucket.getTxBytes());
assertEquals(30, bucket.getTxPackets());
// 40 -> 50: contains half of uid2Entry2
assertTrue(stats.getNextBucket(bucket));
assertEquals(40, bucket.getStartTimeStamp());
assertEquals(50, bucket.getEndTimeStamp());
assertEquals(75, bucket.getRxBytes());
assertEquals(5, bucket.getRxPackets());
assertEquals(125, bucket.getTxBytes());
assertEquals(10, bucket.getTxPackets());
assertFalse(stats.hasNextBucket());
}
private void runQueryDetailsAndCheckTemplate(int networkType, String subscriberId,
NetworkTemplate expectedTemplate) throws RemoteException {
final long startTime = 1;
final long endTime = 100;
final int uid1 = 10001;
final int uid2 = 10002;
reset(mStatsSession);
when(mService.openSessionForUsageStats(anyInt(), anyString())).thenReturn(mStatsSession);
when(mStatsSession.getRelevantUids()).thenReturn(new int[] { uid1, uid2 });
when(mStatsSession.getHistoryIntervalForUid(any(NetworkTemplate.class),
anyInt(), anyInt(), anyInt(), anyInt(), anyLong(), anyLong()))
.thenReturn(new NetworkStatsHistory(10, 0));
NetworkStats stats = mManager.queryDetails(
networkType, subscriberId, startTime, endTime);
verify(mStatsSession, times(1)).getHistoryIntervalForUid(
eq(expectedTemplate),
eq(uid1), eq(android.net.NetworkStats.SET_ALL),
eq(android.net.NetworkStats.TAG_NONE),
eq(NetworkStatsHistory.FIELD_ALL), eq(startTime), eq(endTime));
verify(mStatsSession, times(1)).getHistoryIntervalForUid(
eq(expectedTemplate),
eq(uid2), eq(android.net.NetworkStats.SET_ALL),
eq(android.net.NetworkStats.TAG_NONE),
eq(NetworkStatsHistory.FIELD_ALL), eq(startTime), eq(endTime));
assertFalse(stats.hasNextBucket());
}
@Test
public void testNetworkTemplateWhenRunningQueryDetails_NoSubscriberId() throws RemoteException {
runQueryDetailsAndCheckTemplate(ConnectivityManager.TYPE_MOBILE,
null /* subscriberId */, NetworkTemplate.buildTemplateMobileWildcard());
runQueryDetailsAndCheckTemplate(ConnectivityManager.TYPE_WIFI,
"" /* subscriberId */, NetworkTemplate.buildTemplateWifiWildcard());
runQueryDetailsAndCheckTemplate(ConnectivityManager.TYPE_WIFI,
null /* subscriberId */, NetworkTemplate.buildTemplateWifiWildcard());
}
@Test
public void testNetworkTemplateWhenRunningQueryDetails_MergedCarrierWifi()
throws RemoteException {
runQueryDetailsAndCheckTemplate(ConnectivityManager.TYPE_WIFI,
TEST_SUBSCRIBER_ID,
NetworkTemplate.buildTemplateWifi(NetworkTemplate.WIFI_NETWORKID_ALL,
TEST_SUBSCRIBER_ID));
}
private void assertBucketMatches(Entry expected, NetworkStats.Bucket actual) {
assertEquals(expected.uid, actual.getUid());
assertEquals(expected.rxBytes, actual.getRxBytes());
assertEquals(expected.rxPackets, actual.getRxPackets());
assertEquals(expected.txBytes, actual.getTxBytes());
assertEquals(expected.txPackets, actual.getTxPackets());
}
}
2 TrafficStatsTest
public class TrafficStatsTest extends AndroidTestCase {
private static final String LOG_TAG = "TrafficStatsTest";
/** Verify the given value is in range [lower, upper] */
private void assertInRange(String tag, long value, long lower, long upper) {
final Range range = new Range(lower, upper);
assertTrue(tag + ": " + value + " is not within range [" + lower + ", " + upper + "]",
range.contains(value));
}
public void testValidMobileStats() {
// We can't assume a mobile network is even present in this test, so
// we simply assert that a valid value is returned.
assertTrue(TrafficStats.getMobileTxPackets() >= 0);
assertTrue(TrafficStats.getMobileRxPackets() >= 0);
assertTrue(TrafficStats.getMobileTxBytes() >= 0);
assertTrue(TrafficStats.getMobileRxBytes() >= 0);
}
public void testValidTotalStats() {
assertTrue(TrafficStats.getTotalTxPackets() >= 0);
assertTrue(TrafficStats.getTotalRxPackets() >= 0);
assertTrue(TrafficStats.getTotalTxBytes() >= 0);
assertTrue(TrafficStats.getTotalRxBytes() >= 0);
}
public void testValidIfaceStats() {
assertTrue(TrafficStats.getTxPackets("lo") >= 0);
assertTrue(TrafficStats.getRxPackets("lo") >= 0);
assertTrue(TrafficStats.getTxBytes("lo") >= 0);
assertTrue(TrafficStats.getRxBytes("lo") >= 0);
}
public void testThreadStatsTag() throws Exception {
TrafficStats.setThreadStatsTag(0xf00d);
assertTrue("Tag didn't stick", TrafficStats.getThreadStatsTag() == 0xf00d);
final CountDownLatch latch = new CountDownLatch(1);
new Thread("TrafficStatsTest.testThreadStatsTag") {
@Override
public void run() {
assertTrue("Tag leaked", TrafficStats.getThreadStatsTag() != 0xf00d);
TrafficStats.setThreadStatsTag(0xcafe);
assertTrue("Tag didn't stick", TrafficStats.getThreadStatsTag() == 0xcafe);
latch.countDown();
}
}.start();
latch.await(5, TimeUnit.SECONDS);
assertTrue("Tag lost", TrafficStats.getThreadStatsTag() == 0xf00d);
TrafficStats.clearThreadStatsTag();
assertTrue("Tag not cleared", TrafficStats.getThreadStatsTag() != 0xf00d);
}
long tcpPacketToIpBytes(long packetCount, long bytes) {
// ip header + tcp header + data.
// Tcp header is mostly 32. Syn has different tcp options -> 40. Don't care.
return packetCount * (20 + 32 + bytes);
}
@AppModeFull(reason = "Socket cannot bind in instant app mode")
public void testTrafficStatsForLocalhost() throws IOException {
final long mobileTxPacketsBefore = TrafficStats.getMobileTxPackets();
final long mobileRxPacketsBefore = TrafficStats.getMobileRxPackets();
final long mobileTxBytesBefore = TrafficStats.getMobileTxBytes();
final long mobileRxBytesBefore = TrafficStats.getMobileRxBytes();
final long totalTxPacketsBefore = TrafficStats.getTotalTxPackets();
final long totalRxPacketsBefore = TrafficStats.getTotalRxPackets();
final long totalTxBytesBefore = TrafficStats.getTotalTxBytes();
final long totalRxBytesBefore = TrafficStats.getTotalRxBytes();
final long uidTxBytesBefore = TrafficStats.getUidTxBytes(Process.myUid());
final long uidRxBytesBefore = TrafficStats.getUidRxBytes(Process.myUid());
final long uidTxPacketsBefore = TrafficStats.getUidTxPackets(Process.myUid());
final long uidRxPacketsBefore = TrafficStats.getUidRxPackets(Process.myUid());
final long ifaceTxPacketsBefore = TrafficStats.getTxPackets("lo");
final long ifaceRxPacketsBefore = TrafficStats.getRxPackets("lo");
final long ifaceTxBytesBefore = TrafficStats.getTxBytes("lo");
final long ifaceRxBytesBefore = TrafficStats.getRxBytes("lo");
// Transfer 1MB of data across an explicitly localhost socket.
final int byteCount = 1024;
final int packetCount = 1024;
TrafficStats.startDataProfiling(null);
final ServerSocket server = new ServerSocket(0);
new Thread("TrafficStatsTest.testTrafficStatsForLocalhost") {
@Override
public void run() {
try {
final Socket socket = new Socket("localhost", server.getLocalPort());
// Make sure that each write()+flush() turns into a packet:
// disable Nagle.
socket.setTcpNoDelay(true);
final OutputStream out = socket.getOutputStream();
final byte[] buf = new byte[byteCount];
TrafficStats.setThreadStatsTag(0x42);
TrafficStats.tagSocket(socket);
for (int i = 0; i < packetCount; i++) {
out.write(buf);
out.flush();
try {
// Bug: 10668088, Even with Nagle disabled, and flushing the 1024 bytes
// the kernel still regroups data into a larger packet.
Thread.sleep(5);
} catch (InterruptedException e) {
}
}
out.close();
socket.close();
} catch (IOException e) {
Log.i(LOG_TAG, "Badness during writes to socket: " + e);
}
}
}.start();
int read = 0;
try {
final Socket socket = server.accept();
socket.setTcpNoDelay(true);
TrafficStats.setThreadStatsTag(0x43);
TrafficStats.tagSocket(socket);
final InputStream in = socket.getInputStream();
final byte[] buf = new byte[byteCount];
while (read < byteCount * packetCount) {
int n = in.read(buf);
assertTrue("Unexpected EOF", n > 0);
read += n;
}
} finally {
server.close();
}
assertTrue("Not all data read back", read >= byteCount * packetCount);
// It's too fast to call getUidTxBytes function.
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
final NetworkStats testStats = TrafficStats.stopDataProfiling(null);
final long mobileTxPacketsAfter = TrafficStats.getMobileTxPackets();
final long mobileRxPacketsAfter = TrafficStats.getMobileRxPackets();
final long mobileTxBytesAfter = TrafficStats.getMobileTxBytes();
final long mobileRxBytesAfter = TrafficStats.getMobileRxBytes();
final long totalTxPacketsAfter = TrafficStats.getTotalTxPackets();
final long totalRxPacketsAfter = TrafficStats.getTotalRxPackets();
final long totalTxBytesAfter = TrafficStats.getTotalTxBytes();
final long totalRxBytesAfter = TrafficStats.getTotalRxBytes();
final long uidTxBytesAfter = TrafficStats.getUidTxBytes(Process.myUid());
final long uidRxBytesAfter = TrafficStats.getUidRxBytes(Process.myUid());
final long uidTxPacketsAfter = TrafficStats.getUidTxPackets(Process.myUid());
final long uidRxPacketsAfter = TrafficStats.getUidRxPackets(Process.myUid());
final long uidTxDeltaBytes = uidTxBytesAfter - uidTxBytesBefore;
final long uidTxDeltaPackets = uidTxPacketsAfter - uidTxPacketsBefore;
final long uidRxDeltaBytes = uidRxBytesAfter - uidRxBytesBefore;
final long uidRxDeltaPackets = uidRxPacketsAfter - uidRxPacketsBefore;
final long ifaceTxPacketsAfter = TrafficStats.getTxPackets("lo");
final long ifaceRxPacketsAfter = TrafficStats.getRxPackets("lo");
final long ifaceTxBytesAfter = TrafficStats.getTxBytes("lo");
final long ifaceRxBytesAfter = TrafficStats.getRxBytes("lo");
final long ifaceTxDeltaPackets = ifaceTxPacketsAfter - ifaceTxPacketsBefore;
final long ifaceRxDeltaPackets = ifaceRxPacketsAfter - ifaceRxPacketsBefore;
final long ifaceTxDeltaBytes = ifaceTxBytesAfter - ifaceTxBytesBefore;
final long ifaceRxDeltaBytes = ifaceRxBytesAfter - ifaceRxBytesBefore;
// Localhost traffic *does* count against per-UID stats.
/*
* Calculations:
* - bytes
* bytes is approx: packets * data + packets * acks;
* but sometimes there are less acks than packets, so we set a lower
* limit of 1 ack.
* - setup/teardown
* + 7 approx.: syn, syn-ack, ack, fin-ack, ack, fin-ack, ack;
* but sometimes the last find-acks just vanish, so we set a lower limit of +5.
*/
final int maxExpectedExtraPackets = 7;
final int minExpectedExtraPackets = 5;
// Some other tests don't cleanup connections correctly.
// They have the same UID, so we discount their lingering traffic
// which happens only on non-localhost, such as TCP FIN retranmission packets
final long deltaTxOtherPackets = (totalTxPacketsAfter - totalTxPacketsBefore)
- uidTxDeltaPackets;
final long deltaRxOtherPackets = (totalRxPacketsAfter - totalRxPacketsBefore)
- uidRxDeltaPackets;
if (deltaTxOtherPackets > 0 || deltaRxOtherPackets > 0) {
Log.i(LOG_TAG, "lingering traffic data: " + deltaTxOtherPackets + "/"
+ deltaRxOtherPackets);
}
// Check that the per-uid stats obtained from data profiling contain the expected values.
// The data profiling snapshot is generated from the readNetworkStatsDetail() method in
// networkStatsService, so it's possible to verify that the detailed stats for a given
// uid are correct.
final NetworkStats.Entry entry = testStats.getTotal(null, Process.myUid());
final long pktBytes = tcpPacketToIpBytes(packetCount, byteCount);
final long pktWithNoDataBytes = tcpPacketToIpBytes(packetCount, 0);
final long minExpExtraPktBytes = tcpPacketToIpBytes(minExpectedExtraPackets, 0);
final long maxExpExtraPktBytes = tcpPacketToIpBytes(maxExpectedExtraPackets, 0);
final long deltaTxOtherPktBytes = tcpPacketToIpBytes(deltaTxOtherPackets, 0);
final long deltaRxOtherPktBytes = tcpPacketToIpBytes(deltaRxOtherPackets, 0);
assertInRange("txPackets detail", entry.txPackets, packetCount + minExpectedExtraPackets,
uidTxDeltaPackets);
assertInRange("rxPackets detail", entry.rxPackets, packetCount + minExpectedExtraPackets,
uidRxDeltaPackets);
assertInRange("txBytes detail", entry.txBytes, pktBytes + minExpExtraPktBytes,
uidTxDeltaBytes);
assertInRange("rxBytes detail", entry.rxBytes, pktBytes + minExpExtraPktBytes,
uidRxDeltaBytes);
assertInRange("uidtxp", uidTxDeltaPackets, packetCount + minExpectedExtraPackets,
packetCount + packetCount + maxExpectedExtraPackets + deltaTxOtherPackets);
assertInRange("uidrxp", uidRxDeltaPackets, packetCount + minExpectedExtraPackets,
packetCount + packetCount + maxExpectedExtraPackets + deltaRxOtherPackets);
assertInRange("uidtxb", uidTxDeltaBytes, pktBytes + minExpExtraPktBytes,
pktBytes + pktWithNoDataBytes + maxExpExtraPktBytes + deltaTxOtherPktBytes);
assertInRange("uidrxb", uidRxDeltaBytes, pktBytes + minExpExtraPktBytes,
pktBytes + pktWithNoDataBytes + maxExpExtraPktBytes + deltaRxOtherPktBytes);
assertInRange("iftxp", ifaceTxDeltaPackets, packetCount + minExpectedExtraPackets,
packetCount + packetCount + maxExpectedExtraPackets);
assertInRange("ifrxp", ifaceRxDeltaPackets, packetCount + minExpectedExtraPackets,
packetCount + packetCount + maxExpectedExtraPackets);
assertInRange("iftxb", ifaceTxDeltaBytes, pktBytes + minExpExtraPktBytes,
pktBytes + pktWithNoDataBytes + maxExpExtraPktBytes);
assertInRange("ifrxb", ifaceRxDeltaBytes, pktBytes + minExpExtraPktBytes,
pktBytes + pktWithNoDataBytes + maxExpExtraPktBytes);
// Localhost traffic *does* count against total stats.
// Check the total stats increased after test data transfer over localhost has been made.
assertTrue("ttxp: " + totalTxPacketsBefore + " -> " + totalTxPacketsAfter,
totalTxPacketsAfter >= totalTxPacketsBefore + uidTxDeltaPackets);
assertTrue("trxp: " + totalRxPacketsBefore + " -> " + totalRxPacketsAfter,
totalRxPacketsAfter >= totalRxPacketsBefore + uidRxDeltaPackets);
assertTrue("ttxb: " + totalTxBytesBefore + " -> " + totalTxBytesAfter,
totalTxBytesAfter >= totalTxBytesBefore + uidTxDeltaBytes);
assertTrue("trxb: " + totalRxBytesBefore + " -> " + totalRxBytesAfter,
totalRxBytesAfter >= totalRxBytesBefore + uidRxDeltaBytes);
assertTrue("iftxp: " + ifaceTxPacketsBefore + " -> " + ifaceTxPacketsAfter,
totalTxPacketsAfter >= totalTxPacketsBefore + ifaceTxDeltaPackets);
assertTrue("ifrxp: " + ifaceRxPacketsBefore + " -> " + ifaceRxPacketsAfter,
totalRxPacketsAfter >= totalRxPacketsBefore + ifaceRxDeltaPackets);
assertTrue("iftxb: " + ifaceTxBytesBefore + " -> " + ifaceTxBytesAfter,
totalTxBytesAfter >= totalTxBytesBefore + ifaceTxDeltaBytes);
assertTrue("ifrxb: " + ifaceRxBytesBefore + " -> " + ifaceRxBytesAfter,
totalRxBytesAfter >= totalRxBytesBefore + ifaceRxDeltaBytes);
// Localhost traffic should *not* count against mobile stats,
// There might be some other traffic, but nowhere near 1MB.
assertInRange("mtxp", mobileTxPacketsAfter, mobileTxPacketsBefore,
mobileTxPacketsBefore + 500);
assertInRange("mrxp", mobileRxPacketsAfter, mobileRxPacketsBefore,
mobileRxPacketsBefore + 500);
assertInRange("mtxb", mobileTxBytesAfter, mobileTxBytesBefore,
mobileTxBytesBefore + 200000);
assertInRange("mrxb", mobileRxBytesAfter, mobileRxBytesBefore,
mobileRxBytesBefore + 200000);
}
}