Как узнать текущее использование приложения в сети (или всего) даже на Android N?

Как видно из названия. Я хочу знать, сколько байтов в секунду конкретное приложение использует в определенное время.

Может быть, я могу использовать команду " netstat"? Но если так, как я могу отфильтровать его для конкретного приложения / процесса?

Нужно ли мне также иметь разрешение на это?

В настоящее время люди говорят, что используют TrafficStats.getUidRxBytes(packageInfo.uid), но отсюда: https://developer.android.com/reference/android/net/TrafficStats.html, он говорит, что он не поддерживается N, и что я должен использовать NetworkStatsManager вместо этого. Есть ли пример, чтобы использовать его?

Может быть, объединенное решение?

РЕДАКТИРОВАТЬ: я пытался использовать NetworkStatsManager на Android N, но мне не удалось. Я не могу найти ни одного примера того, как его использовать, и все вопросы об этом в стеке были схожи с точки зрения того, что он не может его использовать. Пожалуйста, если кто-нибудь знает, как им пользоваться, напишите об этом.

2 ответа

Решение

Использование NetworkStats не сложно.

Уровень API устройства должен быть как минимум 23. Вот несколько шагов, необходимых для начала использования NetworkStatsManager

  1. Объявите необходимые разрешения в AndroidManifest.xml:

    <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
    <uses-permission
        android:name="android.permission.PACKAGE_USAGE_STATS"
        tools:ignore="ProtectedPermissions"/>
    
  2. Спросите разрешения в Activity

    android.permission.PACKAGE_USAGE_STATS не является нормальным разрешением, поэтому не может быть просто запрошено. Чтобы проверить, было ли предоставлено разрешение, проверьте:

    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;
    }
    

    Чтобы запросить это разрешение, просто позвоните Intent:

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

    Также необходимо другое разрешение: Manifest.permission.READ_PHONE_STATE, Однако это нормальное разрешение, поэтому его можно запросить как любое другое разрешение.

  3. использование NetworkStatsManager:

    Чтобы получить его, позвоните по телефону:

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

    Все, что извлекается из NetworkStatsManager упакован в Buckets, Это просто простой POJO, который содержит данные.

    ГЛОБАЛЬНЫЙ:

    Чтобы получить общее использование WiFi:

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

    от NetworkStats.Bucket для получения использования можно вызвать два метода (в бит / с):

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

    Получить данные для мобильной сети сложнее. Для того чтобы получить Bucket вызов:

    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 "";
    }
    

    ПРИЛОЖЕНИЕ:

    Для получения данных для конкретного приложения прочитайте документацию для queryDetailsForUID.

    Чтобы узнать, как использовать пакет для 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);
    

    Чтобы получить пакет использования для мобильных устройств:

    Unfortunalety 
    
    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);
    

    К сожалению, в соответствии с этим фрагментом кода получение статистики возможно только для ConnectivityManager.TYPE_MOBILE а также ConnectivityManager.TYPE_WIFI,

Сделал пример репозитория Github, демонстрирующий использование.

Существует также сложный способ получения статистики, но он должен работать для всех адаптеров: (но это дорого, потому что вы должны постоянно читать файлы),

  1. Здесь вы можете увидеть все ваши адаптеры и отправленные / полученные данные:
cat /proc/net/dev
  1. Вы можете получить статистику по адаптеру из этих файлов:
cat /sys/class/net/**wlan0**/statistics/**rx**_bytes

wlan0 может быть всеми вашими адаптерами, но вам нужно сохранить значение в вашем приложении, потому что если вы выключите / включите адаптер, значение снова будет равно 0. (Вы можете иметь значения RX и TX)

  1. Вы можете получить активный, в данный момент отправляющий / получающий список приложений
cat /proc/net/**tcp6**

Это также можно сделать с помощью tcp, tcp6, upd, upd6

Здесь у вас есть столбец "uid", который является UID ваших установленных приложений. (Так что это приложение использует сеть прямо сейчас)

Из этого файла вы также можете увидеть конечную точку подключения.

  1. Проверять отправленные / полученные байты для каждого приложения (например, вызовы API TrafficStats)
cat /proc/uid_stat/**10348**/tcp_snd

Идентификатор здесь - это UID приложения. Вы также отправили: tcp_snd и полученные: tcp_rcv файлы.

  1. Вы также можете выполнить некоторые процессы и сопоставить UID с:
cat /proc/**17621**/status

Идентификатор здесь - это идентификатор процесса, который вы можете получить, например, из верхней команды. В файле состояния у вас будет UID, который является UID установленного / системного приложения.

+1. Вы также можете посмотреть на этот файл, API-интерфейс TrafficStats использует это:

cat /proc/net/xt_qtaguid/stats

Заголовки:

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

Ценности:

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

Так что, возможно, смешивая первый ответ с этими знаниями, вы можете получить больше и лучшую статистику.

Добавление моей упрощенной функции, чтобы получить общее использование сети через /proc/net/dev.

Нет уникального разрешения не требуется.

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;
}
Другие вопросы по тегам