Android4.4 以太网和DHCP启动过程介绍

转自:http://blog.csdn.net/wlwl0071986/article/details/51451843

Android4.4已经加入了以太网的支持。现在对以太网的初始化流程、网络策略配置、dhcp交互过程等做一些简单的介绍。

一、以太网启动流程

1. 创建ConnectivityService

SystemServer.Java

// networkmanagement、networkStats、networkPolicy已经提前创建好,并作为参数传入

connectivity = new ConnectivityService(context,networkManagement,networkStats,networkPolicy);

2. 创建NetworkStateTracker和RadioAttributes

ConnectivityService.java

//会创建多个Tracker,包括wifi、bluetooth、mobile、ethernet等

mNetTrackers = new NetworkStateTracker[ConnectivityManager.MAX_NETWORK_TYPE+1];

mRadioAttributes = new RadioAttributes[ConnectivityManager.MAX_RADIO_TYPE+1];

3. 加载网络策略配置属性

String[] raStrings = context.getResources().getStringArray(
                com.Android.internal.R.array.radioAttributes);
        for (String raString : raStrings) {
            RadioAttributes r = new RadioAttributes(raString);
            if (VDBG) log("raString=" + raString + " r=" + r);
            if (r.mType > ConnectivityManager.MAX_RADIO_TYPE) {
                loge("Error in radioAttributes - ignoring attempt to define type " + r.mType);
                continue;
            }
            if (mRadioAttributes[r.mType] != null) {
                loge("Error in radioAttributes - ignoring attempt to redefine type " +
                        r.mType);
                continue;
            }
            mRadioAttributes[r.mType] = r;
        }

4. 对已支持的网络接口进行优先级排序

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

5. 启动已支持网络接口的各个tracker

for (int targetNetworkType : mPriorityList) {
            final NetworkConfig config = mNetConfigs[targetNetworkType];
            final NetworkStateTracker tracker;
            try {
                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();
            }
        }

6. 启动配置文件指定的以太网接口

sIfaceMatch = context.getResources().getString(com.android.internal.R.string.config_ethernet_iface_regex);

final String[] ifaces = mNMService.listInterfaces();

// 如果指定的接口存在于网络服务接口列表中,则通过该接口连接网络

if (iface.matches(sIfaceMatch)) {

...

reconnect();

7. 启动DHCP服务

public boolean reconnect() {
        if (mLinkUp) {
            mTeardownRequested.set(false);
            runDhcp();
        }
        return mLinkUp;
    }

private void runDhcp() {
        Thread dhcpThread = new Thread(new Runnable() {
            public void run() {
                DhcpResults dhcpResults = new DhcpResults();
                if (!NetworkUtils.runDhcp(mIface, dhcpResults)) {

// 如果启动DHCP服务失败,则什么都不做退出
                    Log.e(TAG, "DHCP request error:" + NetworkUtils.getDhcpError());
                    return;
                }

// 如果启动DHCP服务成功,则设置对应的连接状态,并通知其他网络服务模块更新状态
                mLinkProperties = dhcpResults.linkProperties;

mNetworkInfo.setIsAvailable(true);
                mNetworkInfo.setDetailedState(DetailedState.CONNECTED, null, mHwAddr);
                Message msg = mCsHandler.obtainMessage(EVENT_STATE_CHANGED, mNetworkInfo);
                msg.sendToTarget();
            }
        });
        dhcpThread.start();
    }

8. 以太网的网络策略配置

frameworks\base\core\res\res\values-large\config.xml

// 设置以太网的优先级,第四列数值越大、优先级越高

<string-array translatable="false" name="networkAttributes">
        <item>"wifi,1,1,2,-1,true"</item>
        <item>"bluetooth,7,7,0,-1,true"</item>
        <item>"ethernet,9,9,9,-1,true"</item>
    </string-array>

// 增加允许连接的网络类型,9表示以太网接口
    <string-array translatable="false" name="radioAttributes">
        <item>"1,1"</item>
        <item>"7,1"</item>
        <item>"9,1"</item>
    </string-array>

// 修改init.rc,增加以太网的service

service dhcpcd_eth0 /system/bin/dhcpcd -ABDHKLd
        class main
        disabled
        oneshot

二、DHCP启动过程

1. JNI层接口

Java层调用的NetworkUtils.runDhcp(mIface, dhcpResults)接口,在android_net_Netutils.cpp中定义

static jboolean android_net_utils_runDhcp(JNIEnv* env, jobject clazz, jstring ifname, jobject info)
        {
            return android_net_utils_runDhcpCommon(env, clazz, ifname, info, false);
        }

static jboolean android_net_utils_runDhcpCommon(JNIEnv* env, jobject clazz,

jstring ifname, jobject dhcpResults, bool renew)
        {

....

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

// 下面一段是将获取到的ip地址字段存储到JNI变量中供java层获取,此处省略

....

}

2. DHCP客户端(libnetutils)

dhcp_do_request接口定义在dhcp_utils.c中,该接口主要做了以下几个工作:

a. 启动DHCP服务,并等待直至DHCP准备OK

property_set(ctrl_prop, daemon_cmd);

wait_for_property(daemon_prop_name, desired_status, 10)

b. 等待DHCP服务端返回一个结果

wait_for_property(result_prop_name, NULL, 30)

c. 如果DHCP返回的结果状态值为OK,则从DHCP服务端设置的全局属性中读取ip地址、网关等信息

fill_ip_info(interface, ipaddr, gateway, prefixLength, dns, server, lease, vendorInfo, domain, mtu)

3. DHCP服务端(dhcpcd)

a. DHCP消息类型

DHCPDISCOVER

DHCPOFFER

DHCPREQUEST

DHCPDECLINE

DHCPACK

DHCPNACK

DHCPRELEASE

DHCPINFORM

b. DHCP的四步租约过程

DHCP租约过程就是DHCP客户机动态获取IP地址的过程。

①客户机请求IP(客户机发DHCPDISCOVER广播包)

当一个DHCP客户机启动时,会自动将自己的IP地址配置成0.0.0.0,由于使用0.0.0.0不能进行正常通

信,所以客户机就必须通过DHCP服务器来获取一个合法的地址。由于客户机不知道DHCP服务器的IP地址,

所以它使用0.0.0.0的地址作为源地址,使用UDP68端口作为源端口,使用255.255.255.255作为目标地址,使

用UDP67端口作为目的端口来广播请求IP地址信息。广播信息中包含了DHCP客户机的MAC地址和计算机

名,以便使DHCP服务器能确定是哪个客户机发送的请求。

②服务器响应(服务器发DHCPOFFER广播包)

当DHCP服务器接收到客户机请求IP地址的信息时,它就在自己的IP地址池中查找是否有合法的IP地址提

供给客户机。如果有,DHCP服务器就将此IP地址做上标记,加入到DHCPOFFER的消息中,然后DHCP服务器

就广播一则包括下列信息的DHCPOFFER消息:

DHCP客户机的MAC地址

DHCP服务器提供的合法IP地址

子网掩码

默认网关(路由)

租约的期限

DHCP服务器的IP地址

因为DHCP客户机还没有IP地址,所以DHCP服务器使用自己的IP地址作为源地址,使用UDP67端口作为源端

口,使用255.255.255.255作为目标地址,使用UDP68端口作为目的端口来广播DHCPOFFER信息。

③客户机选择IP(客户机发DHCPREQUEST广播包)

DHCP客户机从接收到的第一个DHCPOFFER消息中选择IP地址,发出IP地址的DHCP服务器将该地址保

留,这样该地址就不能提供给另一个DHCP客户机。当客户机从第一个DHCP服务器接收DHCPOFFER并选择IP

地址后,DHCP租约的第三过程发生。客户机将DHCPREQUEST消息广播到所有的DHCP服务器,表明它接受提

供的内容。DHCPREQUEST消息包括为该客户机提供IP配置的服务器的服务标识符(IP地址)。DHCP服务器

查看服务器标识符字段,以确定它自己是否被选择为指定的客户机提供IP地址,如果那些DHCPOFFER被拒

绝,则DHCP服务器会取消提供并保留其IP地址以用于下一个IP租约请求。

在客户机选择IP的过程中,虽然客户机选择了IP地址,但是还没有配置IP地址,而在一个网络中可能有几

个DHCP服务器,所以客户机仍然使用0.0.0.0的地址作为源地址,使用UDP68端口作为源端口,使用

255.255.255.255作为目标地址,使用UDP67端口作为目的端口来广播DHCPREQUEST信息。

④服务器确定租约(服务器发DHCPACK/DHCPNAK广播包)

DHCP服务器接收到DHCPREQUEST消息后,以DHCPACK消息的形式向客户机广播成功的确认,该消息包

含有IP地址的有效租约和其他可能配置的信息。虽然服务器确认了客户机的租约请求,但是客户机还没有收

到服务器的DHCPACK消息,所以服务器仍然使用自己的IP地址作为源地址,使用UDP67端口作为源端口,使

用255.255.255.255作为目标地址,使用UDP68端口作为目的端口来广播DHCPACK信息。当客户机收到

DHCPACK消息时,它就配置了IP地址,完成了TCP/IP的初始化。

如果DHCPREQUEST不成功,例如客户机试图租约先前的IP地址,但该IP地址不再可用,或者因为客户机

移到其他子网,该IP无效时,DHCP服务器将广播否定确认消息DHCPNAK。当客户机接收到不成功的确认时,

它将重新开始DHCP租约过程。

如果DHCP客户机无法找到DHCP服务器,它将从TCP/IP的B类网段169.254.0.0中挑选一个IP地址作为自

己的IP地址,继续每隔5分钟尝试与DHCP服务器进行通讯,一旦与DHCP服务器取得联系,则客户机放弃自

动配置的IP地址,而使用DHCP服务器分配的IP地址。

c. DHCP客户机向网络DHCP服务器租约IP地址的流程及实际log

dhcpcd[1544]: version 5.5.6 starting
        dhcpcd[1544]: get_duid: Permission denied
        dhcpcd[1544]: eth0: using ClientID 01:12:78:e4:0b:44:00
        dhcpcd[1544]: eth0: executing `/system/etc/dhcpcd/dhcpcd-run-hooks‘, reason PREINIT
        dhcpcd[1544]: eth0: reading lease `/data/misc/dhcp/dhcpcd-eth0.lease‘
        dhcpcd[1544]: eth0: rebinding lease of 192.168.4.164
        dhcpcd[1544]: eth0: sending REQUEST (xid 0x440be4), next in 3.54 seconds
        dhcpcd[1544]: eth0: sending REQUEST (xid 0x440be4), next in 7.63 seconds
        dhcpcd[1544]: eth0: NAK: from 192.168.4.1
        dhcpcd[1544]: eth0: executing `/system/etc/dhcpcd/dhcpcd-run-hooks‘, reason NAK
        dhcpcd[1544]: eth0: broadcasting for a lease
        dhcpcd[1544]: eth0: sending DISCOVER (xid 0x440be4), next in 4.97 seconds
        dhcpcd[1544]: eth0: offered 192.168.4.165 from 192.168.4.1
        dhcpcd[1544]: eth0: sending REQUEST (xid 0x440be4), next in 3.02 seconds
        dhcpcd[1544]: eth0: acknowledged 192.168.4.165 from 192.168.4.1
        dhcpcd[1544]: eth0: leased 192.168.4.165 for 7200 seconds
        dhcpcd[1544]: eth0: adding IP address 192.168.4.165/24
        dhcpcd[1544]: eth0: adding route to 192.168.4.0/24
        dhcpcd[1544]: eth0: adding default route via 192.168.4.1
        dhcpcd[1544]: eth0: writing lease `/data/misc/dhcp/dhcpcd-eth0.lease‘
        dhcpcd[1544]: eth0: executing `/system/etc/dhcpcd/dhcpcd-run-hooks‘, reason BOUND

d. 调试网络的常用命令

(1) netcfg
             netcfg   //查看ip情况
             netcfg eth0 up dhcp   //通过dhcp 自动获取ip和网关
 
        (2) ifconfig
             ifconfig eth0 192.168.8.81 up
             ifconfig eth0 192.168.8.81 netmask 255.255.255.0 up
 
        (3) gateway 配置
             route add default gw 192.168.0.1 dev eth0
 
        (4) dns 配置
             setprop net.dns1 192.168.8.11
             setprop net.dns2 147.11.100.30
 
        (5) mac adddr
             ifconfig eth0 hw ether 00:AA:BB:33:44:55

时间: 2025-01-05 02:22:54

Android4.4 以太网和DHCP启动过程介绍的相关文章

Android启动过程介绍

开机过程大致可以分为以下三个阶段 OS级别 由bootloader载入linux kernel后,kernel开始初始化, 并载入built-in的驱动程序.Kernel完成开机后,载入init process,切换至user-space后,结束kernel 的循序过程(sequence),进入排程模式(process scheduling). Android级别 /system/core/init/init.cpp 由init process 开始,读取init.rc,Native服务启动,并

Android内核开发:图解Android系统的启动过程

本文是<Android内核开发>系列的第六篇文章,前面的几篇文章介绍了Android内核开发相关的基础知识,包括:Android源码的下载.版本和分支介绍.编译和烧写等等,从本文起就要开始真正地进行Android内核的学习及实战了. 学习任何软硬件系统,研究系统启动过程都是一种非常有效地起步手段,搞Android内核开发也不例外.网上有很多文章对Android启动相关代码进行分析和走读,大家可以先搜索阅读一下,我个人更喜欢更加直观的方式去理解未知的东西,包括图.表.系统输出的log信息等等,因

Android4.4 framework分析——Zygote进程的启动过程

Android启动过程中的第一个进程init,在启动过程中会启动两个关键的系统服务进程ServiceManager和Zygote.本文要介绍的就是Zygote进程的启动,Zygote俗称孵化器,专门用于生产(启动)新的进程.Zygote是在Init.rc(aosp/system/core/rootdir)里描述并由init进程启动的.相关代码如下: service zygote /system/bin/app_process -Xzygote /system/bin --zygote --sta

0-Android应用程序的Activity启动过程简要介绍和学习计划

源码分析 之 Activity启动过程简要介绍和学习计划 来源: http://blog.csdn.net/luoshengyang/article/details/6685853 声明: RTFSC(Read the fucking source code)是Linus的名言,也是学习IT技术一个重要手段.学习android最好手段就是对android进行系统分析,关于android系统的源码,CSDN的老罗分析分析是最系统一个.但是老罗的分析源码的blog,有几点不够好: 1.废话太多,可能

Android应用程序的Activity启动过程简要介绍和学习计划

文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/6685853 在Android系统中,Activity和Service是应用程序的核心组件,它们以松藕合的方式组合在一起构成了一个完整的应用程序,这得益 于应用程序框架层提供了一套完整的机制来协助应用程序启动这些Activity和Service,以及提供Binder机制帮助它们相互间进行通信.在前 面的文章Android进程间通信(IPC)机制B

计算机启动过程详细介绍

全面认识计算机启动过程 首先让我们来了解一些基本概念.第一个是大家非常熟悉的BIOS(基本输入输出系统),BIOS是直接与硬件打交道的底层代码,它为操作系统提供了控制硬件设备的基本功能.BIOS包括有系统BIOS(即常说的主板BIOS).显卡BIOS和其它设备(例如IDE控制器.SCSI卡或网卡等)的BIOS,其中系统BIOS是本文要讨论的主角,因为计算机的启动过程正是在它的控制下进行的.BIOS一般被存放在ROM(只读存储芯片)之中,即使在关机或掉电以后,这些代码也不会消失. 第二个基本概念是

redis(一)内部机制的介绍和启动过程

redis(一)内部机制的介绍和启动过程 redis的基本介绍 redis服务端 redis客户端 redis的持久化 redis中的文件事件和时间时间 redis的启动过程 redis的基本介绍 redis是一种非关系型数据库,采用=key,value的形式来存储数据.key是二进制数据,对于value的数据类型,redis支持string.hash.list.set.sorted set五种类型.对于单个redis实例,内部使用多线程通信,但是对外采用RESP单线程通信协议,在TCP层通过二

【分析】dalvik虚拟机启动过程(二)

源码版本:Android-4.4.4_r2 提示:大部分分析直接注释在代码内. 接着上一篇[分析]dalvik虚拟机启动过程(一) JNI_CreateJavaVM函数调用dvmCreateJNIEnv创建JNIEnv后,接着又调用了dvmStartup函数初始化VM: /* * VM 初始化. * VM initialization. Pass in any options provided on the command line. * Do not pass in the class nam

【转载】简述Linux的启动过程

原文:简述Linux的启动过程 本文将简单介绍一下Linux的启动过程,希望对那些安装Linux的过程中遇到了问题的朋友有些帮助 声明:本人没用过UEFI模式和GPT分区格式,所有关于这两部分的内容都是网络上找的资料,仅供参考. 典型启动顺序 计算机通电后,CPU开始从一个固定的地址加载代码并开始执行,这个地址就是BIOS的驱动程序所在的位置,于是BIOS的驱动开始执行. BIOS驱动首先进行一些自检工作,然后根据配置的启动顺序,依次尝试加载启动程序.比如配置的启动顺序是CD->网卡01->U