[Network]Android N Ethernet新IP获取机制—IpManager

本文介绍了Android N中 Ethernet IP获取机制的变化,从依赖于/system/bin/dhcpcd到使用DhcpClient并通过IpManager进行管理。IpManager是一个用于获取IP的管理器,它涉及到了IPv4和IPv6的配置与处理过程,为ipv6的支持奠定了基础。在新机制下,Android N直接通过UDP请求包与DHCP Server交互,增强了IP获取的稳定性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

android N之前 Ethernet旧的ip获取

Android N之前,即android 5.0和android 6.0的IP获取机制都是通过/system/bin下面的dhcpcd的bin档去拿的ip

//EthernetNetworkFactory.java (frameworks\opt\net\ethernet\java\com\android\server\ethernet)

    public void onRequestNetwork() {
        // TODO: Handle DHCP renew.
        Thread dhcpThread = new Thread(new Runnable() {
            public void run() {
                        ...

                    DhcpResults dhcpResults = new DhcpResults();
                    // TODO: Handle DHCP renewals better.
                    // In general runDhcp handles DHCP renewals for us, because
                    // the dhcp client stays running, but if the renewal fails,
                    // we will lose our IP address and connectivity without
                    // noticing.
                    //通过这个函数call到jni之后,最后run /system/bin/dhcpcd
                    if (!NetworkUtils.runDhcp(mIface, dhcpResults)) {
                        Log.e(TAG, "DHCP request error:" + NetworkUtils.getDhcpError());
                        // set our score lower than any network could go
                        // so we get dropped.
                        mFactory.setScoreFilter(-1);
                        return;
                    }

不知道是不是dhcpcd本身的问题,这段code其实不是稳定,超时拿不到ip的现象经常可以遇到。只能在这里多加几次try runDhcp。

android N新的拿IP的机制

android N不要了runDhcpcd(),而是通过DhcpClient,这个是android M引入的wifi那边另外一种获得ip的方式,DhcpClient是通过framework发送dhcpcd协议的UDP请求包直接去拿IP,不再使用开源的dhcpcd。在调用DhcpClient的基础之前,google还用了一个状态机IpManager来管理dhcpcd成功还是失败等状态,将ip赋值给IpConfiguration和LinkProperties传递到上层的framework。还有就是加入ipv6的支持,不过ipv6的部分目前还没做好。

//EthernetNetworkFactory.java (frameworks\opt\net\ethernet\java\com\android\server\ethernet)    

        final Thread ipProvisioningThread = new Thread(new Runnable() {
            public void run() {
                if (DBG) {
                    Log.d(TAG, String.format("starting ipProvisioningThread(%s): mNetworkInfo=%s",
                            mIface, mNetworkInfo));
                }

                LinkProperties linkProperties;

                IpConfiguration config = mEthernetManager.getConfiguration();

                if (config.getIpAssignment() == IpAssignment.STATIC) {
                    if (!setStaticIpAddress(config.getStaticIpConfiguration())) {
                        // We've already logged an error.
                        return;
                    }
                    linkProperties = config.getStaticIpConfiguration().toLinkProperties(mIface);
                } else {

                    mNetworkInfo.setDetailedState(DetailedState.OBTAINING_IPADDR, null, mHwAddr);
                    WaitForProvisioningCallback ipmCallback = new WaitForProvisioningCallback() {
                        @Override
                        public void onLinkPropertiesChange(LinkProperties newLp) {
                            synchronized(EthernetNetworkFactory.this) {
                                if (mNetworkAgent != null && mNetworkInfo.isConnected()) {
                                    mLinkProperties = newLp;
                                    mNetworkAgent.sendLinkProperties(newLp);
                                }
                            }
                        }
                    };

                    synchronized(EthernetNetworkFactory.this) {
                        stopIpManagerLocked();
                        //IPManger,用这个代替之前的runDhcpcd()
                        mIpManager = new IpManager(mContext, mIface, ipmCallback);

                        if (config.getProxySettings() == ProxySettings.STATIC ||
                                config.getProxySettings() == ProxySettings.PAC) {
                            mIpManager.setHttpProxy(config.getHttpProxy());
                        }

                        final String tcpBufferSizes = mContext.getResources().getString(
                                com.android.internal.R.string.config_ethernet_tcp_buffers);
                        if (!TextUtils.isEmpty(tcpBufferSizes)) {
                            mIpManager.setTcpBufferSizes(tcpBufferSizes);
                        }

                        final ProvisioningConfiguration provisioningConfiguration =
                                mIpManager.buildProvisioningConfiguration()
                                        .withProvisioningTimeoutMs(0)
                                        .build();
                        //start
                        mIpManager.startProvisioning(provisioningConfiguration);
                    }
                    //通过callback去拿到新的IP
                    linkProperties = ipmCallback.waitForProvisioning();
                    if (linkProperties == null) {
                        Log.e(TAG, "IP provisioning error");
                        // set our score lower than any network could go
                        // so we get dropped.
                        mFactory.setScoreFilter(-1);
                        synchronized(EthernetNetworkFactory.this) {
                            stopIpManagerLocked();
                        }
                        return;
                    }
                }

IpManager

IpManager,是wifi和Ethernet都会用到。把它定义为获取ip的管理器好了。

1.构造函数

    public IpManager(Context context, String ifName, Callback callback)
                throws IllegalArgumentException {
        this(context, ifName, callback, INetworkManagementService.Stub.asInterface(
                ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE)));
    }

    /**
     * An expanded constructor, useful for dependency injection.
     */
    public IpManager(Context context, String ifName, Callback callback,
            INetworkManagementService nwService) throws IllegalArgumentException {
        super(IpManager.class.getSimpleName() + "." + ifName);
        mTag = getName();

        mContext = context;
        //网口的名字,eth0 or wlan0
        mInterfaceName = ifName;
        mClatInterfaceName = CLAT_PREFIX + ifName;
        mCallback = new LoggingCallbackWrapper(callback);
        //NetworkManagementService对象,可以对Interface做一些操作
        mNwService = nwService;

        //检测Interface的状态变化
        mNetlinkTracker = new NetlinkTracker(
                mInterfaceName,
                new NetlinkTracker.Callback() {
                    @Override
                    public void update() {
                        sendMessage(EVENT_NETLINK_LINKPROPERTIES_CHANGED);
                    }
                }) {
            @Override
            public void interfaceAdded(String iface) {
   
   
                super.interfaceAdded(iface);
                if (mClatInterfaceName.equals(iface)) {
                    mCallback.setNeighborDiscoveryOffload(false);
                }
            }

            
<think>我们正在为运行Android 16的树莓派4配置静态IP地址。由于Android系统通常通过图形界面设置网络,但在树莓派上运行Android且没有图形界面时,可能需要通过命令行或修改配置文件来实现。 然而,需要注意的是,标准的Android系统(尤其是手机/平板)通常不提供直接设置静态IP的命令行工具(如ifconfig、ip命令可能被精简)。但树莓派上运行的Android系统可能有所不同,它可能基于AOSP(Android Open Source Project)并可能保留了更多Linux特性。 根据引用[3]和[4],我们通常通过代码获取IP地址,但设置静态IP可能需要系统权限或修改系统配置文件。在Android中,网络设置通常由系统服务管理,普通应用无法直接修改。但如果我们有root权限,并且系统支持,我们可以尝试修改网络配置文件。 在树莓派上运行Android,我们可能需要考虑以下方法: 方法1:通过Android系统设置(如果有图形界面) 如果有图形界面,可以进入设置->网络和互联网->Wi-Fi(或以太网)->点击当前连接的网络,然后选择“编辑”,将IP设置从DHCP改为静态,并输入IP地址、网关、子网掩码和DNS。 方法2:通过命令行(需要root权限) 如果系统支持命令行工具,并且我们有root权限,我们可以尝试使用以下步骤: 1. 获取当前网络接口名称(通常是wlan0或eth0) 2. 使用ip命令或ifconfig设置IP地址(临时生效,重启后失效) 3. 修改配置文件,使静态IP在重启后仍然有效。 但是,Android系统通常不使用传统的/etc/network/interfaces文件,而是使用Netd和NetManager服务。在Android中,网络配置信息存储在/data/misc/ethernet/目录(以太网)或/data/misc/wifi/目录(Wi-Fi)下的配置文件中。具体路径可能因系统版本和设备而异。 对于Wi-Fi,我们可能需要修改wpa_supplicant.conf文件,但该文件通常用于配置Wi-Fi连接(SSID和密码),而不是静态IP。 另一种方法是使用Android提供的设置命令(settings)或通过设置全局属性,但这不是标准方法。 考虑到我们运行的是Android 16(即Android 4.1.x,较旧的版本),并且是在树莓派上,可能系统已经做了一些定制。我们可以尝试使用以下步骤: 步骤1:通过ADB连接树莓派(确保已开启USB调试或网络调试) 步骤2:获取root权限(adb root 或 adb shell后执行su) 步骤3:使用命令行设置静态IP(临时): 例如,设置eth0的静态IP为192.168.1.100,子网掩码255.255.255.0,网关192.168.1.1: ifconfig eth0 192.168.1.100 netmask 255.255.255.0 route add default gw 192.168.1.1 dev eth0 但是,这样设置后,DNS可能还需要设置。我们可以修改/resolv.conf文件,但Android通常使用Netd管理DNS,所以可能不持久。 步骤4:设置持久化(重启后仍然有效): 我们需要修改系统启动时加载的网络配置。在Android中,这通常通过init.rc脚本或类似机制。我们可以创建一个启动脚本,在系统启动时设置静态IP。 例如,在/system/etc/init.sh(如果存在)中添加上述命令,或者创建一个init.d脚本(如果系统支持init.d)。 但是,请注意,修改系统分区需要remount为可写,并且需要系统支持。 方法3:使用第三方应用 在Google Play上有一些应用可以设置静态IP,但需要root权限。 由于树莓派运行Android,我们也可以考虑使用以太网连接,并修改以太网的配置文件。在Android中,以太网设置可能存储在: /data/misc/ethernet/ipconfig.txt 我们可以尝试创建或修改这个文件,内容如下: static ipaddress 192.168.1.100 netmask 255.255.255.0 gateway 192.168.1.1 dns1 8.8.8.8 dns2 8.8.4.4 然后重启以太网服务或重启设备。 但是,这种方法是否有效取决于系统是否实现了读取该文件的机制。在AOSP中,以太网服务(EthernetService)可能会读取这个文件。 根据以上分析,我们给出以下步骤(假设使用以太网,并且系统支持): 1. 通过ADB连接设备: adb connect <树莓派的IP> (如果通过USB连接,则直接使用adb shell) adb shell 2. 获取root权限: su 3. 创建或修改配置文件: echo "static" > /data/misc/ethernet/ipconfig.txt echo "ipaddress 192.168.1.100" >> /data/misc/ethernet/ipconfig.txt echo "netmask 255.255.255.0" >> /data/misc/ethernet/ipconfig.txt echo "gateway 192.168.1.1" >> /data/misc/ethernet/ipconfig.txt echo "dns1 8.8.8.8" >> /data/misc/ethernet/ipconfig.txt echo "dns2 8.8.4.4" >> /data/misc/ethernet/ipconfig.txt 4. 重启网络服务或重启设备。 注意:/data/misc/ethernet/目录可能需要特定权限,我们可以使用chmod和chown来设置,但通常该目录属于ethernet用户组,所以可能不需要修改。 如果使用Wi-Fi,配置文件可能在/data/misc/wifi/wpa_supplicant.conf,但该文件主要用于连接认证,静态IP设置可能需要额外的配置。我们可以尝试在wpa_supplicant.conf中设置: network={ ssid="your_ssid" psk="your_password" key_mgmt=WPA-PSK id_str="static" } 并在同一目录下创建一个名为ipconfig.txt的文件,内容与上面类似。但是,Wi-Fi的静态IP配置可能不通过这种方式。 由于Android版本较旧,且树莓派的Android系统可能经过定制,建议查阅该Android系统的文档或社区支持。 如果以上方法都不行,我们可以考虑修改系统源码中的网络配置部分,然后重编译系统。但这对于普通用户来说比较困难。 总结:在树莓派4上运行Android 16设置静态IP,我们建议先尝试图形界面(如果有)。如果没有,则尝试通过ADB修改配置文件(/data/misc/ethernet/ipconfig.txt)并重启。如果不行,可以尝试在启动脚本中设置静态IP(需要系统支持)。 注意:操作前请备份重要数据,修改系统文件有风险。 相关问题: 1. 如何通过ADB连接树莓派上的Android设备? 2. 在Android系统中,如何在没有root权限的情况下设置静态IP? 3. 树莓派运行Android时,如何通过修改启动脚本设置静态IP?</think>要在运行 Android 16 的树莓派 4 上设置静态 IP 地址,请按照以下步骤操作: ### 方法 1:通过 Android 系统设置(推荐) 1. **进入网络设置** - 打开 Android 设置 → 选择 **Wi-Fi** 或 **网络和互联网** - 长按当前连接的 Wi-Fi 网络 → 选择 **修改网络** 2. **配置静态 IP** - 勾选 **高级选项** → 将 **IP 设置** 从 DHCP 改为 **静态** - 填写以下信息: - `IP 地址`:您要设置的静态 IP(如 `192.168.1.100`) - `网关`:路由器地址(如 `192.168.1.1`) - `网络前缀长度`:子网掩码(通常为 `24`,对应 `255.255.255.0`) - `DNS 1/2`:DNS 服务器(如 `8.8.8.8` 和 `8.8.4.4`) 3. **保存设置** - 点击 **保存** → 重连接网络使配置生效[^1][^2] ### 方法 2:通过 ADB 命令(需 root 权限) ```bash # 连接树莓派 adb connect <树莓派IP> adb shell # 进入超级用户模式 su # 设置静态IP(示例:eth0接口) ip link set eth0 down ip addr add 192.168.1.100/24 dev eth0 ip route add default via 192.168.1.1 ip link set eth0 up # 设置DNS setprop net.dns1 8.8.8.8 setprop net.dns2 8.8.4.4 ``` > ⚠️ **注意**: > 1. 静态 IP 需在路由器 DHCP 范围外(如路由器分配 `192.168.1.2~100`,则静态 IP 应设为 `192.168.1.101` 以上) > 2. 若使用 Wi-Fi,将命令中的 `eth0` 替换为 `wlan0` > 3. ADB 方法重启后失效,永久设置需修改系统配置文件[^3][^4] ### 永久生效配置(需修改系统文件) 1. 通过 ADB 挂载系统分区为可写: ```bash adb remount /system rw ``` 2. 编辑网络配置文件: ```bash adb pull /system/etc/network_interfaces.xml ``` 3. 在文件中添加: ```xml <interface name="eth0"> <ip address="192.168.1.100" netmask="255.255.255.0"/> <route gateway="192.168.1.1"/> </interface> ``` 4. 推送回设备并重启: ```bash adb push network_interfaces.xml /system/etc/ adb reboot ``` ### 验证配置 ```bash adb shell ip addr show eth0 # 检查IP adb shell netstat -rn # 检查路由 adb shell getprop net.dns # 检查DNS ```
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值