20

As the title says. I wish to know how many bytes per second a specific app use at specific time.

Maybe I can use "netstat" command? But if so, how can I filter it to a specific app/process ?

Do I also need to have some permission to do it?

Currently people say to use TrafficStats.getUidRxBytes(packageInfo.uid) , but, from here: https://developer.android.com/reference/android/net/TrafficStats.html#getUidRxBytes(int) , it says it's not supported from N , and that I should use NetworkStatsManager instead. Is there any example to use it?

Maybe a merged solution?

EDIT: I tried to use NetworkStatsManager on Android N, but I failed. I can't find any sample of how to use it, and all stackOverflow questions about it were similar in term of not being able to use it well. Please, if anyone knows how to use it, write about it.

android developer
  • 114,585
  • 152
  • 739
  • 1,270
  • 2
    http://stackoverflow.com/questions/11939939/how-can-i-find-the-data-usage-on-a-per-application-basis-on-android – Riad Sep 04 '16 at 07:03
  • @Riad Thanks. So you say I should use TrafficStats.getUidRxBytes(packageInfo.uid) ? From this: https://developer.android.com/reference/android/net/TrafficStats.html#getUidRxBytes(int) . If so, it says it's not supported from N , and that I should use NetworkStatsManager instead. Any example to use it? – android developer Sep 04 '16 at 07:31

3 Answers3

30

The usage of NetworkStats is not complicated.

The device API level must be at minimum 23. Here are some steps required prior to starting the usage of NetworkStatsManager

  1. Declare the required permissions in AndroidManifest.xml:

    <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
    <uses-permission
        android:name="android.permission.PACKAGE_USAGE_STATS"
        tools:ignore="ProtectedPermissions"/>
    
  2. Ask for permission in Activity

    android.permission.PACKAGE_USAGE_STATS is not a normal permission, therefore cannot be simply requested. In order to check, whether the permission has been granted, check:

    AppOpsManager appOps = (AppOpsManager) getSystemService(Context.APP_OPS_SERVICE);
    int mode = appOps.checkOpNoThrow(AppOpsManager.OPSTR_GET_USAGE_STATS,
            android.os.Process.myUid(), getPackageName());
    if (mode == AppOpsManager.MODE_ALLOWED) {
        return true;
    }
    

    To ask for this permission, simply call Intent:

    Intent intent = new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS);
    startActivity(intent);
    

    Another permmission is also needed: Manifest.permission.READ_PHONE_STATE. However, this is normal permission so can be requested as any other permission

  3. Use NetworkStatsManager:

    To obtain it's reference, call:

    NetworkStatsManager networkStatsManager = (NetworkStatsManager) getApplicationContext().getSystemService(Context.NETWORK_STATS_SERVICE);
    

    Everything that is retrieved from NetworkStatsManager is packed into Buckets. This is just a simple POJO, that holds the data.

    GLOBAL:

    To get the overall usage for WiFi:

    NetworkStats.Bucket bucket;
    try {
        bucket = networkStatsManager.querySummaryForDevice(ConnectivityManager.TYPE_WIFI,
                "",
                0,
                System.currentTimeMillis());
    } catch (RemoteException e) {
        return -1;
    }
    

    from NetworkStats.Bucket, two methods can be called to get the usage (in Bps):

    bucket.getRxBytes();
    bucket.getTxBytes();
    

    Obtaining the data for mobile network is harder. In order to obtain the Bucket call:

    public long getAllRxBytesMobile(Context context) {
        NetworkStats.Bucket bucket;
        try {
            bucket = networkStatsManager.querySummaryForDevice(ConnectivityManager.TYPE_MOBILE,
                getSubscriberId(context, ConnectivityManager.TYPE_MOBILE),
                0,
                System.currentTimeMillis());
        } catch (RemoteException e) {
            return -1;
        }
        return bucket.getRxBytes();
    }
    
    //Here Manifest.permission.READ_PHONE_STATS is needed
    private String getSubscriberId(Context context, int networkType) {
        if (ConnectivityManager.TYPE_MOBILE == networkType) {
        TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
            return tm.getSubscriberId();
        }
        return "";
    }
    

    APPLICATION:

    For obtaining the data for specific application, read the documentation for queryDetailsForUID.

    To get the package usage for WiFi:

    NetworkStats networkStats = null;
    try {
        networkStats = networkStatsManager.queryDetailsForUid(
                ConnectivityManager.TYPE_WIFI,
                "",
                0,
                System.currentTimeMillis(),
                packageUid);
    } catch (RemoteException e) {
        return -1;
    }
    NetworkStats.Bucket bucket = new NetworkStats.Bucket();
    networkStats.getNextBucket(bucket);
    

    To get the package usage for Mobile:

    NetworkStats networkStats = null;
    try {
        networkStats = networkStatsManager.queryDetailsForUid(
                ConnectivityManager.TYPE_MOBILE,
                getSubscriberId(context, ConnectivityManager.TYPE_MOBILE),
                0,
                System.currentTimeMillis(),
                packageUid);
    } catch (RemoteException e) {
        return -1;
    }
    NetworkStats.Bucket bucket = new NetworkStats.Bucket();
    networkStats.getNextBucket(bucket);
    

    Unfortunately, according to this piece of code getting statistics is only possible for ConnectivityManager.TYPE_MOBILE and ConnectivityManager.TYPE_WIFI.

Made a sample Github repo demostrating the usage.

Mateen Ulhaq
  • 24,552
  • 19
  • 101
  • 135
R. Zagórski
  • 20,020
  • 5
  • 65
  • 90
  • What if I want to get network usage from all types of networks? Both wifi AND mobile? It's in a flag, no? Also, I think you have a mistake: it's not "Bps", it's just bytes. You got the number of bytes since its beginning... – android developer Sep 10 '16 at 00:24
  • Also, the code you wrote is not about an app, but in global. What I asked is the network usage of an app. – android developer Sep 10 '16 at 21:04
  • I tried to get of "Etherent", but each time I did it, it complained something like "java.util.concurrent.ExecutionException: java.lang.IllegalArgumentException: Cannot create template for network type 9, subscriberId '425010...'." – android developer Sep 10 '16 at 22:41
  • 1
    To get Wifi and Mobile just sum the data from both sources. Updated the answer to show how to obtain data for one application and why it is not possible for `Ethernet`. – R. Zagórski Sep 12 '16 at 07:19
  • About the other types, I see. But why do those constants exist, if I can't use them? – android developer Sep 13 '16 at 06:45
  • In Android N, if you're querying results for your own app (not using one of the methods that require a UID), you no longer need to request the `PACKAGE_USAGE_STATS` permission – Cigogne Eveillée Nov 05 '16 at 00:30
  • app usage based on uid is returning a constant value every time. This is not with every device but in some devices. Samsung A5, Nexus 5 etc is not working properly. – rajeesh Jan 14 '17 at 05:53
  • getting always 0 for application when using querySummaryForDevice – aj0822ArpitJoshi Mar 31 '17 at 13:19
  • @rajeesh Correct. How can I handle this well? – android developer May 14 '17 at 22:50
  • @Zagórski, why startTime parameter is always '0' in your example? – Satish Shetty Jan 22 '19 at 22:30
  • For new android versions >=Android10, security exception comes on getSubscriberId – shubham chouhan Jun 17 '22 at 12:31
10

There is also a complicated way to get statistics, but this should work for all adapters: (but it is expensive, because you have to read files continously),

  1. Here you can see all your adapters and the sent/received data:
cat /proc/net/dev
  1. You can get statistics per adapter from these files:
cat /sys/class/net/**wlan0**/statistics/**rx**_bytes

wlan0 could be all of your adapters, but you need to maintain the value in your app, because if you switch off/on the adapter, the value will be 0 again. (You can have RX and TX values)

  1. You can get the active, currently sending/receiving app list
cat /proc/net/**tcp6**

This also can be done with tcp, tcp6, upd, upd6

Here you have the "uid" column, which is the UID of your installed apps. (So that app is using the network right now)

From this file you can also see the connection endpoint.

  1. Check sent/received bytes per application (like the TrafficStats API calls)
cat /proc/uid_stat/**10348**/tcp_snd

The id here is the application UID. You have the sent: tcp_snd and the received: tcp_rcv files too.

  1. You can also do some process and UID matching with:
cat /proc/**17621**/status

The id here is the process id what you can get from e.g. the top command. In the status file you will have the UID, which is the installed/system app UID.

+1. You can take a look also on this file, TrafficStats API uses this:

cat /proc/net/xt_qtaguid/stats

Headers:

idx iface acct_tag_hex uid_tag_int cnt_set rx_bytes rx_packets tx_bytes tx_packets rx_tcp_bytes rx_tcp_packets rx_udp_bytes rx_udp_packets rx_other_bytes rx_other_packets tx_tcp_bytes tx_tcp_packets tx_udp_bytes tx_udp_packets tx_other_bytes tx_other_packets

Values:

6 eth0 0x0 10005 0 0 0 80 2 0 0 0 0 0 0 80 2 0 0 0 0
7 eth0 0x0 10005 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
8 eth0 0x0 10007 0 11133 25 4751 24 11133 25 0 0 0 0 4751 24 0 0 0 0
9 eth0 0x0 10007 1 5965514 4358 102028 2327 5965514 4358 0 0 0 0 102028 2327 0 0 0 0
10 eth0 0x0 10014 0 95 1 40 1 95 1 0 0 0 0 40 1 0 0 0 0

So maybe mixing the first answer with this knowledge, you can have more and better statistics.

danesz
  • 495
  • 2
  • 9
  • Nice. Thank you. Does it require any permission? Have you tried it? – android developer Sep 13 '16 at 06:48
  • 1
    I used this solution in the past (in Android 2.1 - 2.3 - 4.4 times). If I remember correctly, no specific permissions needed. But maybe on Android 6-7 something changed. – danesz Sep 13 '16 at 07:16
  • 1
    I tried it now on an Android 6 device. All the mentioned files are readable without any permission. e.g. `FileInputStream fileInputStream = new FileInputStream("/proc/19534/status");` – danesz Sep 13 '16 at 07:30
  • On Android 7 everything works except `cat /sys/class/net/wlan0/statistics/rx_bytes` (permission denied) but you can get the same value from `cat /proc/net/dev` – danesz Sep 13 '16 at 08:35
  • About Android 7 (and what happened on 5 BTW? ) , what's the permission you need? Also, does "/proc/**17621**/status" work for specific processes? – android developer Sep 13 '16 at 13:17
  • 1
    on Android 7 the issue is similar to this: https://bugs.chromium.org/p/chromium/issues/detail?id=586021 the `/proc/**17621**/status` calls are working. I have not tried on Android 5 (but if it works from 2.1 to 6.0, I don't think the 5.0 has issues...) try it ;) – danesz Sep 13 '16 at 13:34
  • Interesting. Nice. Thank you. If you know how to parse it well, please post about it too. You get +1 for the effort. – android developer Sep 13 '16 at 22:46
  • 1
    which file do you want to parse? – danesz Sep 14 '16 at 08:44
  • The one that shows network usage stats about specific app, like the usage of the normal API. – android developer Sep 14 '16 at 08:51
  • 1
    I added a new 4. paragraph to the comment. You have to read the file, it contains only one number. `number = Long.parseLong(file.readLine());` – danesz Sep 14 '16 at 09:15
  • so this works even on Android 7 ? Is it only of tcp? Shouldn't I parse all files there, if that's how it works? Also, seeing you wrote "file.readLine()", I understand that I can use File API ,instead of "cat" command, right? – android developer Sep 14 '16 at 09:47
  • 1
    Yes, it works on 7.0 and yes, you can use File API. (check my 3. comment). It is only tcp, but the TrafficStats, NetworksStats give you back the TCP+UDP (and also the `/proc/net/dev` file). So just need to do a subtraction to get UDP. – danesz Sep 14 '16 at 11:59
  • I mean how can I get both together, by parsing these files alone. Is it possible to get both TCP&UDP stats of a single app using the File API (without using TrafficStats or NetworksStats) that you've shown? – android developer Sep 14 '16 at 14:33
  • 1
    From the moment your app is installed, you can capture and count all the traffic. The usage history I think is available only via API. I haven't found any files yet for those.... Check also the +1 paragraph. – danesz Sep 14 '16 at 19:45
  • All this time I was talking about network usage. I don't understand what you mean. – android developer Sep 14 '16 at 22:46
  • 1
    There is no way without API to e.g. getAllSentBytesForApplication(appid) from a certain period. The system also counts the sent/received bytes, ofc from the beginnings (== really first start of the device), then gives you back via API. When your statistics app is installed, you have to monitor the mentioned files, save the values, summarise them, etc... because after a device restart the files are cleared. You have to do the work. – danesz Sep 15 '16 at 07:32
  • I think I see what you mean now. What about a delta of it? For example, Suppose I get the number on time T1, and a second later, on T2 ? Will this work fine? Will it get the network usage (all types, of a single app, for both sent&received bytes) for this amount of time? – android developer Sep 15 '16 at 08:26
  • 1
    Yes, it will. Everything is depend on your needs, how/how often/when you store the values, but basically you can do what you want. – danesz Sep 15 '16 at 08:46
  • Nice. Thank you. I will give you +1 for the effort and the huge help and dedication, but can't accept because normal API is always better. – android developer Sep 15 '16 at 14:25
  • I've noticed the original answer doesn't always work. I tried using this one, but "top" only returns the current process. Which of the above would be able to at least get a global stats of the network usage? And how do I parse it? – android developer May 14 '17 at 22:54
2

Adding my simplified function to get overall network usage via /proc/net/dev.

No unique permission is needed.

public static long[] getNetworkUsageKb() {
    BufferedReader reader;
    String line;
    String[] values;
    long[] totalBytes = new long[2];//rx,tx
    try {
        reader = new BufferedReader(new FileReader("/proc/net/dev"));

        while ((line = reader.readLine()) != null) {
            if (line.contains("eth") || line.contains("wlan")){
                values = line.trim().split("\\s+");
                totalBytes[0] +=Long.parseLong(values[1]);//rx
                totalBytes[1] +=Long.parseLong(values[9]);//tx
            }
        }
        reader.close();
    }
    catch (FileNotFoundException e) {
        e.printStackTrace();
    }
    catch (IOException e) {
        e.printStackTrace();
    }

    //transfer to kb
    totalBytes[0] =  totalBytes[0] / 1024;
    totalBytes[1] =  totalBytes[1] / 1024;

    return totalBytes;
}
Arkady
  • 624
  • 1
  • 8
  • 17
  • What exactly do you get from it? The total bytes for download and upload from some time? The current download/upload speed ? Is it possible for specific apps too? – android developer Feb 14 '19 at 11:49
  • 1
    Actually, I've used the danesz suggestion, and it looks like this is the total number since boot. You can dig here fore some explanation https://stackoverflow.com/questions/3521678/what-are-meanings-of-fields-in-proc-net-dev. – Arkady Feb 14 '19 at 14:45
  • I see. But is it possible to get the total so far (or from specific time), and also of specific app? – android developer Feb 14 '19 at 19:49
  • I see. Thank you. – android developer Feb 19 '19 at 15:03