Android流量统计分析

Android流量统计分析

本文只做为个人分析留档。

使用

NetworkStatsManager statsManager = (NetworkStatsManager) getSystemService(Context.NETWORK_STATS_SERVICE);
tatsManager.querySummary(ConnectivityManager.TYPE_MOBILE, "", startTime, endTime);
statsManager.querySummaryForDevice(ConnectivityManager.TYPE_WIFI, null, startTime, endTime);

权限分析

为什么需要权限?
NetworkStatsAccess.java
@NetworkStatsAccess.Level int checkAccessLevel

这类生成查询的等级,按顺序来。
1.如果是系统签名,后面直接放行,可以查询全部。NetworkStatsAccess.Level.DEVICE;
2.如果是isDeviceOwner ,system uid同上。NetworkStatsAccess.Level.DEVICE;
3.如果拥有上述权限,level是:NetworkStatsAccess.Level.DEVICESUMMARY;
4.如果是isProfileOwner,level是:NetworkStatsAccess.Level.USER;
5.啥都没有就是默认:NetworkStatsAccess.Level.DEFAULT;

查询之前检测level

public static boolean isAccessibleToUser(int uid, int callerUid,
            @NetworkStatsAccess.Level int accessLevel) {
        switch (accessLevel) {
            case NetworkStatsAccess.Level.DEVICE:
                // Device-level access - can access usage for any uid.
                return true;
            case NetworkStatsAccess.Level.DEVICESUMMARY:
                // Can access usage for any app running in the same user, along
                // with some special uids (system, removed, or tethering) and
                // anonymized uids
                return uid == android.os.Process.SYSTEM_UID || uid == UID_REMOVED
                        || uid == UID_TETHERING || uid == UID_ALL
                        || UserHandle.getUserId(uid) == UserHandle.getUserId(callerUid);
            case NetworkStatsAccess.Level.USER:
                // User-level access - can access usage for any app running in the same user, along
                // with some special uids (system, removed, or tethering).
                return uid == android.os.Process.SYSTEM_UID || uid == UID_REMOVED
                        || uid == UID_TETHERING
                        || UserHandle.getUserId(uid) == UserHandle.getUserId(callerUid);
            case NetworkStatsAccess.Level.DEFAULT:
            default:
                // Default access level - can only access one's own usage.
                return uid == callerUid;
        }
    }

NetworkStatsAccess.Level.USER 和NetworkStatsAccess.Level.DEVICESUMMARY基本类似,就是多了一个uid == UID_ALL。
NetworkStatsAccess.Level.DEVICESUMMARY这里面根据系统不一样有点区别。如果系统不支持多用户,那么就能查询全部,跟NetworkStatsAccess.Level.DEVICE一样,否则看注释。

NetworkStatsService

这个类是核心类,所有的查询都是从这里开始。

1.构造
NetworkStatsService service = new NetworkStatsService(context, networkManager, alarmManager,
wakeLock, getDefaultClock(), TelephonyManager.getDefault(),
new DefaultNetworkStatsSettings(context), new NetworkStatsObservers(),
getDefaultSystemDir(), getDefaultBaseDir());

构造里面传入的路径是/data/system/netstats.记住这个路径。

2.systemReady
这个方法有几个重要的方法:

 mDevRecorder = buildRecorder(PREFIX_DEV, mSettings.getDevConfig(), false);
            mXtRecorder = buildRecorder(PREFIX_XT, mSettings.getXtConfig(), false);
            mUidRecorder = buildRecorder(PREFIX_UID, mSettings.getUidConfig(), false);
            mUidTagRecorder = buildRecorder(PREFIX_UID_TAG, mSettings.getUidTagConfig(), true);
//对应上面路径下面的四个文件
bootstrapStatsLocked()//这个里面多了一个路径。

初次检查,上述路径没有数据,那么统计数据哪里来的?
bootstrapStatsLocked()搞定了。
这个部分回到Android之前的统计了。

public NetworkStatsFactory(File procRoot, boolean useBpfStats) {
        mStatsXtIfaceAll = new File(procRoot, "net/xt_qtaguid/iface_stat_all");
        mStatsXtIfaceFmt = new File(procRoot, "net/xt_qtaguid/iface_stat_fmt");
        mStatsXtUid = new File(procRoot, "net/xt_qtaguid/stats");
        mUseBpfStats = useBpfStats;
    }

以上所有的文件解析出来,就是Android所有记录的流量使用情况。
其中/data/system/netstats 里面保存了uid ,pkg,subscriberId。似乎卸载的app的包名也记录了,这部分没有仔细去看。大概是因为文件是生成的时候app还没卸载。但是因为有部分流量是使用之前的流量统计逻辑,这部分不会记录包名,如果生成文件是已经卸载似乎就不会记录。

查询匹配

查询之前,生成:

 private static NetworkTemplate createTemplate(int networkType, String subscriberId) {
        final NetworkTemplate template;
        switch (networkType) {
            case ConnectivityManager.TYPE_MOBILE:
                template = subscriberId == null
                        ? NetworkTemplate.buildTemplateMobileWildcard()
                        : NetworkTemplate.buildTemplateMobileAll(subscriberId);
                break;
            case ConnectivityManager.TYPE_WIFI:
                template = NetworkTemplate.buildTemplateWifiWildcard();
                break;
            default:
                throw new IllegalArgumentException("Cannot create template for network type "
                        + networkType + ", subscriberId '"
                        + NetworkIdentity.scrubSubscriberId(subscriberId) + "'.");
        }
        return template;
    }

生成的template与上述NetworkStatsService读取的数据做匹配。
具体规则区间,可以去看NetworkStatsCollection。

说明一个,移动网络查询之前subscriberId,在Android9还是10以后,传入null,就是查询时间段类的所有卡信息,即使卡不存在。即生成的template是NetworkTemplate(MATCH_MOBILE_WILDCARD, null, null);

然后正常查询,系统会去掉uid_tag文件的信息。这个部分暂时没细看是做什么的。后续有时间补充。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值