Android 流量统计
最近项目上有一个应用流量统计的功能需要实现,在此总结一下
流量统计架构
在Android9.0
之前,流量监控是基于xt_qtaguid
模块的,通过读取/proc/net/xt_qtaguid/stats
文件内容进行解析获取对应流量数据。
Android9.0
之后,改为基于eBPF
的网络监控模块,更加灵活且更易于维护;Android9.0
通过读取/sys/fs/bpf/traffic_uid_stats_map
获取数据;Android10.0
通过读取/sys/fs/bpf/map_netd_app_uid_stats_map
获取数据。
调试查看流量信息:
XXX:/ # dumpsys netd trafficcontroller
TrafficController
BPF module status: BPF_LEVEL_EXTENDED
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:
mUidCounterSetMap:
1001 1
1073 1
1002 1
10046 1
1000 1
10016 1
1076 1
10037 1
10056 1
10019 1
mAppUidStatsMap::
uid rxBytes rxPackets txBytes txPackets
10020 11763 24 15904 37
1076 324919 1159 208570 1326
10036 171008 1213 143638 1239
1051 19271 140 9751 154
0 3106517 27677 3158691 28118
10019 18591 30 5631 39
1020 356303 1419 118037 454
10045 19345 121 14092 167
10037 16511616 13422 13875657 12921
1000 3623643 35705 3038615 35624
mStatsMapA:
ifaceIndex ifaceName tag_hex uid_int cnt_set rxBytes rxPackets txBytes txPackets
mStatsMapB:
ifaceIndex ifaceName tag_hex uid_int cnt_set rxBytes rxPackets txBytes txPackets
3 dummy0 0x0 0 0 0 0 152 2
1 lo 0x0 1000 1 8232 98 8232 98
3 dummy0 0x0 1020 0 1527 6 1527 6
1 lo 0x0 1020 0 1176 6 1176 6
mIfaceIndexNameMap:
ifaceIndex=8 ifaceName=rmnet_ipa0
ifaceIndex=6 ifaceName=sit0
ifaceIndex=7 ifaceName=ip6tnl0
ifaceIndex=4 ifaceName=ip_vti0
ifaceIndex=2 ifaceName=bond0
ifaceIndex=11 ifaceName=p2p0
ifaceIndex=3 ifaceName=dummy0
ifaceIndex=10 ifaceName=wlan0
ifaceIndex=5 ifaceName=ip6_vti0
ifaceIndex=1 ifaceName=lo
mIfaceStatsMap::
ifaceIndex ifaceName rxBytes rxPackets txBytes txPackets
3 dummy0 50949 196 110002 501
10 wlan0 4210802 7222 764069 6653
1 lo 19812096 73362 19812096 73362
查询方法
这里主要介绍一下NetworkStatsManager的相关方法
TrafficStats
- 获取所有网口总的统计数据
- static long getTotalRxBytes() //返回设备启动后收到的字节数。
- static long getTotalRxPackets() //返回设备启动后收到的数据包数量。
- static long getTotalTxBytes() //返回设备启动后传输的字节数。
- static long getTotalTxPackets() //返回设备启动后传输的数据包数量。
- 根据UID获取统计数据
- static long getUidRxBytes(int uid) //返回设备引导后给定UID收到的字节数。
- static long getUidRxPackets(int uid) //返回设备启动后给定UID收到的数据包数量。
- static long getUidTxBytes(int uid) //返回设备启动后收到的字节数。
- static long getUidTxPackets(int uid) //返回设备启动后传输的数据包数量
NetworkStatsManager
通过UID查询单个应用
- 通过包名获取UID
private static int getUidByPackageName(Context context, String packageName) {
int uid = -1;
PackageManager packageManager = context.getPackageManager();
try {
PackageInfo packageInfo = packageManager.getPackageInfo(packageName,
PackageManager.GET_META_DATA);
uid = packageInfo.applicationInfo.uid;
} catch (PackageManager.NameNotFoundException ex) {
ex.printStackTrace();
}
return uid;
}
- 获取应用使用流量信息
- 参数描述如下:
networkType
查询网络类型 (ConnectivityManager.TYPE_WIFI
,ConnectivityManager.TYPE_MOBILE
)subscriberId
设备唯一id(android 10及以后设备 获取不了,可不传)startTime
查询指定时间段 开始时间戳endTime
查询指定时间段 结束时间uid
查询设备的Uid
- 参数描述如下:
NetworkStatsManager statsManager = (NetworkStatsManager) AccountApplication.getContext()
.getSystemService(NETWORK_STATS_SERVICE);
try (NetworkStats stats = statsManager.queryDetailsForUid(ConnectivityManager.TYPE_MOBILE,
null, startTime, endTime, uid)) {
NetworkStats.Bucket bucket = new NetworkStats.Bucket();
stats.getNextBucket(bucket);
totalSize = bucket.getRxBytes() + bucket.getTxBytes();
}
问题
在使用queryDetailsForUid方法的过程中,发现获取到的数据为0,通过网上查阅才了解到,需要设置subscribeId为null而不是“”,源码中也有相关说明
- queryDetailsForUid(int networkType, String subscriberId,long startTime, long endTime, int uid)
- queryDetailsForUidTagState(int networkType, String subscriberId,long startTime, long endTime, int uid, int tag, int state)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sbCtthh2-1677057537429)(C:\Users\ts\AppData\Roaming\Typora\typora-user-images\image-20230222171356158.png)]
- createTemplate(int networkType, String subscriberId)
这里可以看到传入null进行查询就可以了;
然后是查询APP今日使用流量,发现总是获取为0,后来通过查询时间改为次日即可正常查询出来,very good!
参考连接
- (2条消息) android 统计应用流量 NetworkStatsManager_android获取流量使用情况_北极的松树的博客-CSDN博客
- Android-流量统计 - Wxy的个人博客 (leo-wxy.github.io)
- NetworkStatsManager.java - OpenGrok cross reference for /frameworks/base/core/java/android/app/usage/NetworkStatsManager.java (aospxref.com)