这篇文章,主要是对之前学习ConnectivityService的再一次回顾与加深理解。
frameworks/base/services/java/com/android/server/SystemServer.java
在判断网络可用的情况下,开启各个网络相关的服务,其中就有ConnectivityService:
if (!disableNetwork) {
......//省略无关代码
try {
Slog.i(TAG, "Connectivity Service");
connectivity = new ConnectivityService(
context, networkManagement, networkStats, networkPolicy);
ServiceManager.addService(Context.CONNECTIVITY_SERVICE, connectivity);
} catch (Throwable e) {
reportWtf("starting Connectivity Service", e);
}
}
在ConnectivityService的构造函数中对已支持的网络接口进行优先级排序:
frameworks/base/services/java/com/android/server/ConnectivityService.java
// high priority first
mPriorityList = new int[mNetworksDefined];
{
int insertionPoint = mNetworksDefined-1;
int currentLowest = 0;
int nextLowest = 0;
while (insertionPoint > -1) {
for (NetworkConfig na : mNetConfigs) {
if (na == null) continue;
if (na.priority < currentLowest) continue;
if (na.priority > currentLowest) {
if (na.priority < nextLowest || nextLowest == 0) {
nextLowest = na.priority;
}
continue;
}
mPriorityList[insertionPoint--] = na.type;
}
currentLowest = nextLowest;
nextLowest = 0;
}
}
然后循环开启各个追踪器,并在createTracker中开启了EthernetDataTracker:
// Create and start trackers for hard-coded networks
for (int targetNetworkType : mPriorityList) {
final NetworkConfig config = mNetConfigs[targetNetworkType];
final NetworkStateTracker tracker;
try {
Slog.e(TAG,"nettype =" + targetNetworkType + ",priority= " + mNetConfigs[targetNetworkType].priority);
tracker = netFactory.createTracker(targetNetworkType, config);
mNetTrackers[targetNetworkType] = tracker;
} catch (IllegalArgumentException e) {
Slog.e(TAG, "Problem creating " + getNetworkTypeName(targetNetworkType)
+ " tracker: " + e);
continue;
}
tracker.startMonitoring(context, mTrackerHandler);
if (config.isDefault()) {
tracker.reconnect(); //1
}
}
看一下createTracker函数的实现,这里只看以太网类型的:
public NetworkStateTracker createTracker(int targetNetworkType, NetworkConfig config) {
switch (config.radio) {
......
case TYPE_ETHERNET:
return EthernetDataTracker.getInstance();
}
}
我们再回到注释1处,可以看到它调用了tracker的reconnect函数,并且由上可知tracker实际是EthernetDataTracker对象,所以reconnect实现如下(代码有删减,以方便理解):
frameworks/base/core/java/android/net/EthernetDataTracker.java
public boolean reconnect() {
String connectMode = mEthernetManager.getEthernetMode();
......
if(EthernetManager.ETHERNET_CONNECT_MODE_MANUAL.equals(connectMode)) {
configureInterface();
} else if(EthernetManager.ETHERNET_CONNECT_MODE_DHCP.equals(connectMode)) {
runDhcp();
} else if(EthernetManager.ETHERNET_CONNECT_MODE_PPPOE.equals(connectMode)) {
if(PppoeManager.PPPOE_STATE_CONNECTING != mConnectStatus) {
runPppoe();
}
else{
return false;
}
}else if(EthernetManager.ETHERNET_CONNECT_MODE_NONE.equals(connectMode)){
return false;
}else {
return false;
}
return true;
}
上面的代码主要是从setting数据库中获取系统属性,看网络连接方式是哪种模式,然后再根据不同的连接模式选择runDhcp(决定启动DHCP或IPOE),runPppoe(决定启动PPPOE),configureInterface(决定启动静态ip)。
我们跟踪runDhcp函数的实现,代码有删减,红框框柱的代码里我们调用native函数runDhcpPlus:
private void runDhcp() {
Thread dhcpThread = new Thread(new Runnable() {
public void run() {
synchronized(mLock){
DoubleStackCtrFlag = false;
mConnectStatus = EthernetManager.ETHERNET_CONNECT_STATE_CONNECTING;
DhcpResults dhcpResults = new DhcpResults();
int option60 = mEthernetManager.getDhcpOption60State();
String login = mEthernetManager.getDhcpOption60Login();
String password = mEthernetManager.getDhcpOption60Password();
int option125 = mEthernetManager.getDhcpOption125State();
String option125Info = mEthernetManager.getDhcpOption125Info();
if(mTempIpoeUserName != null && mTempIpoePassword != null) {
//run ipoe once
option60 = EthernetManager.OPTION60_STATE_ENABLED;
login = mTempIpoeUserName;
password = mTempIpoePassword;
setTempIpoeInfo(null, null);
} else if(mTempIpoeUserName != null) {
//run dhcp once
option60 = EthernetManager.OPTION60_STATE_DISABLED;
option125 = EthernetManager.OPTION125_STATE_DISABLED;
setTempIpoeInfo(null, null);
}
if(!NetworkUtils.runDhcpPlus(mIface, dhcpResults, option60, login, password, option125, option125Info)){
......
}
});
dhcpThread.start();
}
这里需要注意的是我们通过option60和option125字段判断是启动DHCP还是IPOE,当它们为0的时候,启动的是DHCP,当它们为1的时候启动的是IPOE。runDhcpPlus函数是一个native代码,实现代码位于:
frameworks/base/core/jni/android_net_NetUtils.cpp
static JNINativeMethod gNetworkUtilMethods[] = {
......
{ "runDhcpPlus", "(Ljava/lang/String;Landroid/net/DhcpResults;ILjava/lang/String;Ljava/lang/String;ILjava/lang/String;)Z", (void *)android_net_utils_runDhcp },
......
};
由jni动态注册原理,c代码中的android_net_utils_runDhcp函数对应着java端的runDhcpPlus函数,继续往下走:
static jboolean android_net_utils_runDhcp(JNIEnv* env, jobject clazz, jstring ifname, jobject info,
jint option60, jstring Login, jstring Password, jint option125, jstring option125Info)
{
return android_net_utils_runDhcpCommon(env, clazz, ifname, info, false,
option60, Login, Password, option125, option125Info);
}
然后走到android_net_utils_runDhcpCommon函数中,这里需要注意第五个参数为false,我们继续往下跟:
static jboolean android_net_utils_runDhcpCommon(JNIEnv* env, jobject clazz, jstring ifname,
jobject dhcpResults, bool renew, jint option60, jstring Login, jstring Password,
jint option125, jstring option125Info)
{
......
if (renew) {
result = ::dhcp_do_request_renew(nameStr, ipaddr, gateway, &prefixLength,
dns, server, &lease, vendorInfo, domains, mtu);
} else {
result = ::dhcp_do_request(nameStr, ipaddr, gateway, &prefixLength,
dns, server, &lease, vendorInfo, domains, mtu,
option60Support, LoginStr, PasswordStr, option125Support, option125Str);
}
......
return (jboolean)(result == 0);
}
由于renew为false,我们就会直接走红框里面的代码,在这里我们获取到ip地址等信息,然后再下面代码中会将ip地址等变量通过调用java端的函数,设入到java端的系统变量中以供java层代码调用。例如ip地址设置:
result = env->CallBooleanMethod(dhcpResults, dhcpResultsFieldIds.addLinkAddress,
env->NewStringUTF(ipaddr), prefixLength);
现在接着往下分析dhcp_do_request函数的实现:
system/core/libnetutils/dhcp_utils.c
/*
* Start the dhcp client daemon, and wait for it to finish
* configuring the interface.
*
* The device init.rc file needs a corresponding entry for this work.
*
* Example:
* service dhcpcd_ /system/bin/dhcpcd -ABKL -f dhcpcd.conf
*/
int dhcp_do_request(const char *interface,
char *ipaddr,
char *gateway,
uint32_t *prefixLength,
char *dns[],
char *server,
uint32_t *lease,
char *vendorInfo,
char *domain,
char *mtu,
bool supportOption60,
const char *login,
const char *password,
bool supportOption125,
const char *option125Info)
{
......
}
该接口主要做了以下几个工作:
a. 启动DHCP服务,并等待直至DHCP准备OK
property_set(ctrl_prop, daemon_cmd);
ALOGD("daemon_cmd:%s\n", daemon_cmd);
if (wait_for_property(daemon_prop_name, desired_status, 10) < 0) {
snprintf(errmsg, sizeof(errmsg), "%s", "Timed out waiting for dhcpcd to start");
return -1;
}
b. 等待DHCP服务端返回一个结果
if (!property_get(result_prop_name, prop_value, NULL)) {
/* shouldn't ever happen, given the success of wait_for_property() */
snprintf(errmsg, sizeof(errmsg), "%s", "DHCP result property was not set");
//dhcp_stop(interface);
return -1;
}
c. 如果DHCP返回的结果状态值为OK,则从DHCP服务端设置的全局属性中读取ip地址、网关等信息
if (strcmp(prop_value, "ok") == 0) {
char dns_prop_name[PROPERTY_KEY_MAX];
if (fill_ip_info(interface, ipaddr, gateway, prefixLength, dns,
server, lease, vendorInfo, domain, mtu) == -1) {
//dhcp_stop(interface);
return -1;
}
return 0;
} else {
snprintf(errmsg, sizeof(errmsg), "DHCP result was %s", prop_value);
//dhcp_stop(interface);
return -1;
}
runPppoe函数实现如下,代码有删减:
private synchronized void runPppoe(){
Thread pppoeThread = new Thread(new Runnable() {
public void run() {
synchronized(mLock){
......
PppoeNative.connectPppoe(mIface, userName, userPass);
......
}
});
pppoeThread.start();
}
connectPppoe函数实现代码在native层:
frameworks/base/core/jni/android_net_pppoe.cpp
static void android_net_connectPppoe(JNIEnv * env, jobject clazz, jstring ifname, jstring user, jstring passwd)
{
const char *myIfname = env->GetStringUTFChars(ifname, NULL);
const char *myUser = env->GetStringUTFChars(user, NULL);
const char *myPasswd = env->GetStringUTFChars(passwd, NULL);
pppoe_connect(myIfname, myUser, myPasswd);
env->ReleaseStringUTFChars(ifname, myIfname);
env->ReleaseStringUTFChars(user, myUser);
env->ReleaseStringUTFChars(passwd, myPasswd);
}
pppoe_connect函数的实现是在system/core/libnetutils/pppoe_utils.c。
同样的是通过property_set启动pppoe服务
void pppoe_connect(const char *ifname, const char *user, const char *passwd)
{
......
property_set(ctrl_prop, daemon_cmd);
......
}
configureInterface函数中主要看runCheckIpConflict函数的实现:
private void configureInterface() {
......
runCheckIpConflict();
......
}
继续往下:
private void runCheckIpConflict() {
Thread ipConflictThread = new Thread(new Runnable() {
public void run() {
DhcpInfo dhcpInfo = mEthernetManager.getSavedEthernetIpInfo();
int result;
String action = "android.net.conn.IP_ADDRESS_CONFLICTED";
if (dhcpInfo == null) {
Log.i(TAG, "dhcpinfo null, don't ip conflict check");
} else {
try {
result = NetworkUtils.checkIpConflict(mIface, NetworkUtils.intToInetAddress(dhcpInfo.ipAddress).toString());
} catch (Exception e) {
Log.e(TAG, "runCheckIpConflict intToInetAddress error");
result = 0;
}
if (result == 1) {
Log.i(TAG, "ip conflict");
postNotification(action, null, 0,
null, null);
}else
Log.i(TAG, "ip not conflict");
}
}
});
ipConflictThread.start();
}
它调用了native层的checkIpConflict函数,用来检查ip设置是否冲突,跟踪checkIpConflict函数的实现代码:
frameworks/base/core/jni/android_net_NetUtils.cpp
static jint android_net_utils_checkIpConflict(JNIEnv* env, jobject clazz, jstring ifname, jstring ipaddr)
{
int result;
const char *nameStr = env->GetStringUTFChars(ifname, NULL);
const char *ipAddr = env->GetStringUTFChars(ipaddr, NULL);
result = ::check_ip_conflict(nameStr, ipAddr);
env->ReleaseStringUTFChars(ifname, nameStr);
env->ReleaseStringUTFChars(ipaddr, ipAddr);
return (jint)result;
}
system/core/libnetutils/dhcp_utils.c
int check_ip_conflict(const char *interface, const char *ipaddr)
{
const char *ctl_start = "ctl.start";
char prop_ipcflt_start[PROPERTY_VALUE_MAX];
char prop_ipcflt_result[PROPERTY_KEY_MAX];
char value[PROPERTY_VALUE_MAX];
struct in_addr ip;
char *ipstr;
memset(value, 0, PROPERTY_VALUE_MAX);
snprintf(prop_ipcflt_start, sizeof(prop_ipcflt_start),
"init.svc.%s_%s:", "ipconflict", interface);
if (!strcmp(value, "running")) {
snprintf(errmsg, sizeof(errmsg), "%s", "ipconflict is running now");
return -1;
}
memset(value, 0, PROPERTY_VALUE_MAX);
snprintf(prop_ipcflt_result, sizeof(prop_ipcflt_result),
"dhcp.%s.%s.result", "ipconflict", interface);
property_set(prop_ipcflt_result, "");
ipstr = (char *)ipaddr;
if (*ipaddr == '/')
ipstr = (char*)ipaddr + 1;
snprintf(prop_ipcflt_start, sizeof(prop_ipcflt_start),
"%s_%s:%s %s", "ipconflict",
interface, interface, ipstr);
property_set(ctl_start, prop_ipcflt_start);
/* Wait for the daemon to return a result */
if (wait_for_property(prop_ipcflt_result, NULL, 15) < 0) {
snprintf(errmsg, sizeof(errmsg), "%s", "Timed out waiting for ipconflict finish");
return -1;
}
if (!property_get(prop_ipcflt_result, value, NULL)) {
/* shouldn't ever happen, given the success of wait_for_property() */
snprintf(errmsg, sizeof(errmsg), "%s", "ipconflict result property was not set");
return -1;
}
if (inet_pton(AF_INET, value, &ip))
return 1;
return 0;
}
可以看到也是与上面类似的方式启动服务
这里主要是讲以太网相关的内容,其实里面管理了众多的连接服务,比如说wifi,蓝牙,移动数据连接等,但这里就不再引申了。