Android WiFi 获取 IP 过程

20 篇文章 5 订阅
9 篇文章 1 订阅

FdEventsReader 类

在其 start 接口中调用了 createAndRegisterFd(),调用了 createFd 接口创建了 mFd 对象,并将对应的对象通过 MessageQueue() 的 addOnFileDescriptorEventListener 方法加入到监听队列中;

其中要求 Subclasses 必须实现 createFd 接口并且重载 handlePacket 方法;

PacketReader 类继承 FdEventsReader 类

它作为抽象类需要子类作为实现类实现具体的方法;

DhcpPacketHandler 继承 PacketReader

packages/modules/NetworkStack/src/android/net/dhcp/DhcpClient.java 中的 DhcpPacketHandler 继承  PacketReader 作为内部类实现了 DhcpClient 类中各种报文的发送和接收操作;

DhcpDiscover 报文的发送过程

1. 在 DhcpInitState,其继承关系为 DhcpInitState -> PacketRetransmittingState -> TimeoutState -> LoggingState -> State;

2. 在 DhcpInitState 的 enter 接口中调用了父类的 enter,在 PacketRetransmittingState 的 enter 中调用了 sendPacket,实际上为虚函数,在 DhcpInitState 实现

3. 在 DhcpInitState 的 sendPacket 接口调用了 DhcpClient 中的 sendDiscoverPacket;

4. 进而调用了 DhcpPacket 类的 buildDiscoverPacket 构建报文,调用了 transmitPacket 进行发送;

5. transmitPacket 调用了 DhcpPacketHandler 的 transmitPacket 发给报文,所用的 socket 为其从 FdEventsReader 中继承而来的 fd,其中实现了 createFd 和 handlePacket 的重载,通过 Os 类的 socket 和 send 等方法实现;

进入 DhcpInitState 状态

1. 在 ObtainingConfigurationState 收到 EVENT_CONFIGURATION_INVALID 或 EVENT_CONFIGURATION_TIMEOUT 消息时;

2. startInitRebootOrInit 接口中通过 preDhcpTransitionTo 进入,需要 mWaitBeforeStartState 为 false;

首先来看整体的状态关系:

        addState(mStoppedState);
        addState(mDhcpState);
            addState(mDhcpInitState, mDhcpState);
            addState(mWaitBeforeStartState, mDhcpState);
            addState(mWaitBeforeObtainingConfigurationState, mDhcpState);
            addState(mDhcpPreconnectingState, mDhcpState);
            addState(mObtainingConfigurationState, mDhcpState);
            addState(mDhcpSelectingState, mDhcpState);
            addState(mDhcpRequestingState, mDhcpState);
            addState(mIpAddressConflictDetectingState, mDhcpState);
            addState(mIpv6OnlyWaitState, mDhcpState);
            addState(mDhcpHaveLeaseState, mDhcpState);
                addState(mConfiguringInterfaceState, mDhcpHaveLeaseState);
                addState(mDhcpBoundState, mDhcpHaveLeaseState);
                addState(mWaitBeforeRenewalState, mDhcpHaveLeaseState);
                addState(mDhcpRenewingState, mDhcpHaveLeaseState);
                addState(mDhcpRebindingState, mDhcpHaveLeaseState);
                addState(mDhcpDecliningState, mDhcpHaveLeaseState);
            addState(mDhcpInitRebootState, mDhcpState);
            addState(mDhcpRebootingState, mDhcpState);
        // CHECKSTYLE:ON IndentationCheck

        setInitialState(mStoppedState);

默认状态为 StoppedState,在其收到 CMD_START_DHCP 消息时可能调用 startInitRebootOrInit 而进入到 DhcpInitState;

在 IpClient 类中通过 startDhcpClient 方法发送对应的 CMD_START_DHCP 消息;此时进入 IpClient 来分析;

startDhcpClient 可能的入口:

1. startIPv4 进入,其调用者在 RunningState 的 enter 中,需要 Configuration 中 mEnablePreconnection 和 mStaticIpConfig 均为 false;

2. PreconnectingState 状态的 enter 中直接调用了 startDhcpClient 接口;

先来分析 IpClient 的整体状态关系:

//在构造函数中调用了 configureAndStartStateMachine 接口,父类为 StateMachine

    private void configureAndStartStateMachine() {
        // CHECKSTYLE:OFF IndentationCheck
        addState(mStoppedState);
        addState(mStartedState);
            addState(mPreconnectingState, mStartedState);
            addState(mClearingIpAddressesState, mStartedState);
            addState(mRunningState, mStartedState);
        addState(mStoppingState);
        // CHECKSTYLE:ON IndentationCheck

        setInitialState(mStoppedState);

        super.start();
    }

1. 在 StioppedState 状态下,其收到 CMD_START 之后会进入到 ClearingIpAddressState 中;

2. 在 ClearingIpAddressState 的 enter 中,其发送了 CMD_ADDRESSES_CLEARED 类型的 defer 消息,稍后它自己就会收到该消息,并对其处理;

3. 在收到 CMD_ADDRESSES_CLEARED 消息后,ClearingIpAddressState 的 processMessage 会直接调用 transitionTo 而转换状态,在这里需要强调,如果 isUsingPreconnection() 返回 false,则会进入 RunningState 中,否则 PreconnectingState;

4. 先分析 isUsingPreconnection() 为 true 的情况:其 enter 接口直接调用了 startDhcpClient 如前面分析的启动了 DhcpClient;

5. 在 收到 CMD_COMPLETE_PRECONNECTION 消息的情况下转入到 RunningState;

6. 在 RunningState 状态下处理各种 DhcpClient 启动以及启动后的状态变化;

7. 在 isUsingPreconnection() 返回 false 的情况下直接进入 RunningState 进行处理,这里会调用 startIPv4() 进行 startDhcpClient;

8. 进入到前面分析的 DhcpClient 进行分析,其收到了 CMD_START_DHCP 消息;

触发过程

当底层 wpa_supplicant 端完成了 Authentication 和 Association 动作实现了无线网络连接之后,通过 onStateChanged 回调,将消息通知到 Framework 层,WifiMonitor 通过 broadcastNetworkConnectionEvent 将 NETWORK_CONNECTION_EVENT 消息发送出来,其实就是将消息发送给所有注册到 WifiMonitor 中的 Handler 对象

ClientModeImpl 中 ConnectModeState 收到对应的消息,转入到 ObtainingIpState 中;

ObtainingIpState 的 enter 接口调用了 startIpClient 启动客户端 IpClient,其实就是调用了 IpClientManager 的 startProvisioning 接口进入处理过程;

IpClientManager 的 startProvisioning 实际调用了 IpClient 的 startProvisioning 接口,其内部就是发送了 CMD_START 消息,进入到 StoppedState 处理过程,如前面分析。

onStateChanged 分析

onStateChanged_1_3 中第一个参数 newState 即为新的状态,经过 supplicantHidlStateToFrameworkState 转换得到上层可以识别的状态:

    /**
     * Converts the supplicant state received from HIDL to the equivalent framework state.
     */
    protected static SupplicantState supplicantHidlStateToFrameworkState(int state) {
        switch (state) {
            case ISupplicantStaIfaceCallback.State.DISCONNECTED:
                return SupplicantState.DISCONNECTED;
            case ISupplicantStaIfaceCallback.State.IFACE_DISABLED:
                return SupplicantState.INTERFACE_DISABLED;
            case ISupplicantStaIfaceCallback.State.INACTIVE:
                return SupplicantState.INACTIVE;
            case ISupplicantStaIfaceCallback.State.SCANNING:
                return SupplicantState.SCANNING;
            case ISupplicantStaIfaceCallback.State.AUTHENTICATING:
                return SupplicantState.AUTHENTICATING;
            case ISupplicantStaIfaceCallback.State.ASSOCIATING:
                return SupplicantState.ASSOCIATING;
            case ISupplicantStaIfaceCallback.State.ASSOCIATED:
                return SupplicantState.ASSOCIATED;
            case ISupplicantStaIfaceCallback.State.FOURWAY_HANDSHAKE:
                return SupplicantState.FOUR_WAY_HANDSHAKE;
            case ISupplicantStaIfaceCallback.State.GROUP_HANDSHAKE:
                return SupplicantState.GROUP_HANDSHAKE;
            case ISupplicantStaIfaceCallback.State.COMPLETED:
                return SupplicantState.COMPLETED;
            default:
                throw new IllegalArgumentException("Invalid state: " + state);
        }
    }

当 WiFi 连接上之后会收到 ISupplicantStaIfaceCallback.State.COMPLETED 状态,转换为 SupplicantState.COMPLETED 状态,在 onStateChanged_1_3 处理过程中调用了 broadcastNetworkConnectionEvent 接口广播消息;

ISupplicantStaIfaceCallback 定义在对应的 hal 文件中:

  • hardware/interfaces/wifi/supplicant/1.2/ISupplicantStaIfaceCallback.hal
  • hardware/interfaces/wifi/supplicant/1.3/ISupplicantStaIfaceCallback.hal
  • hardware/interfaces/wifi/supplicant/1.0/ISupplicantStaIfaceCallback.hal
  • hardware/interfaces/wifi/supplicant/1.1/ISupplicantStaIfaceCallback.hal

ISupplicantStaIfaceCallback.State.COMPLETED 定义在 1.0 中;

深入到 wpa_supplicant 来看,在其 src/common/defs.h 头文件中定义了 enum wpa_states,与 上述 hal 中定义相同,表示了无线连接过程中的状态,状态的变化都是通过 wpa_supplicant_set_state 来进行设定,其内部通过 wpas_notify_state_changed 接口进行广播,在 Android 中添加了 HIDL 接口,wpas_hidl_notify_state_changed 继而被调用,其内部使用了 hidl_manager 的 notifyStateChange 方法,在其内部找到对应的 sta_iface 对象,调用其注册的 Callback 方法将消息通过 Binder 传送给上层

IP 配置过程

在 IpClient 中 RunningState 下 processMessage 处理 DhcpClient.CMD_CONFIGURE_LINKADDRESS 消息,调用了 InterfaceCtrl 的 setIPv4Address 设置 IPv4 地址;

继续调用 setInterfaceConfiguration 配置,从而调用了 netd 中的 binder 服务接口 interfaceSetCfg 设置

Route 等配置

在 ClientModeImpl 中通过 updateLinkProperties 接口,调用了 NetworkAgent 的 sendLinkProperties 将对应的连接方式的信息发送给 ConnectivityService;

updateLinkProperties 是在 DefaultState 的 processMessage 中被调用来处理 CMD_UPDATE_LINKPROPERTIES 消息,该消息在 DefaultState 的子状态不处理,因而留给他统一处理;

CMD_UPDATE_LINKPROPERTIES 消息由 IpClientCallBacksImpl 的 onProvisioningSuccess 和 onLinkPropertiesChange 发出;

IpClientCallBacksImpl 创建的对象 mIpClientCallbacks 在 ClientModeImpl 的 setupClientMode 中创建 IpClient 时作为参数传递给 IpClient,IpClient 进行回调时会调用;

① IpClientCallBacksImpl 的 onProvisioningSuccess 在 IpClient 的 onProvisioningSuccess 调用时通过 mCallback.onProvisioningSuccess 调用,这是在其 dispatchCallback 接口调用过程中调用;

② 而 dispatchCallback 则在下面场景被调用:

  • handleIPv4Success:在 startIPv4 且为 StaticIp 的情况下调用(不再分析),或者 IpClient.RunningState.processMessage 处理 DhcpClient.CMD_POST_DHCP_ACTION 消息时处理 DhcpClient.DHCP_SUCCESS 消息调用;
  • handleLinkPropertiesUpdate:在各种不同的状态下用于状态变化的更新,比如 EVENT_NETLINK_LINKPROPERTIES_CHANGED 消息;
  • handleProvisioningFailure:处理获取 IP 失败的情况,对应 handleIPv4Success 的 handleIPv4Failure 中调用,或者 StartedState 下 EVENT_PROVISIONING_TIMEOUT 处理;
  • IpClient.RunningState.processMessage:处理 setIPv4Address 设置 IP 地址失败的情况,分发 PROV_CHANGE_LOST_PROVISIONING 消息;

重点分析第一个 IpClient.RunningState.processMessage 下处理 DhcpClient.CMD_POST_DHCP_ACTION 消息,事件为 DhcpClient.DHCP_SUCCESS;

该消息在 DhcpClient 的 notifySuccess 接口中发出,首先其在 ConfiguringInterfaceState 状态下的 enter 接口被调用,这是在 DHCP 成功后状态切换引起,如果存在冲突地址监测,则在 IpAddressConflictDetectingState 状态下完成检测转入,否则在 receiveOfferOrAckPacket 接口中转入;该接口在 DhcpInitState 中被调用,实际上是在 PacketRetransmittingState 的 processMessage 接口中处理 CMD_RECEIVED_PACKET 消息而调用了 receivePacket,该接口由子类实现,即 DhcpInitState 中的 receivePacket 接口;

CMD_RECEIVED_PACKET 则是在监听消息的套接字 DhcpPacketHandler 中处理函数 handlePacket 读取报文进行处理之后发出的信息,而实际上 DhcpPacketHandler 继承 PacketReader,其套接字会在 Handler 中进行 eventpoll 操作来查询消息从而调用 handlePacket 接口

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值