Android KK(4.4) 以太网和DHCP启动过程介绍

  • 1. ethernet启动流程
  • 2. DHCP启动过程
    • 2.1 JNI层接口
    • 2.2 DHCP客户端(libnetutils)
    • 2.3 DHCP服务端(dhcpcd)

  常做Android的大佬们可能知道,Android 5.0是一个历史分水岭一样的版本,其前后改动应该是所有版本中最大的了,而目前我的工作主要就围绕着Android P(9.0)和Android KK(4.4)两个版本进行支持,再了解熟悉了9.0的网络流程后,乘胜追击熟悉下Android 4.4 的网络模块,在源码基础上对以太网的初始化流程、网络策略配置、dhcp交互过程等做一些简单的介绍,老规矩先上图。

1. ethernet启动流程

  这次文章写的稍微简单了些,只是简单介绍下流程,没有具体分析代码,如果对具体代码感兴趣可以看下Android network框架分析,本篇更多在介绍流程上的概述,话不多说开搞!

  • 创建ConnectivityService
    frameworks\base\services\java\com\android\server\SystemServer.java
    networkmanagement、networkStats、networkPolicy已经提前创建好,并作为参数传入

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

  • 创建NetworkStateTracker和RadioAttributes
    frameworks\base\services\java\com\android\server\ConnectivityService.java
    会创建多个Tracker,包括wifi、bluetooth、mobile、ethernet等

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

  • 加载网络策略配置属性
    frameworks\base\services\java\com\android\server\ConnectivityService.java

        // Load device network attributes from resourcesString[] 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;}
    
  • 对已支持的网络接口进行优先级排序
    frameworks\base\services\java\com\android\server\ConnectivityService.java

        // high priority firstmPriorityList = 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;}}
    
  • 启动已支持网络接口的各个tracker
    frameworks\base\services\java\com\android\server\ConnectivityService.java

        // Create and start trackers for hard-coded networksfor (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();}}
    
  • 启动配置文件指定的以太网接口
    frameworks\base\core\java\android\net\EthernetDataTracker.java
    EthernetDataTracker 将会寻找第一个以 eth 开头的有线网络设备,打开并开始做 dhcp

        <!-- Regex of wired ethernet ifaces --><string translatable="false" name="config_ethernet_iface_regex">eth\\d</string>
    
            sIfaceMatch = context.getResources().getString(com.android.internal.R.string.config_ethernet_iface_regex);try {final String[] ifaces = mNMService.listInterfaces();for (String iface : ifaces) {// 如果指定的接口存在于网络服务接口列表中,则通过该接口连接网络if (iface.matches(sIfaceMatch)) {mNMService.setInterfaceUp(iface);InterfaceConfiguration config = mNMService.getInterfaceConfig(iface);if (getEthernetCarrierState(iface) == 1) {mIface = iface;mLinkUp = true;mNetworkInfo.setIsAvailable(true);if (config != null && mHwAddr == null) {mHwAddr = config.getHardwareAddress();if (mHwAddr != null) {mNetworkInfo.setExtraInfo(mHwAddr);}}}// if a DHCP client had previously been started for this interface, then stop itNetworkUtils.stopDhcp(iface);}}reconnect();} catch (RemoteException e) {Log.e(TAG, "Could not get list of interfaces " + e);}
    
  • 启动DHCP服务
    frameworks\base\core\java\android\net\EthernetDataTracker.java

    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();
    }
    
  • 以太网的网络策略配置
    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 -ABDHKLdclass maindisabledoneshot
    

2. DHCP启动过程

2.1 JNI层接口

  Java层调用的NetworkUtils.runDhcp(mIface, dhcpResults)接口,在frameworks\base\core\jni\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)
{......const char *nameStr = env->GetStringUTFChars(ifname, NULL);if (nameStr == NULL) return (jboolean)false;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.2 DHCP客户端(libnetutils)

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

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

    property_set(ctrl_prop, daemon_cmd);
    wait_for_property(daemon_prop_name, desired_status, 10)

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

    wait_for_property(result_prop_name, NULL, 30)

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

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

2.3 DHCP服务端(dhcpcd)

  • DHCP消息类型

    DHCPDISCOVER

    DHCPOFFER

    DHCPREQUEST

    DHCPDECLINE

    DHCPACK

    DHCPNACK

    DHCPRELEASE

    DHCPINFORM

  • 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地址。

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

       dhcpcd[1544]: version 5.5.6 startingdhcpcd[1544]: get_duid: Permission denieddhcpcd[1544]: eth0: using ClientID 01:12:78:e4:0b:44:00dhcpcd[1544]: eth0: executing `/system/etc/dhcpcd/dhcpcd-run-hooks', reason PREINITdhcpcd[1544]: eth0: reading lease `/data/misc/dhcp/dhcpcd-eth0.lease'dhcpcd[1544]: eth0: rebinding lease of 192.168.4.164dhcpcd[1544]: eth0: sending REQUEST (xid 0x440be4), next in 3.54 secondsdhcpcd[1544]: eth0: sending REQUEST (xid 0x440be4), next in 7.63 secondsdhcpcd[1544]: eth0: NAK: from 192.168.4.1dhcpcd[1544]: eth0: executing `/system/etc/dhcpcd/dhcpcd-run-hooks', reason NAKdhcpcd[1544]: eth0: broadcasting for a leasedhcpcd[1544]: eth0: sending DISCOVER (xid 0x440be4), next in 4.97 secondsdhcpcd[1544]: eth0: offered 192.168.4.165 from 192.168.4.1dhcpcd[1544]: eth0: sending REQUEST (xid 0x440be4), next in 3.02 secondsdhcpcd[1544]: eth0: acknowledged 192.168.4.165 from 192.168.4.1dhcpcd[1544]: eth0: leased 192.168.4.165 for 7200 secondsdhcpcd[1544]: eth0: adding IP address 192.168.4.165/24dhcpcd[1544]: eth0: adding route to 192.168.4.0/24dhcpcd[1544]: eth0: adding default route via 192.168.4.1dhcpcd[1544]: eth0: writing lease `/data/misc/dhcp/dhcpcd-eth0.lease'dhcpcd[1544]: eth0: executing `/system/etc/dhcpcd/dhcpcd-run-hooks', reason BOUND
    
  • 调试网络的常用命令

    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

Android KK(4.4) 以太网和DHCP启动过程介绍相关推荐

  1. 从源码角度看Android系统Launcher在开机时的启动过程

    Launcher是Android所有应用的入口,用来显示系统中已经安装的应用程序图标. Launcher本身也是一个App,一个提供桌面显示的App,但它与普通App有如下不同: Launcher是所 ...

  2. symfony入门学习资料之十六:Symfony框架启动过程介绍

    symfony入门学习资料之十六:Symfony框架启动过程介绍 Symfony框架的核心本质是把Request转换成Response的一个过程.从入口文件(web_dev.php)的源码可以看个大概 ...

  3. Android 7.0 增加以太网设置DHCP和静态IP

    https://blog.csdn.net/zhouyuanjing/article/details/78415891 Android 7.0 自带EthernetService,默认开机就会启动,默 ...

  4. Android应用程序组件Content Provider的启动过程源代码分析(1)

             通过前面的学习,我们知道在Android系统中,Content Provider可以为不同的应用程序访问相同的数据提供统一的入口.Content Provider一般是运行在独立的进 ...

  5. Android 系统(14)---SystemServer进程启动过程

    SystemServer进程的启动 在上一篇文章Framework学习(二)Zygote进程启动过程中,我们已经知道Zygote进程会启动SystemServer进程,但具体启动流程还没有涉及,本文我 ...

  6. Android 系统(12)---Zygote进程启动过程

    android系统进程启动流程 android系统的Zygote进程是所有android进程的父进程,包括SystemServer和各种应用进程都是通过Zygote进程fork出来的.Zygote(孵 ...

  7. Android系统启动(二) — Zygote进程启动过程

    1 概述 在 Android 系统中,DVM(Dalvik 虚拟机)和 ART,系统服务进程 system_server 以及应用程序进程都是由 Zygote 进程来创建的(而 Native 程序,也 ...

  8. Android系统启动流程(四)Launcher启动过程与系统启动流程

    相关文章 Android系统架构与系统源码目录 Android系统启动流程(一)解析init进程启动过程 Android系统启动流程(二)解析Zygote进程启动过程 Android系统启动流程(三) ...

  9. iOS程序的启动过程介绍

    大家在学习iPhone开发时候,都会写HelloWorld程序.大家一般都是通过向导,生成项目,然后通过模拟器启动应用程序.但是大家知道其背后的启动过程吗?也就是当点击程序图标启动程序开始到退出程序整 ...

最新文章

  1. 构建一个运行在Azure虚拟机上的MySQL Spring Boot应用程序
  2. oracle 9i net基本配置
  3. linux 创建crontab文件位置,linux - 如何通过脚本创建crontab - Ubuntu问答
  4. java布局垂直居中_CSS水平居中和垂直居中解决方案(转)
  5. Java享元模式之字符串享元
  6. mysql base64 乱码_PHP base64编码后解码乱码的解决办法
  7. Qt工程生成xcode工程文件
  8. MongoDB 分析查询性能
  9. mac 用户 文件夹 权限_Mac视频播放软件推荐
  10. 在实际项目中,如何选择合适的机器学习模型?
  11. 响应式编程在Android 中的一些探索
  12. java manualbuffer_FlatBuffer Java Bean自由转换
  13. 实习商汤,校招华为,我的深度学习之路
  14. Elasticsearch 入门到精通-Elasticsearch创建索引
  15. codecademy SQL 编程系列一Introduction
  16. 10个热门大数据发展趋势
  17. 1.17 “干项目太累,那是因为姿势不对” Stacey矩阵
  18. IDEA启动显示 找不到应用程序
  19. OVP电路应用(一)_12V电源_DIO1280
  20. 外贸新手如何利用领英寻找你的潜在客户(建议收藏)

热门文章

  1. ubuntu22.04踩坑笔记1--mendeley安装中APPimage打不开及闪退问题
  2. 机器人如何改变我们的世界
  3. 【立创开源】N32G430C8L7最小系统板
  4. 如何用手机控制stm32单片机,通过蓝牙模块
  5. 服务器删除登录日志文件,CentOS查看登录日志及其它安全日志 清空删除系统日志的方法...
  6. P6专题:Oracle P6 数据库管理(SQLite/单机版数据库)
  7. MATLAB GUI笔记(八):列表框的使用
  8. 多元微积分_三维散度
  9. Android随笔-IPC
  10. 湖北生态工程职业技术学院信息机电学院智慧林业物联网实训室竣工