最近在android4.4上边移植以太网遇到一个问题,手动设置ip的时候dns无法保存,平台telechip tcc8935
相关的代码目录:
v14.04_r1_tcc-android-4.4.2/frameworks/base/services/java/com/android/server/ConnectivityService.java
v14.04_r1_tcc-android-4.4.2/frameworks/base/services/java/com/android/server/EthernetService.java
v14.04_r1_tcc-android-4.4.2/frameworks/base/core/java/android/net/ConnectivityManager.java
v14.04_r1_tcc-android-4.4.2/device/telechips/m805_893x/overlay/frameworks/base/core/res/res/values/config.xml
各种不同类型的网络是在文件config.xml中进行配置,config.xml的路径如下:\android\frameworks\base\core\res\res\values 其中网络配置的内容如下:
01.
<string-array translatable=
"false"
name=
"networkAttributes"
>
02.
<item>
"wifi,1,1,1,-1,true"
</item>
03.
<item>
"ethernet,9,9,0,-1,true"
</item>
04.
<item>
"mobile,0,0,0,-1,true"
</item>
05.
<item>
"mobile_mms,2,0,2,60000,true"
</item>
06.
<item>
"mobile_supl,3,0,2,60000,true"
</item>
07.
<item>
"mobile_hipri,5,0,3,60000,true"
</item>
08.
<item>
"mobile_fota,10,0,2,60000,true"
</item>
09.
<item>
"mobile_ims,11,0,2,60000,true"
</item>
10.
<item>
"mobile_cbs,12,0,2,60000,true"
</item>
11.
<item>
"wifi_p2p,13,1,0,-1,true"
</item>
12.
</string-array>
上面各项字符串的排列顺序与类NetworkConfig的成员变量一一对应,按顺序如下:
name,type,radio,priority,restoreTime,dependencyMet
第4个元素就是我们需要的优先级设置值
在上面的内容中,我们在配置文件中设置了不同网络类型的优先级。那么配置文件里面的设置是在什么时候被系统读取使用的呢?通过查看源代码会发现是在类ConnectivityService的构造函数中被调用的。具体代码如下:
view sourceprint?
01.
String[] naStrings = context.getResources().getStringArray(
02.
com.android.internal.R.array.networkAttributes);
03.
for
(String naString : naStrings) {
04.
try
{
05.
NetworkConfig n =
new
NetworkConfig(naString);
06.
if
(n.type > ConnectivityManager.MAX_NETWORK_TYPE) {
07.
loge(
"Error in networkAttributes - ignoring attempt to define type "
+
08.
n.type);
09.
continue
;
10.
}
11.
if
(mNetConfigs[n.type] !=
null
) {
12.
loge(
"Error in networkAttributes - ignoring attempt to redefine type "
+
13.
n.type);
14.
continue
;
15.
}
16.
if
((mRadioAttributes[n.radio] ==
null
) && (n.type != ConnectivityManager.TYPE_ETHERNET)) {
17.
loge(
"Error in networkAttributes - ignoring attempt to use undefined "
+
18.
"radio "
+ n.radio +
" in network type "
+ n.type);
19.
continue
;
20.
}
21.
mNetConfigs[n.type] = n;
22.
mNetworksDefined++;
23.
}
catch
(Exception e) {
24.
// ignore it - leave the entry null
25.
}
26.
}
首先获取config.xml中的字符串保存在String数组naStrings 中,然后循环处理数组中的每个元素(如"wifi,1,1,1,-1,true")。之前我们说过,每个元素中的各项跟类NetworkConfig的成员变量对应。这里的处理主要是在声明一个NetworkConfig对象时把每个String元素作为参数,最后把每个NetworkConfig对象加到数组mNetConfigs中。至此,有关各种网络的配置都保存在数组mNetConfigs里。接下来,看看mNetConfigs数组是在什么时候派上用场的?
通过阅读源代码,可以知道各种网络在启动的时候,会发出一个message(EVENT_STATE_CHANGED),这个消息会在ConnectivityService的内部类NetworkStateTrackerHandler中被处理。代码如下:
view sourceprint?
01.
private
class
NetworkStateTrackerHandler
extends
Handler {
02.
......
03.
@Override
04.
public
void
handleMessage(Message msg) {
05.
NetworkInfo info;
06.
switch
(msg.what) {
07.
case
NetworkStateTracker.EVENT_STATE_CHANGED:
08.
......
09.
if
(info.getDetailedState() ==
10.
NetworkInfo.DetailedState.FAILED) {
11.
handleConnectionFailure(info);
12.
}
else
if
(info.getDetailedState() ==
13.
DetailedState.CAPTIVE_PORTAL_CHECK) {
14.
handleCaptivePortalTrackerCheck(info);
15.
}
else
if
(state == NetworkInfo.State.DISCONNECTED) {
16.
handleDisconnect(info);
17.
}
else
if
(state == NetworkInfo.State.SUSPENDED) {
18.
handleDisconnect(info);
19.
}
else
if
(state == NetworkInfo.State.CONNECTED) {
20.
handleConnect(info);
21.
}
22.
......
当网络启动时,最终会调用到函数handleConnect,下面看看handleConnect具体实现:
view sourceprint?
01.
if
(mNetConfigs[newNetType].isDefault()) {
02.
if
(mActiveDefaultNetwork != -
1
&& mActiveDefaultNetwork != newNetType) {
03.
if
(isNewNetTypePreferredOverCurrentNetType(newNetType)) {
04.
// tear down the other
05.
NetworkStateTracker otherNet =
06.
mNetTrackers[mActiveDefaultNetwork];
07.
if
(DBG) {
08.
log(
"Policy requires "
+ otherNet.getNetworkInfo().getTypeName() +
09.
" teardown"
);
10.
}
11.
if
(!teardown(otherNet)) {
12.
loge(
"Network declined teardown request"
);
13.
teardown(thisNet);
14.
return
;
15.
}
16.
}
else
{
17.
// don't accept this one
18.
if
(VDBG) {
19.
log(
"Not broadcasting CONNECT_ACTION "
+
20.
"to torn down network "
+ info.getTypeName());
21.
}
22.
teardown(thisNet);
23.
return
;
24.
}
25.
}
26.
......
27.
mActiveDefaultNetwork = newNetType;
第一个if是判断网络是不是默认路由的,上文提到的config.xml中wifi、Ethernet、mobile都是默认路由的。第二个if是根据变量mActiveDefaultNetwork 来判断是否有其他网络正在运行。如果有的话,就对两种网络的优先级进行比较。优先比较是通过函数isNewNetTypePreferredOverCurrentNetType来实现的。如果返回false则不连接网络并返回。如果返回true则断开当前网络,并把newNetType赋给mActiveDefaultNetwork,确保之后的网络操作使用的是新的网络连接。
到这里已经完成了网络切换的流程。下面分析函数isNewNetTypePreferredOverCurrentNetType,代码如下:
view sourceprint?
1.
private
boolean
isNewNetTypePreferredOverCurrentNetType(
int
type) {
2.
if
((type != mNetworkPreference &&
3.
mNetConfigs[mActiveDefaultNetwork].priority >
4.
mNetConfigs[type].priority) ||
5.
mNetworkPreference == mActiveDefaultNetwork)
return
false
;
6.
return
true
;
7.
}
函数isNewNetTypePreferredOverCurrentNetType是判断是否切换为新启动的网络。从函数代码可以发现,变量mNetworkPreference 起到了很关键的作用,因为当mActiveDefaultNetwork等于mNetworkPreference时,不管优先级怎样,函数直接返回false。所以必须搞清楚mNetworkPreference的值到底是什么?
通过查找,知道mNetworkPreference是在ConnectivityService的构造函数中初始化的。
mNetworkPreference = getPersistedNetworkPreference();
下面看一下函数getPersistedNetworkPreference返回的是什么值?
.
private
int
getPersistedNetworkPreference() {
02.
final
ContentResolver cr = mContext.getContentResolver();
03.
04.
final
int
networkPrefSetting = Settings.Global
05.
.getInt(cr, Settings.Global.NETWORK_PREFERENCE, -
1
);
06.
if
(networkPrefSetting != -
1
) {
07.
return
networkPrefSetting;
08.
}
09.
10.
return
ConnectivityManager.DEFAULT_NETWORK_PREFERENCE;
11.
}
第1、2句是获取ContentProvider与Settings.Global.NETWORK_PREFERENCE对应的值。该ContentProvider是在类DataBaseHelper中初始化,其中有一句关键的代码
1.
db.execSQL(
"INSERT INTO system ('name', 'value') values ('network_preference', '"
+
2.
ConnectivityManager.DEFAULT_NETWORK_PREFERENCE +
"')"
);
可见其实保存的也是ConnectivityManager.DEFAULT_NETWORK_PREFERENCE的值。至此,可以明确一点,那就是
mNetworkPreference = ConnectivityManager.DEFAULT_NETWORK_PREFERENCE
上面我们提到的变量mNetworkPreference除了在初始化的时候会改变它的值,还有一个函数也能改变它的值。这个函数是ConnectivityService中的setNetworkPreference。查看源代码可以发现,DataCommand和WifiCommand都调用了ConnectivityService中的这个函数。而DataCommand和WifiCommand是在Svc中初始化的。看到这里,大概可以猜到通过Android的svc命令能够实现上述两个类定义的功能。其中,DataCommand实现对移动网络的开启、关闭和优先选择设置,WifiCommand实现对wifi网络的开启、关闭和优先选择设置。下面看一下命令svc的使用方法:
01.
# svc data
02.
svc data
03.
Control mobile data connectivity
04.
usage: svc data [enable|disable]
05.
Turn mobile data on or off.
06.
//设置移动网络的数据是否启用
07.
svc data prefer
08.
Set mobile as the preferred data network
09.
//设置移动网络的数据优先于WIFI
10.
11.
# svc wifi
12.
svc wifi
13.
Control the Wi-Fi manager
14.
usage: svc wifi [enable|disable]
15.
Turn Wi-Fi on or off.
//设置WIFI是否启用
16.
svc wifi prefer
17.
Set Wi-Fi as the preferred data network
//设置WIFI优先于移动网络的数据,一般应设置成这样,除非你刻意使用移动网络数据传输
svc data prefer最终会改变mNetworkPreference的值。
//以上为优先级文件
//静态获取ip
以太网文件
v14.04_r1_tcc-android-4.4.2/frameworks/base/ethernet/java/android/net/ethernet/EthernetStateTracker.java
在EthernetStateTracker.java的private boolean configureInterface(EthernetDevInfo info) throws UnknownHostException {中
if (info.getConnectMode().equals(EthernetDevInfo.ETH_CONN_MODE_DHCP)) {//动态获取ip地址
if(LOCAL_LOG) Log.i(TAG, "trigger dhcp for device " + info.getIfName());
mDhcpTarget.sendEmptyMessage(EVENT_DHCP_START);
} else {//手动获取ip地址
int event;
mStartingDhcp = false;
mDhcpInfo.ipAddress = stringToIpAddr(info.getIpAddress());
mDhcpInfo.gateway = stringToIpAddr(info.getRouteAddr());
//mDhcpInfo.netmask = stringToIpAddr(info.getNetMask());
mDhcpInfo.netmask = NetworkUtils.netmaskIntToPrefixLength(stringToIpAddr(info.getNetMask()));
mDhcpInfo.dns1 = stringToIpAddr(info.getDnsAddr());
mDhcpInfo.dns2 = 0;
if(LOCAL_LOG) Log.i(TAG, "set ip manually " + mDhcpInfo.toString();
public void notifyStateChange(String ifname,DetailedState state) {//状态改变的函数,更新网络状态