兼容性适配:不同厂商 HAL 差异、Android 版本兼容、CTS 测试

做 Wi-Fi 开发这么多年,我踩过最多的坑,就是兼容性。你想想看,同一套代码,在 Pixel 上跑得飞起,到了某米手机上就频繁断连,到了某为手机上扫描列表出不来。嗯,这就是我们今天要聊的核心问题——兼容性适配。

说白了,Android 的 Wi-Fi 框架是个「半开放」的系统。Google 定义了标准接口,但具体实现交给了各家芯片厂商和手机 OEM。这就导致了大量的碎片化问题。我个人习惯把兼容性适配分成三个维度来考虑:厂商 HAL 差异Android 版本兼容CTS 测试。下面我们一个一个说。

一、厂商 HAL 差异:同一个接口,不同的脾气

Wi-Fi HAL(硬件抽象层)是 Framework 和底层驱动之间的桥梁。Google 定义了通用的 HAL 接口,但 Qualcomm、MTK、展锐、海思等芯片厂商都有自己的实现。我在项目中遇到过最典型的一个问题:同样是调用 startSoftAp(),Qualcomm 的 HAL 会返回一个 WifiConfiguration 对象,而 MTK 的 HAL 可能返回 null 但实际已经启动成功了。

核心差异点:

  • HAL 版本差异:Android 8.0 之前是 HAL 1.0,之后逐步演进到 HAL 1.6、AIDL HAL。不同版本的方法签名、回调机制完全不同。
  • 私有扩展接口:各家厂商会在标准 HAL 之外添加私有接口,比如「Wi-Fi 信号增强模式」、「双 Wi-Fi 加速」等。这些接口在 AOSP 源码里根本找不到。
  • 参数校验严格程度:有些厂商的 HAL 对参数校验非常严格,比如 SSID 长度、密码格式、信道范围等。传错一个参数就直接返回错误,而不会帮你做容错。

我曾经调试过一个 Bug:某款手机调用 setWifiEnabled(true) 后,Wi-Fi 状态一直卡在 WIFI_STATE_ENABLING。查了两天才发现,是 HAL 层在初始化时要求先调用 setCountryCode(),否则就卡死。而 AOSP 的参考实现并没有这个前置条件。

我的建议:在开发阶段,尽量多拿几款不同芯片平台的手机做测试。如果条件有限,至少覆盖 Qualcomm 和 MTK 这两大主流平台。另外,写代码时不要假设 HAL 的行为是「标准」的,多做异常处理和超时保护。

二、Android 版本兼容:从 Pie 到 14,变化有多大?

Android 每个大版本都会对 Wi-Fi 模块做调整。从 Android 9 到 Android 14,我数了数,光 Wi-Fi 相关的 API 变更就有 30 多处。这里我挑几个最关键的来说。

Android 版本 关键变化 影响范围
Android 9 (Pie) 引入 Wi-Fi 建议者 API(WifiNetworkSuggestion) 第三方 App 连接 Wi-Fi 的方式彻底改变
Android 10 (Q) WifiManager 部分 API 废弃,改用 WifiNetworkSpecifier 热点扫描、连接逻辑需要重写
Android 11 (R) 新增 Passpoint 相关 API,Wi-Fi 锁行为变更 后台扫描策略受影响
Android 12 (S) Wi-Fi 权限拆分,新增 NEARBY_WIFI_DEVICES 权限 App 需要适配新的权限模型
Android 13 (T) Wi-Fi 热点 API 重构,新增 WifiManager.addNetworkRecommendation() 热点管理逻辑需要更新
Android 14 (U) Wi-Fi 扫描结果缓存机制优化,新增 6GHz 频段支持 扫描逻辑需要适配新频段

为什么会这样?Google 其实一直在推动 Wi-Fi 模块的「去碎片化」。每次版本更新,都会废弃一些旧的、厂商自定义过多的 API,推出更统一的新接口。但问题是,OEM 厂商的适配速度跟不上。你想想看,Android 14 都发布了,市面上还有大量 Android 11、12 的设备在跑。

避坑指南:我曾经在适配 Android 12 的权限模型时翻过车。旧代码里直接请求 ACCESS_FINE_LOCATION 就能扫描 Wi-Fi,但 Android 12 要求必须同时声明 NEARBY_WIFI_DEVICES 权限。结果我的 App 在 Android 12 设备上扫描结果一直为空,用户还以为是手机坏了。嗯,从那以后我养成了一个习惯:每次适配新版本,第一件事就是检查权限变更。

三、CTS 测试:不只是为了过认证

CTS(Compatibility Test Suite)是 Google 用来保证 Android 设备兼容性的测试套件。很多开发者觉得 CTS 是 OEM 厂商的事,跟自己没关系。其实不然。如果你的 Wi-Fi Service 要集成到系统级产品中,CTS 测试就是一道绕不过去的坎。

CTS 中与 Wi-Fi 相关的测试用例大概有 200 多个,覆盖了以下几个方面:

  • Wi-Fi 开关控制:测试 enable/disable 的稳定性、状态回调的正确性
  • 扫描功能:测试扫描结果的完整性、扫描间隔的合规性
  • 连接/断开:测试各种认证方式(WPA2、WPA3、Open)的连接成功率
  • 热点功能:测试 SoftAP 的启动、客户端连接、频段切换等
  • P2P 功能:测试 Wi-Fi Direct 的设备发现、连接、数据传输
  • Passpoint:测试热点 2.0 的自动连接、凭证管理

我记得有一次,我们的 Wi-Fi Service 在 CTS 测试中频繁失败。排查后发现,是 HAL 层在返回扫描结果时,没有按照 CTS 要求的格式填充 ScanResult 中的 capabilities 字段。CTS 会严格校验这个字段的格式,少一个空格都会判失败。

CTS 测试的实用价值:即使你的产品不需要过 Google 认证,我也建议你跑一遍 CTS 的 Wi-Fi 相关用例。因为这些用例覆盖了 Wi-Fi 功能的各种边界情况,能帮你发现很多平时注意不到的 Bug。我个人的习惯是:每次修改 Wi-Fi 相关代码后,都会在本地跑一遍 CTS 的 Wi-Fi 模块,确保没有引入回归问题。

四、实战:如何构建兼容性适配方案?

说了这么多问题,那到底怎么解决?我总结了一套自己的方法论,分享给你。

第一步:建立兼容性矩阵

把你要支持的设备按「芯片平台 + Android 版本」两个维度列出来。比如:

  • Qualcomm + Android 12
  • Qualcomm + Android 13
  • MTK + Android 12
  • MTK + Android 13
  • 展锐 + Android 11

每个组合至少准备一台真机。模拟器搞不定 Wi-Fi 的兼容性问题。

第二步:抽象适配层

在 Framework 和 HAL 之间加一层适配层。把所有厂商特有的逻辑封装到这里。比如:

// 适配层接口
public interface IWifiHALAdapter {
    boolean startSoftAp(WifiConfiguration config);
    boolean setCountryCode(String countryCode);
    List<ScanResult> getScanResults();
}

// Qualcomm 实现
public class QcomWifiHALAdapter implements IWifiHALAdapter {
    // 处理 Qualcomm 特有的逻辑
}

// MTK 实现
public class MtkWifiHALAdapter implements IWifiHALAdapter {
    // 处理 MTK 特有的逻辑
}

这样,当遇到厂商差异时,你只需要修改对应的 Adapter 实现,而不需要动上层逻辑。

第三步:自动化兼容性测试

写一套自动化测试脚本,覆盖所有兼容性矩阵中的设备组合。每次代码变更后,自动在云测平台上跑一遍。我建议至少覆盖以下场景:

  • Wi-Fi 开关 100 次循环
  • 扫描 + 连接 50 次循环
  • 热点启动 + 客户端连接 30 次循环
  • P2P 设备发现 + 连接 20 次循环
  • CTS 测试套件中的 Wi-Fi 相关用例

一个小技巧:在自动化测试中,加入「异常注入」环节。比如模拟 HAL 返回 null、模拟扫描超时、模拟连接失败等。这样能提前发现代码的健壮性问题,而不是等到用户反馈了才去修。

五、知识体系总览

下面这张图是我整理的 Wi-Fi 兼容性适配的知识体系,你可以对照着检查自己的方案是否完整。

Wi-Fi 兼容性适配知识体系 厂商 HAL 差异 Qualcomm / MTK / 展锐 HAL 版本差异 私有扩展接口 Android 版本兼容 API 变更适配 权限模型变化 行为变更适配 CTS 测试 扫描/连接测试 热点/P2P 测试 Passpoint 测试 实战方案:兼容性矩阵 + 适配层 + 自动化测试 覆盖三大维度,构建完整的兼容性适配方案

兼容性适配这件事,没有银弹。你不可能写一套代码在所有设备上完美运行。但通过系统化的方法——建立兼容性矩阵、抽象适配层、自动化测试——可以把问题控制在可控范围内。嗯,这就是我这些年积累下来的经验,希望对你有帮助。


公众号:蓝海资料掘金营,微信deep3321