STA 连接请求到 wpa_supplicant
STA 连接请求到 wpa_supplicant
从 connectToNetwork 到拿到 IP:OpenHarmony STA 连接 wpa_supplicant 的完整链路
一次 STA 连接请求从应用层走到 wpa_supplicant,再经事件回调回到框架层、最后通过 DHCP 拿到 IP 的完整调用链。
一、这条链路到底有多长
一次用户主动连接,下行调用链大致如下:
应用 / JS API
-> WifiDeviceImpl
-> WifiDeviceProxy(IPC 客户端)
-> WifiDeviceStub / WifiDeviceServiceImpl(SA 侧)
-> IStaService / StaService
-> StaStateMachine(消息驱动)
-> WifiStaHalInterface
-> WifiHdiWpaClient
-> HDI IWpaInterface(wpa_interface_service)
-> wpa_supplicant
-> Wi-Fi 驱动 / 固件
连接过程中还有一条“上行事件链”,负责把 supplicant 的结果传回框架:
wpa_supplicant 事件
-> HDI IWpaCallback
-> wifi_hdi_wpa_callback
-> WifiEventCallback
-> StaMonitor
-> StaStateMachine(InternalMessage)
连接成功且 802.11 关联完成后,还会再开一条“拿 IP”的支线:
StaStateMachine::GetIpState
-> RegisterDhcpClientCallBack / RegisterDhcpClientReportCallBack
-> StartDhcpClient(RouterConfig)
-> DHCP Client SA
-> DhcpResultNotify 回调
-> WIFI_SVR_CMD_STA_DHCP_RESULT_NOTIFY_EVENT
-> StaStateMachine -> LinkedState / NetSupplierInfo 更新
二、一次连接请求的下行链路
2.1 应用层与 Native Framework
以 connectToNetwork(networkId) 为例,应用层最终会走到 WifiDeviceImpl::ConnectToNetwork,它并不直接操作状态机,而是拿到 IPC 代理后转发:
ErrCode WifiDeviceImpl::ConnectToNetwork(int networkId, bool isCandidate)
{
std::lock_guard<std::mutex> lock(mutex_);
RETURN_IF_FAIL(GetWifiDeviceProxy());
return client_->ConnectToNetwork(networkId, isCandidate);
}
WifiDeviceProxy::ConnectToNetwork 通过 Remote()->SendRequest() 发送 DevInterfaceCode::WIFI_SVR_CMD_CONNECT_TO,ConnectToDevice 对应 WIFI_SVR_CMD_CONNECT2_TO。进程边界在这里体现得最明显:应用进程和 Wi-Fi SA 不在同一个进程里。
SA 侧由 WifiDeviceStub::OnConnectTo / OnConnect2To 接收,再转到 WifiDeviceServiceImpl。WifiDeviceServiceImpl::ConnectToNetwork 会做这些事:
- 检查多 VAP 冲突,比如 P2P/热点是否占用了 Wi-Fi。
- 校验权限,例如 VerifyWifiConnectionPermission、企业 Wi-Fi 连接权限、候选网络配置权限。
- 从 WifiServiceManager 拿到 IStaService。
- 调用 pService->ConnectToNetwork(networkId) 或 pService->ConnectToDevice(config)。
从这里开始,请求进入 STA 服务模块。
2.2 StaService:把“连接”变成状态机消息
StaService 是 STA 服务的管理者,但它不直接去连网。它的核心动作是“把请求变成状态机消息”。
ConnectToDevice 的路径:
ErrCode StaService::ConnectToDevice(const WifiDeviceConfig &config) const
{
...
int netWorkId = AddDeviceConfig(config);
...
pStaStateMachine->SendMessage(
WIFI_SVR_CMD_STA_CONNECT_NETWORK, netWorkId, NETWORK_SELECTED_BY_USER);
return WIFI_OPT_SUCCESS;
}
AddDeviceConfig 会把配置加密、保存到 WifiSettings,并分配或复用 networkId。所以 ConnectToDevice 本质是“先保存配置,再按 networkId 发起连接”。
ConnectToNetwork 的路径:
ErrCode StaService::ConnectToNetwork(int networkId, int type) const
{
...
WifiDeviceConfig config;
WifiSettings::GetInstance().GetDeviceConfig(networkId, config, m_instId);
...
pStaStateMachine->SendMessage(
WIFI_SVR_CMD_STA_CONNECT_SAVED_NETWORK, networkId, type);
return WIFI_OPT_SUCCESS;
}
两条路径最终都会进入 StaStateMachine::InitState::StartConnectEvent,由它调用真正执行连接的 StartConnectToNetwork。
2.3 StaStateMachine:准备 supplicant 里的网络
StartConnectToNetwork 是下行链路里最值得展开的一段,主要步骤:
1. 随机 MAC 处理(ConfigRandMacSelfCure / SetRandomMac)
2. 从 WifiSettings 取出 WifiDeviceConfig
3. ClearDeviceConfig:清掉 wpa_supplicant 里旧的网络
4. GetNextNetworkId:等价于 HDI AddNetwork,拿到新的 supplicant networkId
5. ConvertDeviceCfg:把 WifiDeviceConfig 转成 WifiHalDeviceConfig
6. WifiStaHalInterface::SetDeviceConfig(0, ...) 写入 supplicant 网络参数
7. WifiStaHalInterface::Connect(0, ifaceName) 发起连接
代码里有一个容易看晕的细节:这里传的是 WPA_DEFAULT_NETWORKID,它在 6.1 Release 源码里就是 0:
#define WPA_DEFAULT_NETWORKID 0
因为前面刚执行过 ClearDeviceConfig,新 AddNetwork 出来的网络通常就是 0 号网络,所以 SetDeviceConfig(0, ...) 和 Connect(0, ...) 指向的就是刚创建的这个网络。
ConvertDeviceCfg 还会做几件容易被忽略的事:
- 把框架层的 keyMgmt 转换成 supplicant 认识的 key_mgmt 字符串。
- WPA2/WPA3 混合网络时通过 GetSuitableKeyMgmtForWpaMixed 选择合适的 key_mgmt。
- SAE 网络强制 PMF,并收紧协议与加密算法组合。
- WEP、EAP、WAPI 的参数字段补齐。
2.4 WifiStaHalInterface 与 WifiHdiWpaClient
WifiStaHalInterface 是框架层看到的“HAL 门面”,实际实现并不直接打开 wpa_supplicant 的 control socket,而是通过 WifiHdiWpaClient 走 HDI:
WifiErrorNo WifiStaHalInterface::Connect(int networkId, const std::string &ifaceName)
{
...
return mHdiWpaClient->ReqConnect(networkId, ifaceName.c_str());
}
WifiHdiWpaClient 再调用 HDI C 接口:
WifiErrorNo WifiHdiWpaClient::ReqConnect(int networkId, const char *ifaceName)
{
return HdiWpaStaConnect(networkId, ifaceName);
}
HdiWpaStaConnect 最终调用的不是“connect”,而是 supplicant 语义里的 SELECT_NETWORK:
int32_t result = wpaObj->SelectNetwork(wpaObj, ifaceName, networkId);
IWpaInterface 是 HDI v2_0 里 wpa_interface_service 暴露的接口。HdiWpaStart 会通过 HDIDeviceManagerGet + LoadDevice("wpa_interface_service") 加载服务,再用 IWpaInterfaceGetInstance 拿到 IWpaInterface 实例。
网络参数写入链路也是类似的:
StaStateMachine::ConvertDeviceCfg
-> WifiStaHalInterface::SetDeviceConfig
-> WifiHdiWpaClient::SetDeviceConfig
-> 组装 SetNetworkConfig[]
-> HdiWpaStaSetNetwork
-> IWpaInterface::SetNetwork(ifaceName, networkId, fieldName, value)
wifi_hdi_wpa_sta_impl.c 里维护了一张字段映射表,把框架层枚举映射成 wpa_supplicant 配置项:
ssid -> ssid
psk -> psk / sae_password
keyMgmt -> key_mgmt
eap -> eap
identity -> identity
password -> password
phase2 -> phase2
bssid -> bssid
wep_key0..3 -> wep_key0..wep_key3
priority -> priority
scan_ssid -> scan_ssid
ieee80211w -> ieee80211w
proto/pairwise/group -> proto / pairwise / group
这里也能解释为什么框架层连接前要先做“参数翻译”:supplicant 只认自己的 network 配置项,框架层的 WifiDeviceConfig 不能直接塞给 supplicant。
三、上行事件:wpa_supplicant 如何告诉框架“我连上了”
连接是异步的,SelectNetwork 返回成功只代表 supplicant 接受了请求,不代表已经关联成功。真正的结果通过事件回调返回。
3.1 StaMonitor 注册回调
StaMonitor::InitStaMonitor 会构造一个 WifiEventCallback,通过 WifiStaHalInterface::RegisterStaEventCallback 注册到 HAL 层:
WifiEventCallback callBack = {
[this](int status, int code, const std::string &bssid, int locallyGenerated) {
this->OnConnectChangedCallBack(status, code, bssid, locallyGenerated);
},
...
};
WifiStaHalInterface::GetInstance().RegisterStaEventCallback(callBack, ifaceName);
3.2 HDI 回调到 WifiEventCallback
wifi_hdi_wpa_callback.cpp 实现 HDI 侧的 IWpaCallback,例如:
- OnEventConnected -> 解析 HdiWpaConnectParam 中的 bssid,转成字符串后回调 cbk.onConnectChanged(HAL_WPA_CB_CONNECTED, ...)。
- OnEventDisconnected -> 解析断开原因和 bssid,回调 cbk.onConnectChanged(HAL_WPA_CB_DISCONNECTED, ...)。
- 还有 OnEventBssidChanged、OnEventStateChanged、OnEventAssociateReject、OnEventAuthTimeout 等事件。
3.3 StaMonitor 把事件变成状态机消息
StaMonitor::OnConnectChangedCallBack 不直接改状态,而是把事件转成 InternalMessage:
case HAL_WPA_CB_CONNECTED: {
InternalMessagePtr msg = pStaStateMachine->CreateMessage(
WIFI_SVR_CMD_STA_NETWORK_CONNECTION_EVENT);
msg->SetParam1(code);
msg->AddStringMessageBody(bssid);
pStaStateMachine->SendMessage(msg);
break;
}
断连事件则变成 WIFI_SVR_CMD_STA_NETWORK_DISCONNECTION_EVENT。
StaStateMachine 收到 WIFI_SVR_CMD_STA_NETWORK_CONNECTION_EVENT 后,会更新 linkedInfo、清理连接超时定时器,并切换到 GetIpState,进入“拿 IP”阶段。
四、最后一公里:DHCP 客户端拿 IP
802.11 关联成功不代表可以上网,OpenHarmony 还会走 DHCP 获取 IPv4/IPv6 地址。
4.1 进入 GetIpState
StaStateMachine::GetIpState::GoInState 做的事:
- 通过 WifiNetAgent::OnStaMachineUpdateNetSupplierInfo 更新网络供应商信息。
- 上报 CONNECT_OBTAINING_IP 连接状态。
- 读取 WifiDeviceConfig 的 assignMethod,判断是 DHCP 还是静态 IP。
- 调用 HandlePreDhcpSetup,例如关闭 supplicant 的 power save / suspend。
- 注册 DHCP 回调,然后 StartDhcpClient(config)。
4.2 DHCP 回调注册与启动
DHCP 客户端 API 在 foundation/communication/dhcp/interfaces/kits/c/dhcp_c_api.h:
RegisterDhcpClientCallBack(ifname.c_str(), &dhcpclientCallBack_);
RegisterDhcpClientReportCallBack(ifname.c_str(), &dhcpClientReport_);
StartDhcpClient(config);
回调绑定:
dhcpclientCallBack_.OnIpSuccessChanged = DhcpResultNotify::OnSuccess;
dhcpclientCallBack_.OnIpFailChanged = DhcpResultNotify::OnFailed;
RouterConfig 里会带上接口名、bssid、是否需要 IPv4/IPv6、是否禁止使用缓存 IP 等信息。真正跑 DHCP 状态机的是独立的 DHCP Client SA,不在 Wi-Fi 服务进程里。
4.3 DHCP 结果回到状态机
拿到 IP 后,DhcpResultNotify::OnSuccess -> OnSuccessDhcpResult:
- 保存 IPv4/IPv6 结果到状态机内部的 DhcpIpv4Result / DhcpIpv6Result。
- 调用 DhcpResultNotifyEvent(DhcpReturnCode::DHCP_RESULT, ipType)。
- 发送 WIFI_SVR_CMD_STA_DHCP_RESULT_NOTIFY_EVENT 给状态机。
GetIpState::DealDhcpResultNotify 再根据结果处理:
- DHCP_RESULT -> DealDhcpResult(ipType)。
- DHCP_JUMP -> 跳到 LinkedState,连接完成。
- DHCP_FAIL -> 按 DHCP 失败处理,可能触发断开和失败原因上报。
- DHCP_OFFER_REPORT -> 处理 OFFER 上报。
另外还启动了 30 秒的 CMD_START_GET_DHCP_IP_TIMEOUT 定时器,超时走 DealGetDhcpIpv4Timeout,最终会断开并上报 DISCONNECT_BY_DHCP_FAIL。
拿到 IP 后调用 HandlePostDhcpSetup 恢复 power save / suspend 设置,再通过 WifiNetAgent 把 NetSupplierInfo 更新给网络管理,上层网络栈才能认为 Wi-Fi 这条链路真正可用。
更多推荐

所有评论(0)