每篇一格言
——Steve Jobs
下面我们从任务流的角度,具体分析流量检测的流程。
因而角色关系变成了下图
为了阅读更清晰,涉及到源码的部分,只贴出关键源码,省略细节。
dcTracker创建如下线程检测流量变化
private final Runnable mPollNetStat = new Runnable() {
@Override
public void run() {
updateDataActivity();
。。。
};
这里的updateDataActivity主要做两件事:
1.检测发送包(tx)和接收包(rx)的变化,分别调用
TrafficStats.getMobileTxPackets()
TrafficStats.getMobileRxPackets()
2.通知phone有数据包变化
TrafficStats与NetworkStatsService提供java层数据流量接口,担当传递任务的角色,略。
下面是统计数据流量的方法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;
}
}
}
}
。。。
}
GsmCdmaPhone,DefaultPhoneNotifier,TelephonyRegistry,担当传递任务的角色,略。
下面重点看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,通知RSSI刷新。
public void onDataActivity(int direction) {
if (DEBUG) {
Log.d(mTag, "onDataActivity: direction=" + direction);
}
setActivity(direction);
}
上面我们已经从任务流的角度分析了android的流量检测流程。
下面从各个类的角色定位的角度作个总结,如下图所示:
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更新流量状态。
(高通平台)QXDM log PDCP; (MTK平台)ELT log
logcat radio log system log;
TCP dump
TCP dump抓取方法:
adb root
adb shell tcpdump -i any -s 0 -w /data/tcpdump.pcap
adb pull /data/tcpdump.pcap
QXDM、QCAT、WIRESHARK
见下面的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;
}
先看下这个枚举,定义了流量的几种状态
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();
}
}
}
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();
}
}
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();
}
相关章节: