android statistics application traffic NetworkStatsManager

1. Application statistical effect
2. Introduce the API NetworkStatsManager launched after Android official 6.0:
3. Statistical query method (UID, single application, all applications)
4. Pits found in the statistical process and Solutions
5. Comparison between statistical results and system statistics

1. Application statistical effect

No more nonsense, serve first:

2. Introduce the API NetworkStatsManager launched after Android official 6.0:

Let's take a look at the official NetworkStatsManager API document of android:

Android Developer NetworkStatsManager

The NetworkStatsManager class provides access to network usage history and statistics,
It is divided into Summary queries and History queries

Summary queries
These queries summarize network usage over the entire time interval. Therefore, there is only one bucket for a specific combination of key, status, metering and roaming. If it is a summary of user range and device range, a bucket containing the total network usage is returned.

History queries
These queries do not aggregate over time, but aggregate status, metering, and roaming. Therefore, a specific key can have multiple buckets.

Query method:

//Query network usage statistics details. The result is filtered to include only the uid belonging to the calling user (including the uid of each application on the mobile phone, and the total traffic of all applications is available)
queryDetails(int networkType, String subscriberId, long startTime, long endTime)

Query the network usage statistics details of the specified uid. (single application traffic can be queried)
queryDetailsForUid(int networkType, String subscriberId, long startTime, long endTime, int uid)

Query the network usage statistics details of the specified uid and tag.
queryDetailsForUidTag(int networkType, String subscriberId, long startTime, long endTime, int uid, int tag)

Query given uid,Network usage statistics details for tags and status. Only available for those belonging to the calling user uid. The results do not aggregate over time.

queryDetailsForUidTagState(int networkType, String subscriberId, long startTime, long endTime, int uid, int tag, int state)

Query the statistical summary of network usage. (query traffic statistics of multiple applications)
querySummary(int networkType, String subscriberId, long startTime, long endTime)

Query the statistical summary of network usage. The result is a summary of data usage for the entire device. The result is a single bucket aggregated over time, status, uid, tags, metering, and roaming
querySummaryForDevice(int networkType, String subscriberId, long startTime, long endTime)

Query the statistical summary of network usage. The result is the aggregate data usage of all UIDs belonging to the calling user. The result is a single Bucket that aggregates over time, state, and uid.
querySummaryForUser(int networkType, String subscriberId, long startTime, long endTime)

3. Statistical query method (UID, single application, all applications)

a. Demand: Statistics of single application traffic
You can select queryDetailsForUid (int networkType, String subscriberId, long startTime, long endTime, int uid) from the above query methods to meet the requirements. In addition to this method, querySummary() and queryDetails() can also meet the requirements.

queryDetailsForUid(int networkType, String subscriberId, long startTime, long endTime, int uid)

Parameter Description:

  • 1.networkType query network type (ConnectivityManager.TYPE_WIFI, ConnectivityManager.TYPE_MOBILE)
    2. Subscriber id is the unique id of the device (it can't be obtained by android 10 and later devices and can't be transmitted)
    3.startTime query the start timestamp of the specified time period
    4.endTime queries the end time of a specified time period
    5.uid query the uid of the device

**The query method is as follows:**

queryDetailsForUid method statistics

	//Statistical tools
    public static class Usage {
        long totalRxBytes;//Total data download bytes
        long totalTxBytes;//Total data upload bytes
        long wifiTotalData;//wifi total traffic data
        long mobleTotalData;//Mobile total traffic data
        long mobleRxBytes;//Mobile download bytes
        long mobleTxBytes;//Mobile upload byte
        long wifiRxBytes;//wifi download bytes
        long wifiTxBytes;//wifi upload bytes
        String uid;//Package name
    }








 	/**
   * Get uid (single) application traffic
     *
     * @param context   context
     * @param startTime start time
     * @param endTime   End time
     * @param uid       Application uid
     * @return
     */
    public static Usage getUsageBytesByUid(Context context, long startTime, long endTime, int uid) {
        Usage usage = new Usage();
        usage.totalRxBytes = 0;
        usage.totalTxBytes = 0;
        usage.mobleRxBytes = 0;
        usage.mobleTxBytes = 0;
        usage.wifiTxBytes = 0;
        usage.wifiTxBytes = 0;
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
            NetworkStatsManager nsm = (NetworkStatsManager) context.getSystemService(NETWORK_STATS_SERVICE);
            assert nsm != null;
            NetworkStats wifi = nsm.queryDetailsForUid(ConnectivityManager.TYPE_WIFI, null, startTime, endTime, uid);
            NetworkStats moble = nsm.queryDetailsForUid(ConnectivityManager.TYPE_MOBILE, null, startTime, endTime, uid);
            do {
                NetworkStats.Bucket bucket = new NetworkStats.Bucket();
                wifi.getNextBucket(bucket);
                usage.wifiRxBytes += bucket.getRxBytes();
                usage.wifiTxBytes += bucket.getTxBytes();
            } while (wifi.hasNextBucket());
            do {
                NetworkStats.Bucket bucket = new NetworkStats.Bucket();
                moble.getNextBucket(bucket);
                usage.mobleRxBytes += bucket.getRxBytes();
                usage.mobleTxBytes += bucket.getTxBytes();
            } while (moble.hasNextBucket());
            usage.wifiTotalData = usage.wifiRxBytes + usage.wifiTxBytes;
            usage.mobleTotalData = usage.mobleRxBytes + usage.mobleTxBytes;
        }
        return usage;
    }

querySummary method query statistics

    /**
     * Get single application traffic
     * @param context    context
     * @param startTime  start time
     * @param endTime    End time
     * @param uid        Statistical application uid
     * @return
     */
    public static Usage getApplicationQuerySummary(Context context, long startTime, long endTime,int uid) {
        usage = new Usage();
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
            NetworkStatsManager nsm = (NetworkStatsManager) context.getSystemService(NETWORK_STATS_SERVICE);
            assert nsm != null;
            try {
                NetworkStats wifi = nsm.querySummary(ConnectivityManager.TYPE_WIFI, null, startTime, endTime);
                NetworkStats mobile = nsm.querySummary(ConnectivityManager.TYPE_MOBILE, null, startTime, endTime);
                do {
                    NetworkStats.Bucket bucket = new NetworkStats.Bucket();
                    wifi.getNextBucket(bucket);
                    if(bucket.getUid() == uid){
                        usage.wifiRxBytes += bucket.getRxBytes();
                        usage.wifiTxBytes += bucket.getTxBytes();
                    }
                } while (wifi.hasNextBucket());

                do {
                    NetworkStats.Bucket bucket = new NetworkStats.Bucket();
                    mobile.getNextBucket(bucket);
                    if(bucket.getUid() == uid) {
                        usage.mobleRxBytes += bucket.getRxBytes();
                        usage.mobleTxBytes += bucket.getTxBytes();
                    }
                } while (mobile.hasNextBucket());
                usage.wifiTotalData = usage.wifiRxBytes + usage.wifiTxBytes;
                usage.mobleTotalData = usage.mobleRxBytes + usage.mobleTxBytes;
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
        return usage;
    }

queryDetails method statistics

 /**
     * Get single application traffic
     * @param context
     * @param startTime
     * @param endTime
     * @param uid
     * @return
     */
    public static Usage getApplicationQueryDetails(Context context, long startTime, long endTime,int uid) {
        usage = new Usage();
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
            NetworkStatsManager nsm = (NetworkStatsManager) context.getSystemService(NETWORK_STATS_SERVICE);
            assert nsm != null;
            try {
                NetworkStats wifi = nsm.queryDetails(ConnectivityManager.TYPE_WIFI, null, startTime, endTime);
                NetworkStats mobile = nsm.queryDetails(ConnectivityManager.TYPE_MOBILE, null, startTime, endTime);
                do {
                    NetworkStats.Bucket bucket = new NetworkStats.Bucket();
                    wifi.getNextBucket(bucket);
                    if(bucket.getUid() == uid) {
                        usage.wifiRxBytes += bucket.getRxBytes();
                        usage.wifiTxBytes += bucket.getTxBytes();
                    }
                } while (wifi.hasNextBucket());
                do {
                    NetworkStats.Bucket bucket = new NetworkStats.Bucket();
                    mobile.getNextBucket(bucket);
                    if(bucket.getUid() == uid) {
                        usage.mobleRxBytes += bucket.getRxBytes();
                        usage.mobleTxBytes += bucket.getTxBytes();
                    }
                } while (mobile.hasNextBucket());
                usage.wifiTotalData = usage.wifiRxBytes + usage.wifiTxBytes;
                usage.mobleTotalData = usage.mobleRxBytes + usage.mobleTxBytes;
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
        return usage;
    }

b. Statistics of mobile phone traffic (all applications)

From the above query method, we can see that the querySummaryForDevice (int networkType, String subscriberId, long startTime, long endTime, int uid) method is the most suitable for the requirements. In addition to this method, querySummary(), queryDetails() and querySummaryForUser() can also meet the requirements.

querySummaryForDevice(int networkType, String subscriberId, long startTime, long endTime)

Parameter Description:

  • 1.networkType query network type (ConnectivityManager.TYPE_WIFI, ConnectivityManager.TYPE_MOBILE)
    2. Subscriber id is the unique id of the device (it can't be obtained by android 10 and later devices and can't be transmitted)
    3.startTime query the start timestamp of the specified time period
    4.endTime queries the end time of a specified time period

querySummaryForUser method statistics

    /**
     * Get all app traffic
     *
     * @param context
     * @param startTime start time
     * @param endTime   End time
     * @return
     */
    public static Usage getUsageByUidFromSummaryTotal(Context context, long startTime, long endTime) {
        usage = new Usage();
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
            NetworkStatsManager nsm = (NetworkStatsManager) context.getSystemService(NETWORK_STATS_SERVICE);
            assert nsm != null;
            try{
                //querySummaryForUser summarizes the data usage of the entire device.
                NetworkStats.Bucket mobileBucket = nsm.querySummaryForUser(ConnectivityManager.TYPE_MOBILE, null, startTime, endTime);
                usage.mobleTotalData = mobileBucket.getTxBytes() + mobileBucket.getRxBytes();
                usage.mobleTxBytes = mobileBucket.getTxBytes();
                usage.mobleRxBytes = mobileBucket.getRxBytes();

                NetworkStats.Bucket wifiBucket = nsm.querySummaryForUser(ConnectivityManager.TYPE_WIFI, "", startTime, endTime);
                usage.wifiTotalData = wifiBucket.getTxBytes() + wifiBucket.getRxBytes();
                usage.wifiTxBytes = wifiBucket.getTxBytes();
                usage.wifiRxBytes = wifiBucket.getRxBytes();
            }catch (Exception e){
                e.printStackTrace();
            }
        }
        return usage;
    }

Not every method is expanded here. querySummary, queryDetails and querySummaryForUser can also realize total traffic statistics. For specific methods, please refer to single traffic statistics.

4. Pits found in the statistical process and Solutions

BUG1: querySummaryForUser(int networkType, String subscriberId, long startTime, long endTime) queries the total traffic in two times:

When calling this method, the following parameters are passed in:
First parameter: mobile type / wifi type
Second parameter: empty string
The third parameter: today's 0:00 timestamp, such as 2022-3-10:00:00:00 (millisecond value)
The fourth parameter: get the current system timestamp system currentTimeMillis()

This method specifies to query the total traffic usage in a certain period of time. The result is that wifi is normal, but mobile traffic is 0. I feel very puzzled.

resolvent:

Train of thought 1: method problem: first, replace the parameter unchanged with other methods queryDetails and querySummary query, and the result is still 0

Idea 2: parameter problem. According to the previous results, since there is no problem with wifi query, exclude time parameters 3 and 4. For the remaining types and subscriberId (device id), continue to check whether there are other types and types in the connectivity manager_ Mobile type parameter description, which is also excluded.
subscriberId (device id) can't be obtained after android 10. Other third-party applications can count traffic. There's no reason not to. No discount, check the source code first

Find 525 lines in the createTemplate method in the source code, judge whether the subscriberId is null, and then the parameter string passed in above is null, which is not to query the buildTemplateMobileAll method. Finally, the subscriber ID passes in the null value query, and the result data is..

The BUG2: requirement is to query how much traffic is used today, and to call queryDetailsForUid (int networkType, String subscriberId, long startTime, long endTim, int int) two times (mobile and tiktok).

When calling this method, the following parameters are passed in:
The first parameter: wifi and mobile type respectively
Second parameter: null
The third parameter: today's 0:00 timestamp, such as 2022-3-10:00:00:00 (millisecond value)
The fourth parameter: get the current system timestamp system currentTimeMillis()
The fifth parameter: TikTok uid.

The result is 0, and I thought about it. It seems that I have not opened the tiktok today. Then tiktok wifi and flow switch were brushed for a while, and then the query results were all 0.

resolvent

Idea 1: instead, use querySummary and queryDetails to query. It is found that querySummary can query real-time traffic, and queryDetails still has a query result of 0.
Idea 2: change the time. Is it because of the length of time? Just change the time to yesterday.
In this attempt, the end time is extended to the next day. For example, today, March 10, the fourth parameter is replaced with 2022-3-11:00:00:00, and the results can be tested normally.

Summary:

At the beginning of bug1, I didn't expect that the subscriberId is the basic type of String, and the default value is generally an empty String. Who would have thought that it needs to be passed as null. Therefore, we should look at the source code more and don't suffer losses

The problem of bug 2 is rarely used. Just try it a few more times or read the information.
queryDetails and queryDetailsForUid methods belong to historical queries. The statistical unit of this method is counted by hour. You can test it yourself.
querySummary is a summary query, that is, the traffic within a specified time period (instant query). It's not very friendly to translate English into Chinese

5. Comparison between statistical results and system statistics

1. The tested mobile phones include Xiaomi (Android 12), vivo (Android 11) and Meizu
The original statistics of Xiaomi system are basically the same, with a difference of no more than 1M
2. Statistical methods
The statistical results of the two querySummary series and queryDetails series differ by tens of megabytes

Here are some blog addresses for reference:

https://blog.csdn.net/w7849516230/article/details/71705835
https://www.jianshu.com/p/52192441089b/

https://developer.android.com/reference/android/app/usage/NetworkStatsManager#queryDetails(int,%20java.lang.String,%20long,%20long)

Demo download address

Keywords: Android

Added by ghost007 on Thu, 10 Mar 2022 13:03:59 +0200