糖尿病康复,内容丰富有趣,生活中的好帮手!
糖尿病康复 > Android:全面详细的解析Android数据流量统计流程与分析方法(流量检测 流量监控 流

Android:全面详细的解析Android数据流量统计流程与分析方法(流量检测 流量监控 流

时间:2020-12-16 03:27:58

相关推荐

Android:全面详细的解析Android数据流量统计流程与分析方法(流量检测 流量监控 流

作者:GentlemanTsao

/GentelmanTsao

每篇一格言

Stay hungry,stay foolish!

——Steve Jobs

文章目录

前言流量检测的时序:流量监听的Controller(亦是主题):dcTracker流量监听的model:android_net_TrafficStats观察者:PhoneStateListener附录1.数据流量相关文件附录2. Log分析与工具分析需抓到下面的log相关分析工具:附录3.重要代码Step 1. 统计数据包(native层)step 2 .检测是否有上下行数据的逻辑step 3. 通知监听者流量状态更新Step 4.通知RSSI状态更新

前言

Android Telephony Framework跨越AP和BP,想要深入理解telephony,需要对modem架构(BP)、QCRIL/QMI框架、RIL框架以及Framework telephony框架(Java层)的源码有深入研究——不止于对源码的阅读和框架的理解,还需要大量的debug与trace经验。

不同于app|UI 开发的修改即可见,Telephony debug的复杂度很高,在大多数时候要求通过阅读源码结合log trace来复现、定位并解决问题。这对telephony开发者的技术宽度、逻辑严谨性、动手能力和快速反应力提出了多重挑战。

数据业务,作为telephony的重要部分——很可能是最重要的,是4G/5G时代Android的基础功能,值得Android开发者认真研究。

本篇介绍的是Android Framework中的数据流量统计流程,希望你会喜欢。

流量检测的时序:

你大概发现了,上面的时序图是基于MVC架构(Model-View-Controller). View部分并不在图中,因为这不是本篇关注的重点。MVC是常见的软件设计框架,它将业务逻辑、数据以及UI显示解耦,符合耦合性低,重用性和可维护性高的软件工程思想。MVC的改进版有MVP、MVVM等。

从设计模式的角度看,该时序属于观察者模式。如下图所示:

DcTracker扮演主题(被观察者)的角色,当有数据更新时通知PhoneStateListener. PhoneStateListener扮演观察者的角色,监听DcTracker状态的改变。

下面我们从任务流的角度,具体分析流量检测的流程。

为了阅读更清晰,涉及到源码的部分,只贴出关键源码,省略细节。

流量监听的Controller(亦是主题):dcTracker

dcTracker创建如下的Runnable检测流量变化:

private final Runnable mPollNetStat = new Runnable() {@Overridepublic void run() {updateDataActivity();。。。};

该Runnable默认情况下每隔1秒钟执行一次。

这里的updateDataActivity主要做两件事:

检测发送包(tx)和接收包(rx)的变化,分别调用

TrafficStats.getMobileTxPackets()TrafficStats.getMobileRxPackets()

通知phone有数据变化

关于TrafficStats与NetworkStatsService,它们提供java层数据流量接口,这里撇开不具体分析了。

流量监听的model:android_net_TrafficStats

下面是统计数据流量的方法parseIfaceStats。

该方法打开了设备文件“/proc/net/xt_qtaguid/iface_stat_fmt”,统计6种数据,分别是

rxBytes,

rxPackets,

txBytes,

txPackets,

tcpRxPackets,

tcpTxPackets

关键代码如下:

static int parseIfaceStats(const char* iface, struct Stats* stats) {FILE *fp = fopen(QTAGUID_IFACE_STATS, "r");。。。while (fgets(buffer, sizeof(buffer), fp) != NULL) {int matched = sscanf(buffer, "%31s %" SCNu64 " %" SCNu64 " %" SCNu64" %" SCNu64 " " "%*u %" SCNu64 " %*u %*u %*u %*u ""%*u %" SCNu64 " %*u %*u %*u %*u", cur_iface, &rxBytes,&rxPackets, &txBytes, &txPackets, &tcpRxPackets, &tcpTxPackets);if (matched >= 5) {if (matched == 7) {foundTcp = true;}if (!iface || !strcmp(iface, cur_iface)) {stats->rxBytes += rxBytes;stats->rxPackets += rxPackets;stats->txBytes += txBytes;stats->txPackets += txPackets;if (matched == 7) {stats->tcpRxPackets += tcpRxPackets;stats->tcpTxPackets += tcpTxPackets;}}}}。。。}

下面重点看PhoneStateListener。

观察者:PhoneStateListener

PhoneStateListener有一个onDataActivity方法,它根据流量状态做相应处理(例如,通知UI刷新)。

public void onDataActivity(int direction) {// default implementation empty}

该方法的输入参数direction有下面五种状态:

DATA_ACTIVITY_NONE //无流量

DATA_ACTIVITY_IN //下行

DATA_ACTIVITY_OUT //上行

DATA_ACTIVITY_INOUT //上行和下行

DATA_ACTIVITY_DORMANT // 休眠

该方法默认实现为空,也就是什么也不做。由继承PhoneStateListener的子类重写onDataActivity实现具体业务。

看看下面的例子:

MobilePhoneStateListener继承自PhoneStateListener,并重写onDataActivity,通知UI刷新RSSI。

public void onDataActivity(int direction) {if (DEBUG) {Log.d(mTag, "onDataActivity: direction=" + direction);}setActivity(direction);}

上面我们非常概括的梳理了android framework的流量检测流程。去粗取精后,是不是很easy呢?

你可能想了解更多详细的内容,我以附录列出如下:

附录1.数据流量相关文件

android_net_TrafficStats.cpp (amss\linux\android\frameworks\base\core\jni)

JNI层接口,获取IFace状态,统计数据包

TrafficStats.java (amss\linux\android\frameworks\base\core\java\android\net)

java层接口,读取数据包大小

DcTracker.java (amss\linux\android\frameworks\opt\telephony\src\java\com\android\internal\telephony\dataconnection)

负责数据流量跟踪,开了一个子线程定时更新数据包大小,并发送状态给phoneNotifier.

GsmCdmaPhone.java (amss\linux\android\frameworks\opt\telephony\src\java\com\android\internal\telephony)

向UI提供流量状态的接口

DefaultPhoneNotifier.java (amss\linux\android\frameworks\opt\telephony\src\java\com\android\internal\telephony

执行流量通知,将状态通过TelephonyRegistry发给监听者

TelephonyRegistry.java (amss\linux\android\frameworks\base\services\core\java\com\android\server)

向监听者发送流量更新提示

PhoneStateListener.java (amss\linux\android\frameworks\base\telephony\java\android\telephony)

MobileSignalController.java (amss\linux\android\frameworks\base\packages\systemui\src\com\android\systemui\statusbar

私有类MobilePhoneStateListener继承自PhoneStateListener,通知RSSI更新流量状态。

附录2. Log分析与工具

分析需抓到下面的log

modem log:

(高通平台)QXDM log PDCP; (MTK平台)ELT log

Android log:

radio log 及system log;

logcat -v time -b radio -b system > log.txt

TCP log:

TCP dump抓取方法:

adb root

adb shell tcpdump -i any -s 0 -w /data/tcpdump.pcap

adb pull /data/tcpdump.pcap

相关分析工具:

QXDM、QCAT、WIRESHARK

附录3.重要代码

Step 1. 统计数据包(native层)

见下面的parseIfaceStats方法实现。

这里分别累计了rxBytes,rxPackets,txBytes,txPackets,tcpRxPackets,tcpTxPackets。

根据上层的读取接口可以看出,只有tcpRxPackets和tcpTxPackets被看作有效的下行和上行数据。

static int parseIfaceStats(const char* iface, struct Stats* stats) {FILE *fp = fopen(QTAGUID_IFACE_STATS, "r");if (fp == NULL) {return -1;}char buffer[384];char cur_iface[32];bool foundTcp = false;uint64_t rxBytes, rxPackets, txBytes, txPackets, tcpRxPackets, tcpTxPackets;while (fgets(buffer, sizeof(buffer), fp) != NULL) {int matched = sscanf(buffer, "%31s %" SCNu64 " %" SCNu64 " %" SCNu64" %" SCNu64 " " "%*u %" SCNu64 " %*u %*u %*u %*u ""%*u %" SCNu64 " %*u %*u %*u %*u", cur_iface, &rxBytes,&rxPackets, &txBytes, &txPackets, &tcpRxPackets, &tcpTxPackets);if (matched >= 5) {if (matched == 7) {foundTcp = true;}if (!iface || !strcmp(iface, cur_iface)) {stats->rxBytes += rxBytes;stats->rxPackets += rxPackets;stats->txBytes += txBytes;stats->txPackets += txPackets;if (matched == 7) {stats->tcpRxPackets += tcpRxPackets;stats->tcpTxPackets += tcpTxPackets;}}}}if (!foundTcp) {stats->tcpRxPackets = UNKNOWN;stats->tcpTxPackets = UNKNOWN;}if (fclose(fp) != 0) {return -1;}return 0;}

step 2 .检测是否有上下行数据的逻辑

先看下这个枚举,定义了流量的几种状态

public enum Activity {NONE,DATAIN, //下行DATAOUT, //上行DATAINANDOUT, //同时上行和下行DORMANT}

updateDataActivity该方法从3.1中获取到数据包大小,并与上一次检测的结果比较,进而判断是否有流量。

private void updateDataActivity() {long sent, received;DctConstants.Activity newActivity;TxRxSum preTxRxSum = new TxRxSum(mTxPkts, mRxPkts);TxRxSum curTxRxSum = new TxRxSum();curTxRxSum.updateTxRxSum();mTxPkts = curTxRxSum.txPkts;mRxPkts = curTxRxSum.rxPkts;if (VDBG) {log("updateDataActivity: curTxRxSum=" + curTxRxSum + " preTxRxSum=" + preTxRxSum);}if (mNetStatPollEnabled && (preTxRxSum.txPkts > 0 || preTxRxSum.rxPkts > 0)) {sent = mTxPkts - preTxRxSum.txPkts;received = mRxPkts - preTxRxSum.rxPkts;if (VDBG)log("updateDataActivity: sent=" + sent + " received=" + received);if (sent > 0 && received > 0) {newActivity = DctConstants.Activity.DATAINANDOUT;} else if (sent > 0 && received == 0) {newActivity = DctConstants.Activity.DATAOUT;} else if (sent == 0 && received > 0) {newActivity = DctConstants.Activity.DATAIN;} else {newActivity = (mActivity == DctConstants.Activity.DORMANT) ?mActivity : DctConstants.Activity.NONE;}if (mActivity != newActivity && mIsScreenOn) {if (VDBG)log("updateDataActivity: newActivity=" + newActivity);mActivity = newActivity;mPhone.notifyDataActivity();}}}

step 3. 通知监听者流量状态更新

public void notifyDataActivityForSubscriber(int subId, int state) {if (!checkNotifyPermission("notifyDataActivity()" )) {return;}synchronized (mRecords) {int phoneId = SubscriptionManager.getPhoneId(subId);if (validatePhoneId(phoneId)) {mDataActivity[phoneId] = state;for (Record r : mRecords) {// Notify by correct subId.if (r.matchPhoneStateListenerEvent(PhoneStateListener.LISTEN_DATA_ACTIVITY) &&idMatch(r.subId, subId, phoneId)) {try {r.callback.onDataActivity(state);} catch (RemoteException ex) {mRemoveList.add(r.binder);}}}}handleRemoveListLocked();}}

Step 4.通知RSSI状态更新

void setActivity(int activity) {mCurrentState.activityIn = activity == TelephonyManager.DATA_ACTIVITY_INOUT|| activity == TelephonyManager.DATA_ACTIVITY_IN;mCurrentState.activityOut = activity == TelephonyManager.DATA_ACTIVITY_INOUT|| activity == TelephonyManager.DATA_ACTIVITY_OUT;if (mConfig.readIconsFromXml) {mCurrentState.dataActivity = activity;}notifyListenersIfNecessary();}

相关章节:

一篇就够!全面&详细解析android APN

本文为原创,喜欢就点赞吧~

Android:全面详细的解析Android数据流量统计流程与分析方法(流量检测 流量监控 流量提示)相关类PhoneStateListener dcTracker TrafficStats

如果觉得《Android:全面详细的解析Android数据流量统计流程与分析方法(流量检测 流量监控 流》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。