浅谈framework之ConnectivityService

文章目录

  • 1. 前言
  • 2. 启动服务
  • 3. 拨号过程
    • 3.1 动态ip拨号
    • 3.2 pppoe拨号
    • 3.3 静态ip拨号
  • 4. 总结

1. 前言

这篇文章,主要是对之前学习ConnectivityService的再一次回顾与加深理解。

2. 启动服务

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);
               }
    }

3. 拨号过程

在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)。

3.1 动态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;
    }

3.2 pppoe拨号

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);
    ......
   
}

3.3 静态ip拨号

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;
}

可以看到也是与上面类似的方式启动服务

4. 总结

这里主要是讲以太网相关的内容,其实里面管理了众多的连接服务,比如说wifi,蓝牙,移动数据连接等,但这里就不再引申了。

你可能感兴趣的:(android系统定制方案)