Android数据流量的统计流程与分析方法
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
(高通平台)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
3.重要代码
Step 1. 统计数据包
见下面的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();
}
关系图
待续。
本文为博主原创,可尽情点赞、留言与讨论。
上一篇: windows核心编程---内核对象
下一篇: spark数据流的合并与分支