【Android】(七) TrafficStatistics API

在这里插入图片描述

1 统计总计流量

/**
 * 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);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值