昨晚梦见老家的那条小狗好几天没有回家,担心是不是被人偷去吃了,每天都有一些奇怪的梦…….
今天总结一下Android4.4 framework网络管理流程,同样,Android5.0这部分改动很大,在此先不介绍Android5.0的情况。
整体框架
图1. 结构框图
每一个网络都会实现一个NetworkStateTracker,例如WIFI对于的是WifiStateTracker,Ethernet对于的EthernetStateTracker。NetworkStateTracker在网络连接状态发生改变时,会发送消息EVENT_STATE_CHANGED给ConnectivityService。
同时,ConnectivityService可以透过NetworkStateTracker获得网络连接状态(getNetworkInfo)和网络连接信息(getLinkProperties)。
此外,NetworkStateTracker还会向NetworkManagerService注册一个BaseNetworkObserver,用于获取内核发上来的uevent事件,包括interfaceAdded(网口添加)、interfaceRemoved(网口移除)、interfaceLinkStateChanged(网线link up/down)、interfaceStatusChanged(网口up/down)等事件。
ConnectivityService会设置系统的路由和dns,NetworkManagerService是netd进程与SystemServer进程通讯的枢纽,NetworkManagerService在设置路由或者dns时,会通过socket发送给netd,netd再将其设置到kernel中去,同样,kernel有网络事件是通过netlink发送到netd,netd经socket通知到NetworkManagerService,NetworkManagerService再分发到每个NetworkStateTracker。
网络状态管理
NetworkStateTracker将自己的状态发送给ConnectivityService,由它来统一管理,原生Android4.4只允许一个default型网络存在,通过修改framework可以做到WIFI Ethernet同时连接,这点后面再介绍;
ConntivityService中的NetworkStateTrackerHandler接收发自NetworkStateTracker发过来的message,判断当前该网络的状态,不同的状态做不同的处理。
if (info.getDetailedState() ==
NetworkInfo.DetailedState.FAILED) { /* 连接失败 */
handleConnectionFailure(info);
} else if (state == NetworkInfo.State.DISCONNECTED) { /* 断开连接 */
handleDisconnect(info);
} else if (state == NetworkInfo.State.CONNECTED) { /* 已连接 */
handleConnect(info);
}
某一个网络(thisNet)已连接后,先判断此前是否已经有网络(otherNet)连接上了,如果有,则判断preNetType和newNetType谁的优先级高,teardown掉优先级低的网络。
if (isNewNetTypePreferredOverCurrentNetType(newNetType)) {
teardown(otherNet)
} else {
teardown(thisNet)
}
如何判断优先级?对比定义在frameworks\base\core\res\res\values\config.xml中的networkAttributes,谁的priority数值大则谁的优先级高。
<string-array translatable="false" name="networkAttributes">
<item>"wifi,1,1,1,-1,true"</item>
<item>"bluetooth,7,7,1,60000,true"</item>
<item>"ethernet,9,9,2,-1,true"</item>
</string-array>
很明显上面三个网络的优先级是ethernet > bluetooth > wifi;
还有一个地方会影响到网络优先级评判的,那就是frameworks\base\packages\SettingsProvider\res\values\defaults.xml中所定义的
<integer name="def_network_preference">9</integer>
这个指定了网络首选项,设置成首选项的网络优先级总是高于其他网络的,这个设置网络首选项为ethernet,radio-type = 9。
被teardown的网络就断开连接了,但是值得注意的是teardown和disconnect是不一样的,teardown的时候会设置mTeardownRequested.set(true),这样的话只是临时断开连接,如果优先级高的网络断开连接后,ConnectivityService还会把它恢复回来,而disconnect是没有设置的,不允许被恢复连接。
当一个网络断开连接后,会先尝试恢复可以恢复网络。
不管是网络已连接(handleConnect)还是网络断开连接(handleDisconnect),都会调用handleConnectivityChange去设置系统的route和dns,最后发送ConnectivityManager.CONNECTIVITY_ACTION给感兴趣的应用程序。
网络配置管理
直接看handleConnectivityChange这个函数,handleConnectivityChange有两个工作,更新dns和更新路由表。
1) 更新dns
dns只有update操作,没有删除操作,所以当一个网络断开连接之后,handleDnsConfigurationChange里面是什么也不做的,当一个网络已连接之后,会先判断这个网络是否default型,什么是default型网络呢?default型网络就是固定的网络类型,如果config.xml中定义的”networkAttributes”和”radioAttributes”相等则是default型网络,可见常见的wifi和ethernet都是default,而vpn不是。
如果mNetConfigs[netType].isDefault()为真,则将这个网络的dns server设置到系统的默认dns。
updateDnsLocked(network, p.getInterfaceName(), dnses, p.getDomains(), true);
这个函数会做两件事,1、mNetd.setDnsServersForInterface,2、mNetd.setDefaultInterfaceForDns,当然也会将dns server设置到系统属性net.dns[n]中去,其实设置这个系统没什么用,一般情况下不会被使用到。
如果不设置到系统的默认dns,则使用下面的函数:
updateDnsLocked(network, p.getInterfaceName(), dnses, p.getDomains(), false);
这样就不会setDefaultInterfaceForDns,setDefaultInterfaceForDns才是设置系统dns的关键。
2) 更新route
更新route之前,先对比一下该网络类型上次的连接信息(curLp)和该次的连接信息(newLp):
LinkProperties curLp = mCurrentLinkProperties[netType];
LinkProperties newLp = mNetTrackers[netType].getLinkProperties();
注意:如果该网络在连接之前是断开的,则curLp = null,因为每次断开连接的时候都会把linkProperties清掉,如果网络连接状态没有改变,只是连接信息发生了改变,则curLp和newLp都可能非空。
比较curLp和newLp之后,添加路由:
for (RouteInfo r : routeDiff.added) {
if (isLinkDefault || ! r.isDefaultRoute()) {
addRoute(newLp, r, TO_DEFAULT_TABLE, exempt);
} else {
// add to a secondary route table
addRoute(newLp, r, TO_SECONDARY_TABLE, UNEXEMPT);
}
}
删除路由:
for (RouteInfo r : routeDiff.removed) {
if (isLinkDefault || ! r.isDefaultRoute()) {
if (VDBG) log("updateRoutes: default remove route r=" + r);
removeRoute(curLp, r, TO_DEFAULT_TABLE);
}
if (isLinkDefault == false) {
// remove from a secondary route table
removeRoute(curLp, r, TO_SECONDARY_TABLE);
}
}
isLinkDefault = mNetConfigs[netType].isDefault(),可见default网络的全部路由,和非default网络的非默认路由都放在TO_DEFAULT_TABLE中。
总结
最后来画一个图吧……
图2. 流程图