Android 流量统计

Traffic monitoring

Posted by LXG on March 29, 2024

eBPF 流量监控-AOSP

Android是如何实现流量统计的?

Linux eBPF 技术

Linux中的eBPF(Extended Berkeley Packet Filter)是一种革命性的内核技术,它就像一个安全且高效的“小程序”引擎,允许开发者在不直接修改内核代码的情况下,在内核空间运行自己编写的程序。这些小程序可以在很多关键路径上运行,比如网络数据包处理、系统调用跟踪、性能分析,甚至是文件系统和安全策略执行等场景

调试命令

adb shell dumpsys netd trafficcontroller


lxg@lixiaogang:~$ adb shell dumpsys netd trafficcontroller

  TrafficController
    BPF module status: BPF_LEVEL_BASIC

    mCookieTagMap status: OK
    mUidCounterSetMap status: OK
    mAppUidStatsMap status: OK
    mStatsMapA status: OK
    mStatsMapB status: OK
    mIfaceIndexNameMap status: OK
    mIfaceStatsMap status: OK
    mConfigurationMap status: OK
    mUidOwnerMap status: OK

    Cgroup ingress program status: OK
    Cgroup egress program status: OK
    xt_bpf ingress program status: OK
    xt_bpf egress program status: OK
    xt_bpf bandwidth whitelist program status: OK
    xt_bpf bandwidth blacklist program status: OK

    BPF map content:

      mCookieTagMap:
      cookie=760 tag=0xfffffe03 uid=1000
      cookie=347 tag=0xfffffe02 uid=1000
      cookie=555 tag=0xfffffe02 uid=1000
      cookie=759 tag=0xfffffe02 uid=1000

      mUidCounterSetMap:
      1001 1
      10046 1
      10029 1
      10036 1

      mAppUidStatsMap::
      uid rxBytes rxPackets txBytes txPackets
      10046 54619 360 39385 401
      1051 1215 12 827 12
      0 0 0 14940 170
      10018 10383 14 1785 16
      1020 97099 351 57571 205
      1052 17859 139 17973 144
      1000 46792 199 28380 241

      mStatsMapA:
      ifaceIndex ifaceName tag_hex uid_int cnt_set rxBytes rxPackets txBytes txPackets
      10 usb0 0x0 10046 1 8493 47 7894 62
      10 usb0 0x0 10046 0 1993 25 2097 29
      14 wlan0 0x0 1052 0 4672 68 12775 68
      14 wlan0 0x0 1020 0 16820 64 6950 20
      14 wlan0 0xfffffe03 1000 0 656 2 676 2
      14 wlan0 0x0 0 0 0 0 368 3
      14 wlan0 0xfffffe02 1000 0 160 3 0 0
      10 usb0 0x0 1052 0 13187 71 5198 76
      10 usb0 0x0 1000 0 444 8 991 12
      10 usb0 0x0 1020 0 13872 40 6936 20
      10 usb0 0x0 1051 0 180 2 148 2
      10 usb0 0x0 0 0 0 0 1508 8
      14 wlan0 0x0 1000 0 816 5 676 2

      mStatsMapB:
      ifaceIndex ifaceName tag_hex uid_int cnt_set rxBytes rxPackets txBytes txPackets

      mIfaceIndexNameMap:
      ifaceIndex=8 ifaceName=sit0
      ifaceIndex=9 ifaceName=ip6tnl0
      ifaceIndex=6 ifaceName=ip_vti0
      ifaceIndex=7 ifaceName=ip6_vti0
      ifaceIndex=4 ifaceName=gre0
      ifaceIndex=2 ifaceName=eth0
      ifaceIndex=13 ifaceName=wlan0
      ifaceIndex=3 ifaceName=tunl0
      ifaceIndex=10 ifaceName=usb0
      ifaceIndex=5 ifaceName=gretap0
      ifaceIndex=14 ifaceName=wlan0
      ifaceIndex=1 ifaceName=lo
      ifaceIndex=12 ifaceName=wlan0

      mIfaceStatsMap::
      ifaceIndex ifaceName rxBytes rxPackets txBytes txPackets
      2 eth0 1345 7 3498 24
      13 wlan0 4432 14 10504 44
      10 usb0 476366 1759 495960 2198
      5 gretap0 0 0 384 4
      14 wlan0 358823 1204 343910 1104
      1 lo 4076 33 4076 33
      12 wlan0 8022 35 18872 106

      current ownerMatch configuration: 0
      current statsMap configuration: 0

      mUidOwnerMap:
      10006  HAPPY_BOX_MATCH

      mUidPermissionMap:
      10005 PERMISSION_NONE
      10035 PERMISSION_NONE
      1001  BPF_PERMISSION_INTERNET BPF_PERMISSION_UPDATE_DEVICE_STATS
      10003 PERMISSION_NONE
      10022 PERMISSION_NONE
      1073 PERMISSION_NONE
      10002 PERMISSION_NONE
      10034 PERMISSION_NONE
      10006  BPF_PERMISSION_INTERNET BPF_PERMISSION_UPDATE_DEVICE_STATS
      1002  BPF_PERMISSION_INTERNET BPF_PERMISSION_UPDATE_DEVICE_STATS
      10039 PERMISSION_NONE
      1003 PERMISSION_NONE
      10007 PERMISSION_NONE
      10045 PERMISSION_NONE
      10025 PERMISSION_NONE
      10014 PERMISSION_NONE
      10032 PERMISSION_NONE
      10020 PERMISSION_NONE
      10012 PERMISSION_NONE
      10024 PERMISSION_NONE
      10041 PERMISSION_NONE
      1041  BPF_PERMISSION_UPDATE_DEVICE_STATS
      10017 PERMISSION_NONE
      10033 PERMISSION_NONE
      1013  BPF_PERMISSION_INTERNET BPF_PERMISSION_UPDATE_DEVICE_STATS
      10010 PERMISSION_NONE
      10027 PERMISSION_NONE
      10011 PERMISSION_NONE
      10004 PERMISSION_NONE
      10037 PERMISSION_NONE
      10000 PERMISSION_NONE
      1047  BPF_PERMISSION_UPDATE_DEVICE_STATS
      1067 PERMISSION_NONE
      10038 PERMISSION_NONE
      10043 PERMISSION_NONE
      1066 PERMISSION_NONE
      10013 PERMISSION_NONE
      10036 PERMISSION_NONE
      1063 PERMISSION_NONE
      10019 PERMISSION_NONE
      10023 PERMISSION_NONE
      10001 PERMISSION_NONE

      mPrivilegedUser:
      1001 ALLOW_UPDATE_DEVICE_STATS
      1002 ALLOW_UPDATE_DEVICE_STATS
      1013 ALLOW_UPDATE_DEVICE_STATS
      1041 ALLOW_UPDATE_DEVICE_STATS
      1047 ALLOW_UPDATE_DEVICE_STATS
      10006 ALLOW_UPDATE_DEVICE_STATS


adb shell dumpsys netstats –full –uid


adb shell dumpsys netstats --full --uid

Active interfaces:
  iface=usb0 ident=[{type=MOBILE, subType=COMBINED, subscriberId=460086..., metered=true, defaultNetwork=true}] 
Active UID interfaces:
  iface=usb0 ident=[{type=MOBILE, subType=COMBINED, subscriberId=460086..., metered=true, defaultNetwork=true}] 
Top openSession callers (uid=count):

Dev stats:
  Pending bytes: 238053
  Complete history:
  ident=[{type=MOBILE, subType=COMBINED, subscriberId=460082..., metered=true, defaultNetwork=false}] uid=-1 set=ALL tag=0x0
    NetworkStatsHistory: bucketDuration=3600
      st=1711681200 rb=0 rp=0 tb=212 tp=3 op=0
  ident=[{type=MOBILE, subType=COMBINED, subscriberId=460082..., metered=true, defaultNetwork=true}] uid=-1 set=ALL tag=0x0
    NetworkStatsHistory: bucketDuration=3600
      st=1711681200 rb=59748425 rp=43312 tb=786425 tp=17976 op=0
  ident=[{type=MOBILE, subType=COMBINED, subscriberId=460086..., metered=true, defaultNetwork=true}] uid=-1 set=ALL tag=0x0
    NetworkStatsHistory: bucketDuration=3600
      st=1712026800 rb=125126 rp=587 tb=112927 tp=845 op=0
Xt stats:
  Pending bytes: 238053
  Complete history:
  ident=[{type=MOBILE, subType=COMBINED, subscriberId=460082..., metered=true, defaultNetwork=false}] uid=-1 set=ALL tag=0x0
    NetworkStatsHistory: bucketDuration=3600
      st=1711681200 rb=0 rp=0 tb=328 tp=5 op=0
  ident=[{type=MOBILE, subType=COMBINED, subscriberId=460082..., metered=true, defaultNetwork=true}] uid=-1 set=ALL tag=0x0
    NetworkStatsHistory: bucketDuration=3600
      st=1711681200 rb=59748425 rp=43312 tb=786425 tp=17976 op=0
  ident=[{type=MOBILE, subType=COMBINED, subscriberId=460086..., metered=true, defaultNetwork=true}] uid=-1 set=ALL tag=0x0
    NetworkStatsHistory: bucketDuration=3600
      st=1712026800 rb=125126 rp=587 tb=112927 tp=845 op=0
UID stats:
  Pending bytes: 221756
  Complete history:
  ident=[{type=MOBILE, subType=COMBINED, subscriberId=460082..., metered=true, defaultNetwork=false}] uid=0 set=DEFAULT tag=0x0
    NetworkStatsHistory: bucketDuration=7200
      st=1711677600 rb=0 rp=0 tb=376 tp=5 op=0
  ident=[{type=MOBILE, subType=COMBINED, subscriberId=460082..., metered=true, defaultNetwork=true}] uid=0 set=DEFAULT tag=0x0
    NetworkStatsHistory: bucketDuration=7200
      st=1711677600 rb=0 rp=0 tb=2453 tp=39 op=0
  ident=[{type=MOBILE, subType=COMBINED, subscriberId=460082..., metered=true, defaultNetwork=true}] uid=1000 set=DEFAULT tag=0x0
    NetworkStatsHistory: bucketDuration=7200
      st=1711677600 rb=59334215 rp=42639 tb=713280 tp=17139 op=0
  ident=[{type=MOBILE, subType=COMBINED, subscriberId=460082..., metered=true, defaultNetwork=true}] uid=1020 set=DEFAULT tag=0x0
    NetworkStatsHistory: bucketDuration=7200
      st=1711677600 rb=11798 rp=33 tb=7792 tp=22 op=0
  ident=[{type=MOBILE, subType=COMBINED, subscriberId=460082..., metered=true, defaultNetwork=true}] uid=1051 set=DEFAULT tag=0x0
    NetworkStatsHistory: bucketDuration=7200
      st=1711677600 rb=1692 rp=17 tb=1084 tp=17 op=0
  ident=[{type=MOBILE, subType=COMBINED, subscriberId=460082..., metered=true, defaultNetwork=true}] uid=10021 set=DEFAULT tag=0x0
    NetworkStatsHistory: bucketDuration=7200
      st=1711677600 rb=14889 rp=42 tb=12920 tp=156 op=0
  ident=[{type=MOBILE, subType=COMBINED, subscriberId=460082..., metered=true, defaultNetwork=true}] uid=10021 set=FOREGROUND tag=0x0
    NetworkStatsHistory: bucketDuration=7200
      st=1711677600 rb=163011 rp=132 tb=6382 tp=135 op=0
  ident=[{type=MOBILE, subType=COMBINED, subscriberId=460082..., metered=true, defaultNetwork=true}] uid=10046 set=DEFAULT tag=0x0
    NetworkStatsHistory: bucketDuration=7200
      st=1711677600 rb=36438 rp=269 tb=22295 tp=279 op=0
  ident=[{type=MOBILE, subType=COMBINED, subscriberId=460082..., metered=true, defaultNetwork=true}] uid=10046 set=FOREGROUND tag=0x0
    NetworkStatsHistory: bucketDuration=7200
      st=1711677600 rb=189046 rp=168 tb=12387 tp=166 op=0
  ident=[{type=MOBILE, subType=COMBINED, subscriberId=460086..., metered=true, defaultNetwork=true}] uid=0 set=DEFAULT tag=0x0
    NetworkStatsHistory: bucketDuration=7200
      st=1712023200 rb=0 rp=0 tb=5660 tp=76 op=0
  ident=[{type=MOBILE, subType=COMBINED, subscriberId=460086..., metered=true, defaultNetwork=true}] uid=1000 set=DEFAULT tag=0x0
    NetworkStatsHistory: bucketDuration=7200
      st=1712023200 rb=45532 rp=186 tb=26713 tp=227 op=0
  ident=[{type=MOBILE, subType=COMBINED, subscriberId=460086..., metered=true, defaultNetwork=true}] uid=1020 set=DEFAULT tag=0x0
    NetworkStatsHistory: bucketDuration=7200
      st=1712023200 rb=33160 rp=110 tb=23282 tp=78 op=0
  ident=[{type=MOBILE, subType=COMBINED, subscriberId=460086..., metered=true, defaultNetwork=true}] uid=1051 set=DEFAULT tag=0x0
    NetworkStatsHistory: bucketDuration=7200
      st=1712023200 rb=1035 rp=10 tb=679 tp=10 op=0
  ident=[{type=MOBILE, subType=COMBINED, subscriberId=460086..., metered=true, defaultNetwork=true}] uid=10018 set=DEFAULT tag=0x0
    NetworkStatsHistory: bucketDuration=7200
      st=1712023200 rb=3043 rp=4 tb=272 tp=5 op=0
  ident=[{type=MOBILE, subType=COMBINED, subscriberId=460086..., metered=true, defaultNetwork=true}] uid=10018 set=FOREGROUND tag=0x0
    NetworkStatsHistory: bucketDuration=7200
      st=1712023200 rb=7340 rp=10 tb=1513 tp=11 op=0
  ident=[{type=MOBILE, subType=COMBINED, subscriberId=460086..., metered=true, defaultNetwork=true}] uid=10046 set=DEFAULT tag=0x0
    NetworkStatsHistory: bucketDuration=7200
      st=1712023200 rb=7437 rp=26 tb=3073 tp=27 op=0
  ident=[{type=MOBILE, subType=COMBINED, subscriberId=460086..., metered=true, defaultNetwork=true}] uid=10046 set=FOREGROUND tag=0x0
    NetworkStatsHistory: bucketDuration=7200
      st=1712023200 rb=36696 rp=262 tb=26321 tp=283 op=0


单次开机流量数据

adb shell dumpsys netstats –uid


adb shell dumpsys netstats --uid

Active interfaces:
  iface=usb0 ident=[{type=MOBILE, subType=COMBINED, subscriberId=460086..., metered=true, defaultNetwork=true}] 
Active UID interfaces:
  iface=usb0 ident=[{type=MOBILE, subType=COMBINED, subscriberId=460086..., metered=true, defaultNetwork=true}] 
Top openSession callers (uid=count):

Dev stats:
  Pending bytes: 1250069
  History since boot:
  ident=[{type=MOBILE, subType=COMBINED, subscriberId=460086..., metered=true, defaultNetwork=true}] uid=-1 set=ALL tag=0x0
    NetworkStatsHistory: bucketDuration=3600
      st=1712026800 rb=617403 rp=2788 tb=632666 tp=3407 op=0
Xt stats:
  Pending bytes: 1250069
  History since boot:
  ident=[{type=MOBILE, subType=COMBINED, subscriberId=460086..., metered=true, defaultNetwork=true}] uid=-1 set=ALL tag=0x0
    NetworkStatsHistory: bucketDuration=3600
      st=1712026800 rb=617403 rp=2788 tb=632666 tp=3407 op=0
UID stats:
  Pending bytes: 1231831
  History since boot:
  ident=[{type=MOBILE, subType=COMBINED, subscriberId=460086..., metered=true, defaultNetwork=true}] uid=-5 set=DEFAULT tag=0x0
    NetworkStatsHistory: bucketDuration=7200
      st=1712023200 rb=382993 rp=1247 tb=408558 tp=1408 op=0
  ident=[{type=MOBILE, subType=COMBINED, subscriberId=460086..., metered=true, defaultNetwork=true}] uid=0 set=DEFAULT tag=0x0
    NetworkStatsHistory: bucketDuration=7200
      st=1712023200 rb=0 rp=0 tb=8149 tp=95 op=0
  ident=[{type=MOBILE, subType=COMBINED, subscriberId=460086..., metered=true, defaultNetwork=true}] uid=1000 set=DEFAULT tag=0x0
    NetworkStatsHistory: bucketDuration=7200
      st=1712023200 rb=50694 rp=279 tb=39314 tp=365 op=0
  ident=[{type=MOBILE, subType=COMBINED, subscriberId=460086..., metered=true, defaultNetwork=true}] uid=1020 set=DEFAULT tag=0x0
    NetworkStatsHistory: bucketDuration=7200
      st=1712023200 rb=48712 rp=154 tb=31058 tp=100 op=0
  ident=[{type=MOBILE, subType=COMBINED, subscriberId=460086..., metered=true, defaultNetwork=true}] uid=1051 set=DEFAULT tag=0x0
    NetworkStatsHistory: bucketDuration=7200
      st=1712023200 rb=1307 rp=13 tb=903 tp=13 op=0
  ident=[{type=MOBILE, subType=COMBINED, subscriberId=460086..., metered=true, defaultNetwork=true}] uid=1052 set=DEFAULT tag=0x0
    NetworkStatsHistory: bucketDuration=7200
      st=1712023200 rb=15602 rp=86 tb=6357 tp=93 op=0
  ident=[{type=MOBILE, subType=COMBINED, subscriberId=460086..., metered=true, defaultNetwork=true}] uid=10018 set=DEFAULT tag=0x0
    NetworkStatsHistory: bucketDuration=7200
      st=1712023200 rb=3043 rp=4 tb=272 tp=5 op=0
  ident=[{type=MOBILE, subType=COMBINED, subscriberId=460086..., metered=true, defaultNetwork=true}] uid=10018 set=FOREGROUND tag=0x0
    NetworkStatsHistory: bucketDuration=7200
      st=1712023200 rb=7340 rp=10 tb=1513 tp=11 op=0
  ident=[{type=MOBILE, subType=COMBINED, subscriberId=460086..., metered=true, defaultNetwork=true}] uid=10046 set=DEFAULT tag=0x0
    NetworkStatsHistory: bucketDuration=7200
      st=1712023200 rb=9430 rp=51 tb=5170 tp=56 op=0
  ident=[{type=MOBILE, subType=COMBINED, subscriberId=460086..., metered=true, defaultNetwork=true}] uid=10046 set=FOREGROUND tag=0x0
    NetworkStatsHistory: bucketDuration=7200
      st=1712023200 rb=113062 rp=968 tb=98354 tp=1112 op=0


解释

ident=[{type=MOBILE, subType=COMBINED, subscriberId=460086…, metered=true, defaultNetwork=true}]: 描述的是网络接口身份信息,其中:

  • type=MOBILE: 表明这是一个移动网络类型,如2G、3G、4G、5G等。
  • subType=COMBINED: 可能是指综合型的移动网络,即不分具体的子类型。
  • subscriberId=460086…: 移动网络运营商提供的SIM卡的唯一标识(IMSI的一部分)。
  • metered=true: 表示该网络是计费的,即使用此网络产生的流量可能会计入用户的套餐限额。
  • defaultNetwork=true: 表示这是设备默认连接的网络。
  • uid=10046: 指的是执行此次流量操作的用户ID(UID)。在Android系统中,每个应用程序都有一个对应的UID,用于区分不同的应用并进行权限控制。

set=DEFAULT: 这里的“set”通常指的是网络统计集合,DEFAULT可能是默认的统计集合,涵盖了所有的网络活动。

tag=0x0: “tag”在这里通常用于标记特定的网络流量源,0x0表示没有特定的tag标签,可能是所有未特别标记的网络流量。

NetworkStatsHistory: bucketDuration=7200: 描述的是网络流量历史统计信息,这里的bucketDuration表示每个统计桶的时间长度,单位是秒,这里为7200秒,即2小时。

后面的 st=1712023200 rb=9430 rp=51 tb=5170 tp=56 op=0:

  • st=开始时间戳,这里的1712023200是一个Unix时间戳,转换成日期需要进一步处理。
  • rb=收到(Received)的总字节数。
  • rp=收到(Received)的包数。
  • tb=发送(Transmitted)的总字节数。
  • tp=发送(Transmitted)的包数。
  • op=未知字段,可能根据上下文有不同的含义,这里没有足够的信息来准确说明。在某些网络统计中,op可能代表丢包数量或其他操作计数。

源码

frameworks/base/services/core/java/com/android/server/net/NetworkStatsService.java


// 是否启用网络流量统计增强功能,这可能涉及到补充和完善网络使用数据
import static android.provider.Settings.Global.NETSTATS_AUGMENT_ENABLED;

// 设备级别的网络流量统计bucket(时间间隔)持续时间
import static android.provider.Settings.Global.NETSTATS_DEV_BUCKET_DURATION;

// 设备级别的网络流量统计数据的删除保留期限
import static android.provider.Settings.Global.NETSTATS_DEV_DELETE_AGE;

// 设备级别网络流量统计持久化的最大字节数
import static android.provider.Settings.Global.NETSTATS_DEV_PERSIST_BYTES;

// 设备级别网络流量统计数据的旋转保留期限
import static android.provider.Settings.Global.NETSTATS_DEV_ROTATE_AGE;

// 触发全局流量警报的字节数阈值
import static android.provider.Settings.Global.NETSTATS_GLOBAL_ALERT_BYTES;

// 网络流量统计信息的轮询间隔
import static android.provider.Settings.Global.NETSTATS_POLL_INTERVAL;

// 是否启用网络流量统计采样功能
import static android.provider.Settings.Global.NETSTATS_SAMPLE_ENABLED;

// 基于用户ID的网络流量统计bucket持续时间
import static android.provider.Settings.Global.NETSTATS_UID_BUCKET_DURATION;
import static android.provider.Settings.Global.NETSTATS_UID_DELETE_AGE;
import static android.provider.Settings.Global.NETSTATS_UID_PERSIST_BYTES;
import static android.provider.Settings.Global.NETSTATS_UID_ROTATE_AGE;
import static android.provider.Settings.Global.NETSTATS_UID_TAG_BUCKET_DURATION;
import static android.provider.Settings.Global.NETSTATS_UID_TAG_DELETE_AGE;
import static android.provider.Settings.Global.NETSTATS_UID_TAG_PERSIST_BYTES;
import static android.provider.Settings.Global.NETSTATS_UID_TAG_ROTATE_AGE;

public class NetworkStatsService extends INetworkStatsService.Stub {
    static final String TAG = "NetworkStats";
    
    /**
     * Default external settings that read from
     * {@link android.provider.Settings.Global}.
     */
    private static class DefaultNetworkStatsSettings implements NetworkStatsSettings {
        private final ContentResolver mResolver;

        public DefaultNetworkStatsSettings(Context context) {
            mResolver = checkNotNull(context.getContentResolver());
            // TODO: adjust these timings for production builds
        }

        private long getGlobalLong(String name, long def) {
            return Settings.Global.getLong(mResolver, name, def);
        }
        private boolean getGlobalBoolean(String name, boolean def) {
            final int defInt = def ? 1 : 0;
            return Settings.Global.getInt(mResolver, name, defInt) != 0;
        }

        @Override
        public long getPollInterval() {
            return getGlobalLong(NETSTATS_POLL_INTERVAL, 30 * MINUTE_IN_MILLIS);
        }
        @Override
        public long getGlobalAlertBytes(long def) {
            return getGlobalLong(NETSTATS_GLOBAL_ALERT_BYTES, def);
        }
        @Override
        public boolean getSampleEnabled() {
            return getGlobalBoolean(NETSTATS_SAMPLE_ENABLED, true);
        }
        @Override
        public boolean getAugmentEnabled() {
            return getGlobalBoolean(NETSTATS_AUGMENT_ENABLED, true);
        }
        @Override
        public Config getDevConfig() {
            // 修改此处的流量统计间隔时间
            return new Config(getGlobalLong(NETSTATS_DEV_BUCKET_DURATION, HOUR_IN_MILLIS),
                    getGlobalLong(NETSTATS_DEV_ROTATE_AGE, 15 * DAY_IN_MILLIS),
                    getGlobalLong(NETSTATS_DEV_DELETE_AGE, 90 * DAY_IN_MILLIS));
        }
        @Override
        public Config getXtConfig() {
            return getDevConfig();
        }
        @Override
        public Config getUidConfig() {
            // 修改此处的流量统计间隔时间
            return new Config(getGlobalLong(NETSTATS_UID_BUCKET_DURATION, 2 * HOUR_IN_MILLIS),
                    getGlobalLong(NETSTATS_UID_ROTATE_AGE, 15 * DAY_IN_MILLIS),
                    getGlobalLong(NETSTATS_UID_DELETE_AGE, 90 * DAY_IN_MILLIS));
        }
        @Override
        public Config getUidTagConfig() {
            // 修改此处的流量统计间隔时间
            return new Config(getGlobalLong(NETSTATS_UID_TAG_BUCKET_DURATION, 2 * HOUR_IN_MILLIS),
                    getGlobalLong(NETSTATS_UID_TAG_ROTATE_AGE, 5 * DAY_IN_MILLIS),
                    getGlobalLong(NETSTATS_UID_TAG_DELETE_AGE, 15 * DAY_IN_MILLIS));
        }
        @Override
        public long getDevPersistBytes(long def) {
            return getGlobalLong(NETSTATS_DEV_PERSIST_BYTES, def);
        }
        @Override
        public long getXtPersistBytes(long def) {
            return getDevPersistBytes(def);
        }
        @Override
        public long getUidPersistBytes(long def) {
            return getGlobalLong(NETSTATS_UID_PERSIST_BYTES, def);
        }
        @Override
        public long getUidTagPersistBytes(long def) {
            return getGlobalLong(NETSTATS_UID_TAG_PERSIST_BYTES, def);
        }
    }

    @Override
    protected void dump(FileDescriptor fd, PrintWriter rawWriter, String[] args) {
        if (!DumpUtils.checkDumpPermission(mContext, TAG, rawWriter)) return;

        long duration = DateUtils.DAY_IN_MILLIS;
        final HashSet<String> argSet = new HashSet<String>();
        for (String arg : args) {
            argSet.add(arg);

            if (arg.startsWith("--duration=")) {
                try {
                    duration = Long.parseLong(arg.substring(11));
                } catch (NumberFormatException ignored) {
                }
            }
        }

        // usage: dumpsys netstats --full --uid --tag --poll --checkin
        final boolean poll = argSet.contains("--poll") || argSet.contains("poll");
        final boolean checkin = argSet.contains("--checkin");
        final boolean fullHistory = argSet.contains("--full") || argSet.contains("full");
        final boolean includeUid = argSet.contains("--uid") || argSet.contains("detail");
        final boolean includeTag = argSet.contains("--tag") || argSet.contains("detail");

        final IndentingPrintWriter pw = new IndentingPrintWriter(rawWriter, "  ");

        synchronized (mStatsLock) {
            if (args.length > 0 && "--proto".equals(args[0])) {
                // In this case ignore all other arguments.
                dumpProtoLocked(fd);
                return;
            }

            if (poll) {
                performPollLocked(FLAG_PERSIST_ALL | FLAG_PERSIST_FORCE);
                pw.println("Forced poll");
                return;
            }

            if (checkin) {
                final long end = System.currentTimeMillis();
                final long start = end - duration;

                pw.print("v1,");
                pw.print(start / SECOND_IN_MILLIS); pw.print(',');
                pw.print(end / SECOND_IN_MILLIS); pw.println();

                pw.println("xt");
                mXtRecorder.dumpCheckin(rawWriter, start, end);

                if (includeUid) {
                    pw.println("uid");
                    mUidRecorder.dumpCheckin(rawWriter, start, end);
                }
                if (includeTag) {
                    pw.println("tag");
                    mUidTagRecorder.dumpCheckin(rawWriter, start, end);
                }
                return;
            }

            pw.println("Active interfaces:");
            pw.increaseIndent();
            for (int i = 0; i < mActiveIfaces.size(); i++) {
                pw.printPair("iface", mActiveIfaces.keyAt(i));
                pw.printPair("ident", mActiveIfaces.valueAt(i));
                pw.println();
            }
            pw.decreaseIndent();

            pw.println("Active UID interfaces:");
            pw.increaseIndent();
            for (int i = 0; i < mActiveUidIfaces.size(); i++) {
                pw.printPair("iface", mActiveUidIfaces.keyAt(i));
                pw.printPair("ident", mActiveUidIfaces.valueAt(i));
                pw.println();
            }
            pw.decreaseIndent();

            // Get the top openSession callers
            final SparseIntArray calls;
            synchronized (mOpenSessionCallsPerUid) {
                calls = mOpenSessionCallsPerUid.clone();
            }

            final int N = calls.size();
            final long[] values = new long[N];
            for (int j = 0; j < N; j++) {
                values[j] = ((long) calls.valueAt(j) << 32) | calls.keyAt(j);
            }
            Arrays.sort(values);

            pw.println("Top openSession callers (uid=count):");
            pw.increaseIndent();
            final int end = Math.max(0, N - DUMP_STATS_SESSION_COUNT);
            for (int j = N - 1; j >= end; j--) {
                final int uid = (int) (values[j] & 0xffffffff);
                final int count = (int) (values[j] >> 32);
                pw.print(uid); pw.print("="); pw.println(count);
            }
            pw.decreaseIndent();
            pw.println();

            pw.println("Dev stats:");
            pw.increaseIndent();
            mDevRecorder.dumpLocked(pw, fullHistory);
            pw.decreaseIndent();

            pw.println("Xt stats:");
            pw.increaseIndent();
            mXtRecorder.dumpLocked(pw, fullHistory);
            pw.decreaseIndent();

            if (includeUid) {
                pw.println("UID stats:");
                pw.increaseIndent();
                mUidRecorder.dumpLocked(pw, fullHistory);
                pw.decreaseIndent();
            }

            if (includeTag) {
                pw.println("UID tag stats:");
                pw.increaseIndent();
                mUidTagRecorder.dumpLocked(pw, fullHistory);
                pw.decreaseIndent();
            }
        }
    }

}

frameworks/base/services/core/java/com/android/server/net/NetworkStatsRecorder.java


public class NetworkStatsRecorder {
    private static final String TAG = "NetworkStatsRecorder";


    public void dumpLocked(IndentingPrintWriter pw, boolean fullHistory) {
        if (mPending != null) {
            pw.print("Pending bytes: "); pw.println(mPending.getTotalBytes());
        }
        if (fullHistory) {
            pw.println("Complete history:");
            getOrLoadCompleteLocked().dump(pw);
        } else {
            pw.println("History since boot:");
            mSinceBoot.dump(pw);
        }
    }

}

frameworks/base/services/core/java/com/android/server/net/NetworkStatsCollection.java


public class NetworkStatsCollection implements FileRotator.Reader {

    public void dump(IndentingPrintWriter pw) {
        for (Key key : getSortedKeys()) {
            pw.print("ident="); pw.print(key.ident.toString());
            pw.print(" uid="); pw.print(key.uid);
            pw.print(" set="); pw.print(NetworkStats.setToString(key.set));
            pw.print(" tag="); pw.println(NetworkStats.tagToString(key.tag));

            final NetworkStatsHistory history = mStats.get(key);
            pw.increaseIndent();
            history.dump(pw, true);
            pw.decreaseIndent();
        }
    }

}

frameworks/base/core/java/android/net/NetworkStatsHistory.java


public class NetworkStatsHistory implements Parcelable {

    public void dump(IndentingPrintWriter pw, boolean fullHistory) {
        pw.print("NetworkStatsHistory: bucketDuration=");
        pw.println(bucketDuration / SECOND_IN_MILLIS);
        pw.increaseIndent();

        final int start = fullHistory ? 0 : Math.max(0, bucketCount - 32);
        if (start > 0) {
            pw.print("(omitting "); pw.print(start); pw.println(" buckets)");
        }

        for (int i = start; i < bucketCount; i++) {
            pw.print("st="); pw.print(bucketStart[i] / SECOND_IN_MILLIS);
            if (rxBytes != null) { pw.print(" rb="); pw.print(rxBytes[i]); }
            if (rxPackets != null) { pw.print(" rp="); pw.print(rxPackets[i]); }
            if (txBytes != null) { pw.print(" tb="); pw.print(txBytes[i]); }
            if (txPackets != null) { pw.print(" tp="); pw.print(txPackets[i]); }
            if (operations != null) { pw.print(" op="); pw.print(operations[i]); }
            pw.println();
        }

        pw.decreaseIndent();
    }

}


UID 类型

frameworks/base/core/java/android/app/usage/NetworkStats.java


public final class NetworkStats implements AutoCloseable {
    private final static String TAG = "NetworkStats";

        /**
         * Special UID value for aggregate/unspecified.
         */
        public static final int UID_ALL = android.net.NetworkStats.UID_ALL;

        /**
         * Special UID value for removed apps.
         */
        public static final int UID_REMOVED = TrafficStats.UID_REMOVED;

        /**
         * Special UID value for data usage by tethering.
         */
        public static final int UID_TETHERING = TrafficStats.UID_TETHERING;

}

frameworks/base/core/java/android/net/TrafficStats.java


public class TrafficStats {
    /**
     * The return value to indicate that the device does not support the statistic.
     */
    public final static int UNSUPPORTED = -1;

    /** @hide @deprecated use {@link DataUnit} instead to clarify SI-vs-IEC */
    @Deprecated
    public static final long KB_IN_BYTES = 1024;
    /** @hide @deprecated use {@link DataUnit} instead to clarify SI-vs-IEC */
    @Deprecated
    public static final long MB_IN_BYTES = KB_IN_BYTES * 1024;
    /** @hide @deprecated use {@link DataUnit} instead to clarify SI-vs-IEC */
    @Deprecated
    public static final long GB_IN_BYTES = MB_IN_BYTES * 1024;
    /** @hide @deprecated use {@link DataUnit} instead to clarify SI-vs-IEC */
    @Deprecated
    public static final long TB_IN_BYTES = GB_IN_BYTES * 1024;
    /** @hide @deprecated use {@link DataUnit} instead to clarify SI-vs-IEC */
    @Deprecated
    public static final long PB_IN_BYTES = TB_IN_BYTES * 1024;

    /**
     * Special UID value used when collecting {@link NetworkStatsHistory} for
     * removed applications.
     *
     * @hide
     */
    public static final int UID_REMOVED = -4;

    /**
     * Special UID value used when collecting {@link NetworkStatsHistory} for
     * tethering traffic.
     *
     * @hide
     */
    public static final int UID_TETHERING = -5;

}

App 根据UID获取流量


    @RequiresApi(api = Build.VERSION_CODES.M)
    @SuppressLint("HardwareIds")
    public static long getNetworkStatsByUid() {
        long summaryTotal = 0;
        try {
            NetworkStats.Bucket summaryBucket = null;
            summaryBucket = new NetworkStats.Bucket();
            long startTime = System.currentTimeMillis() - SystemClock.elapsedRealtime();
            Log.d(TAG, "startTime: " + startTime);
            NetworkStats NetworkStats = sNSM.queryDetailsForUid(ConnectivityManager.TYPE_MOBILE, sTM.getSubscriberId(), startTime, System.currentTimeMillis(), -5);
            do {
                NetworkStats.getNextBucket(summaryBucket);
                long summaryRx = summaryBucket.getRxBytes();
                long summaryTx = summaryBucket.getTxBytes();
                summaryTotal += summaryRx + summaryTx;
            } while (NetworkStats.hasNextBucket());
        } catch (Exception e) {
            e.printStackTrace();
        }

        Log.d(TAG, "summaryTotal: " + summaryTotal);
        return summaryTotal;
    }