4. WifiStateMachine 详解:状态机层次结构、Supplicant 交互、驱动加载与卸载
各位同学,今天我们来啃一块硬骨头——WifiStateMachine。
说实话,我刚接触 Android Wi-Fi 时,最头疼的就是这个状态机。代码动辄几千行,状态嵌套状态,回调套回调。但后来我发现,搞懂它,你就掌握了 Wi-Fi 框架的命脉。
4.1 状态机设计哲学:为什么非要用状态机?
Wi-Fi 的工作流程不是线性的。你想想看:
- 用户点击「打开 Wi-Fi」
- 驱动要加载
- Supplicant 要启动
- 扫描要执行
- 连接要处理
- 断线要重连
这些操作互相交织,如果用 if-else 写,代码会变成一团乱麻。状态机的好处是:每个状态只关心自己该做的事,状态切换由框架自动完成。
核心思想:WifiStateMachine 继承自 StateMachine,内部维护了一个状态栈。消息进来后,从当前状态开始逐级向上传递,直到某个状态处理了它。
4.2 状态层次结构:一张图看懂
我个人习惯把 WifiStateMachine 的状态分为三层:
嗯,这里要注意:状态是分层的。比如 ConnectModeState 处理连接相关的通用逻辑,而它的子状态只处理具体场景。消息如果被子状态消费了,就不会往上冒泡。
避坑指南:我曾经在定制 Wi-Fi 功能时,往错误的状态里发了消息,结果驱动加载了但 Supplicant 没启动。排查了半天才发现是状态没切过去。记住:发消息前先确认当前状态。
4.3 Supplicant 交互:HIDL 与 Socket 的双通道
WifiStateMachine 和 wpa_supplicant 之间怎么通信?说白了就两条路:
- HIDL 接口(Android 8.0+):通过 IWifiStaIface.hal 调用,比如 startScan()、connect()。
- Socket 监听:wpa_supplicant 通过 UNIX socket 上报事件,比如 SCAN_RESULTS、CONNECTED、DISCONNECTED。
我建议你重点关注事件处理流程。举个例子:
// WifiMonitor 收到 supplicant 事件
private void handleSupplicantEvent(String event) {
if (event.startsWith("CTRL-EVENT-CONNECTED")) {
// 发送消息到状态机
sendMessage(WifiStateMachine.CMD_SUPPLICANT_CONNECTED);
} else if (event.startsWith("CTRL-EVENT-DISCONNECTED")) {
sendMessage(WifiStateMachine.CMD_SUPPLICANT_DISCONNECTED);
}
}
// 状态机处理
case CMD_SUPPLICANT_CONNECTED:
transitionTo(mObtainingIpState);
break;
你看,事件驱动状态切换,这就是状态机的精髓。
注意:Supplicant 事件是异步的。如果你在状态机里同步等待某个事件,系统可能会 ANR。我踩过这个坑——当时在 ConnectedState 里直接调了阻塞 API,结果 UI 线程卡死。后来改成异步回调 + 超时机制才解决。
4.4 驱动加载与卸载:从固件到内核
驱动加载这块,很多同学觉得离应用层很远。其实 WifiStateMachine 里就有触发点。
流程大致是这样的:
- 用户打开 Wi-Fi → WifiServiceImpl 调用 WifiStateMachine 的 setWifiEnabled(true)
- 状态机切换到 DriverLoadingState
- 调用 WifiNative 的 loadDriver() → 通过 HIDL 调用内核模块
- 内核加载固件(比如 wlan.ko)→ 初始化硬件
- 启动 Supplicant → 状态机切换到 SupplicantStartingState
- Supplicant 启动完成 → 切换到 DisconnectedState
卸载流程反过来:
case CMD_WIFI_TOGGLED:
if (!enabled) {
// 先断开连接
transitionTo(mDisconnectingState);
// 然后停止 Supplicant
mWifiNative.stopSupplicant();
// 最后卸载驱动
mWifiNative.unloadDriver();
transitionTo(mInitialState);
}
break;
关键点:驱动加载和 Supplicant 启动是串行的。驱动没加载完,Supplicant 启动会失败。我曾经在低端设备上遇到过驱动加载超时,结果 Supplicant 一直起不来,Wi-Fi 开关打不开。后来加了重试机制才稳定。
4.5 实战:如何调试状态机?
调试 WifiStateMachine 其实有套路。我个人常用三板斧:
| 方法 | 命令/工具 | 说明 |
|---|---|---|
| 查看当前状态 | dumpsys wifi | grep "mStateMachine" |
直接看状态机当前在哪个状态 |
| 跟踪消息 | logcat -s WifiStateMachine | 观察消息流转,看哪个状态处理了消息 |
| 模拟事件 | adb shell cmd wifi send-trigger |
手动触发扫描、连接等事件 |
举个例子,你发现 Wi-Fi 连不上,先跑 dumpsys wifi 看看状态机是不是卡在 ObtainingIpState。如果是,那问题大概率在 DHCP 上,而不是 Supplicant。
小技巧:我习惯在关键状态切换处加自定义日志。比如在 transitionTo() 之前打印当前状态和目标状态。这样出问题时,日志一看就知道状态机走到哪了。
4.6 总结:状态机就是 Wi-Fi 的「大脑」
WifiStateMachine 把 Wi-Fi 的复杂流程抽象成了状态和消息。你只要记住三点:
- 状态分层:父状态处理通用逻辑,子状态处理具体场景
- 事件驱动:Supplicant 上报事件,状态机响应切换
- 串行加载:驱动 → Supplicant → 连接,一步都不能乱
搞懂这些,你就能在 Wi-Fi 框架里自由穿梭了。下次遇到 Wi-Fi 问题,先问自己一句:「当前状态机在哪个状态?」答案往往就在那里。
公众号:蓝海资料掘金营,微信deep3321