Matter OTA Provider Linux 参考应用完全指南:构建、命令行参数与源码原理
【免费下载链接】connectedhomeipMatter (formerly Project CHIP) creates more connections between more objects, simplifying development for manufacturers and increasing compatibility for consumers, guided by the Connectivity Standards Alliance.项目地址: https://gitcode.com/GitHub_Trending/co/connectedhomeip
本篇指南围绕 connectedhomeip(Matter 参考实现)中的 Linux OTA Provider 参考应用展开,它以examples/ota-provider-app/linux为骨架,完整实现了一个 OTA Software Update Provider Cluster Server(OTA 软件升级提供方集群服务器),用于向同 Fabric 内的 OTA Requestor 设备提供固件升级镜像。读完本文,你将掌握该应用的构建方法、全部命令行参数的含义与默认值、--otaImageListJSON 配置文件的编写规范、OTA 镜像头(Software Image Header)的生成方式,以及通过 ACL 授权让 Requestor 正常调用 QueryImage 的完整配置流程,并深入理解其底层源码实现。
应用定位与整体结构
OTA Provider 是 Matter OTA 升级链路中的"服务端"角色:OTA Requestor(请求方,通常是终端设备)向 Provider 发送QueryImage命令询问是否有可用更新,Provider 依据自身策略返回QueryImageResponse;随后双方通过 BDX(Bulk Data Exchange)协议传输固件镜像,Requestor 下载完成后调用ApplyUpdateRequest与NotifyUpdateApplied完成升级闭环。
Linux 版本的 OTA Provider 是一个完整可运行的参考实现,其核心源码分布如下:
- 入口与参数解析:命令行选项定义、JSON 镜像列表解析、
ApplicationInit初始化; - OTA Provider 核心逻辑:
OTAProviderDelegate接口的三个命令处理函数HandleQueryImage、HandleApplyUpdateRequest、HandleNotifyUpdateApplied,以及镜像选择、用户同意、BDX 会话初始化等; - BDX 发送器:继承
chip::bdx::Responder,负责以发送方角色执行 BDX 传输(头文件见 BdxOtaSender.h); - 构建配置 与 args.gni。
应用将 OTA Provider 集群部署在endpoint 0(见 main.cpp 中的kOtaProviderEndpoint = 0),并通过chip::app::Clusters::OTAProvider::SetDelegate(kOtaProviderEndpoint, &GetOtaProviderExample())将委托实例注册到集群服务器上。启动阶段还会把BdxOtaSender注册为 BDX 协议的无请求消息处理器,从而能够接收 Requestor 发起的 BDX 传输请求(见 main.cpp)。
构建 OTA Provider 应用
推荐使用仓库自带的 GN 构建脚本,命令如下:
scripts/examples/gn_build_example.sh examples/ota-provider-app/linux out/debug chip_config_network_layer_ble=falseexamples/ota-provider-app/linux:待构建的 example 目标;out/debug:构建输出目录;chip_config_network_layer_ble=false:禁用 BLE 网络层(Linux 平台通常使用 IP 网络完成发现与配对)。
构建产物为可执行文件chip-ota-provider-app,默认输出到out/debug目录。从 BUILD.gn 可以看到,该目标依赖ota-provider-common、app-main(Linux 平台应用框架)、ota-provider集群实现、user-consent(用户同意模块)、bdx协议栈以及jsoncpp(用于解析镜像列表 JSON)。
命令行参数完全解析
应用通过ChipLinuxAppInit(argc, argv, &cmdLineOptions)解析参数,完整选项定义见 main.cpp。下表整理了所有选项及其在源码中的行为,标注了默认值与"首次响应后回退"策略。
| 选项 | 说明 |
|---|---|
-a, --applyUpdateAction <proceed \| awaitNextAction \| discontinue> | 首次ApplyUpdateResponse中Action字段的值;后续所有响应固定使用proceed。源码中发送响应后会把mUpdateAction重置为kProceed(见 OTAProviderExample.cpp) |
-c, --userConsentNeeded | 若提供,QueryImageResponse的UserConsentNeeded字段置为true;仅当 QueryImage 命令中RequestorCanConsent为 true 时生效,否则该字段为false(见 SendQueryImageResponse) |
-f, --filepath <file path> | 包含 OTA 镜像的文件路径,应用将自动把该文件提供给 OTA Requestor |
-i, --imageUri <uri> | QueryImageResponse中ImageURI字段的值;若未提供,应用会基于节点 ID 与文件设计符自动生成一个合法的 BDX URI |
-m, --maxBDXBlockSize <size> | BDX 传输最大块大小;若未提供,使用默认值 1024 字节(源码常量kMaxBdxBlockSize,见 OTAProviderExample.cpp)。注意:该选项未出现在 README 表格中,但已由源码支持 |
-o, --otaImageList <file path> | 包含 OTA 镜像列表的 JSON 文件路径 |
-p, --delayedApplyActionTimeSec <秒> | 首次ApplyUpdateResponse中DelayedActionTime字段的值;后续响应固定为 0 |
-q, --queryImageStatus <updateAvailable \| busy \| updateNotAvailable> | 首次QueryImageResponse中Status字段的值;后续响应固定回退为updateAvailable |
-t, --delayedQueryActionTimeSec <秒> | 首次QueryImageResponse中DelayedActionTime字段的值;后续响应固定为 0 |
-u, --userConsentState <granted \| denied \| deferred> | 首次QueryImageResponse的用户同意状态;后续固定为granted。注意--queryImageStatus优先级更高(覆盖本选项),三者映射关系为:granted→updateAvailable、denied→updateNotAvailable、deferred→busy |
-x, --ignoreQueryImage <次数> | 忽略(不响应)QueryImage 命令的次数,用于测试超时/无响应场景 |
-y, --ignoreApplyUpdate <次数> | 忽略 ApplyUpdate 请求的次数 |
-P, --pollInterval <毫秒> | BDX 传输的轮询间隔;默认 50ms(源码常量kBdxServerPollIntervalMillis) |
--persistQueryImageStatus | 长选项(无短形式)。提供后,--queryImageStatus及其DelayedActionTime将用于每一次QueryImageResponse,而不是首次响应后回退到updateAvailable,便于持续模拟busy/updateNotAvailable状态(见 main.cpp) |
其中若干选项与源码实现存在深层关联:
- 状态回退机制:默认情况下,
busy、updateNotAvailable等状态被设计为"一次性"条件——服务完一次后,ApplyQueryImageStatusAfterResponse()会将状态重置回updateAvailable、延时归零,避免测试套件因 Provider 卡死在异常状态而反复重启(见 OTAProviderExample.cpp)。如需持续保持特定状态,请使用--persistQueryImageStatus。 - 用户同意状态:
deferred在内部映射为UserConsentState::kObtaining(获取中),此时 Provider 返回busy状态;granted映射为kGranted、denied映射为kDenied(见 main.cpp)。 - 忽略计数:
--ignoreQueryImage N会让前 N 次 QueryImage 请求"石沉大海"(不发送任何响应也不发送错误状态),对应HandleQueryImage开头的计数判断(见 OTAProviderExample.cpp)。
除上述选项外,应用还继承 Linux 平台通用参数,例如--discriminator(长鉴别码,默认 3840)、--secured-device-port(安全端口,默认 5540)、--KVS(KVS 存储位置,默认/tmp/chip_kvs)、--autoApplyImage等,具体可参考 OTA Requestor Linux README 中的运行示例。
一个典型启动命令如下(参考 ota-requestor-app/linux/README.md):
out/chip-ota-provider-app --discriminator 22 --secured-device-port 5565 --KVS /tmp/chip_kvs_provider --filepath /tmp/ota-image.bin使用--filepath与--otaImageList
两种提供镜像的方式存在严格的约束,参数解析逻辑见 main.cpp:
- 二者不能同时提供:
HandleOptions通过静态标志位检测,若先出现-f再出现-o(或反之),会直接报错退出; - 至少必须提供其一:若两者都为
nullptr,ApplicationInit会记录错误日志 "Either an OTA file or image list file must be specified" 并调用chipDie()终止进程(见 main.cpp); - 提供
--filepath时,应用直接将该文件作为唯一可提供的镜像; - 提供
--otaImageList时,应用解析 JSON 文件,从中选取"最新且有效"的软件版本,并把对应 OTA 文件发送给 Requestor。
镜像列表 JSON 格式
--otaImageList指向的 JSON 文件以deviceSoftwareVersionModel数组为核心(解析逻辑见 main.cpp)。注意:文件顶部允许出现 C/C++ 风格注释(builder["collectComments"] = true),README 示例中的{ "foo": 1, // ignored by parser正是利用了这一特性——解析器会忽略该无效字段。
{ "foo": 1, // ignored by parser "deviceSoftwareVersionModel": [ { "vendorId": 1, "productId": 1, "softwareVersion": 10, "softwareVersionString": "1.0.0", "cDVersionNumber": 18, "softwareVersionValid": true, "minApplicableSoftwareVersion": 0, "maxApplicableSoftwareVersion": 100, "otaURL": "/tmp/ota_v10.bin" }, { "vendorId": 1, "productId": 1, "softwareVersion": 20, "softwareVersionString": "1.0.1", "cDVersionNumber": 18, "softwareVersionValid": false, "minApplicableSoftwareVersion": 0, "maxApplicableSoftwareVersion": 100, "otaURL": "/tmp/ota_v20.bin" }, { "vendorId": 1, "productId": 1, "softwareVersion": 30, "softwareVersionString": "1.0.2", "cDVersionNumber": 18, "softwareVersionValid": true, "minApplicableSoftwareVersion": 0, "maxApplicableSoftwareVersion": 100, "otaURL": "/tmp/ota_v30.bin" }, { "vendorId": 1, "productId": 1, "softwareVersion": 40, "softwareVersionString": "1.1.0", "cDVersionNumber": 18, "softwareVersionValid": true, "minApplicableSoftwareVersion": 0, "maxApplicableSoftwareVersion": 100, "otaURL": "/tmp/ota_v40.bin" }, { "vendorId": 1, "productId": 1, "softwareVersion": 50, "softwareVersionString": "1.1.1", "cDVersionNumber": 18, "softwareVersionValid": false, "minApplicableSoftwareVersion": 0, "maxApplicableSoftwareVersion": 100, "otaURL": "/tmp/ota_v50.bin" } ] }各字段含义与源码默认值(解析时缺省即采用,见 main.cpp):
| 字段 | 说明 | 解析默认值 |
|---|---|---|
vendorId | 厂商 ID | 1 |
productId | 产品 ID | 1 |
softwareVersion | 软件版本号(uint32) | 10 |
softwareVersionString | 软件版本字符串 | "1.0.0" |
cDVersionNumber | 认证声明(CD)版本号 | 0 |
softwareVersionValid | 该版本是否有效(可选) | true |
minApplicableSoftwareVersion | 适用的最低请求方版本 | 0 |
maxApplicableSoftwareVersion | 适用的最高请求方版本 | 1000 |
otaURL | OTA 镜像文件路径 | "https://test.com" |
版本校验与镜像选择
从源码可以确认两条关键行为:
版本一致性校验:
SetOTACandidates()会逐个打开候选镜像并解析其头部,通过VerifyOrDie强制校验 JSON 中的vendorId、productId、softwareVersion、softwareVersionString、min/maxApplicableSoftwareVersion与镜像头完全一致;只要有一项不一致,进程即终止(见 OTAProviderExample.cpp)。因此务必保证otaURL指向的文件确实带有与 JSON 条目匹配的镜像头。候选选择算法:
SelectOTACandidate()首先按softwareVersion升序排序所有候选,然后依次遍历,选出满足以下全部条件的候选:softwareVersionValid == true;- 候选版本号
>请求方当前版本号(requestorSoftwareVersion < candidate.softwareVersion); - 请求方当前版本号落在
[minApplicableSoftwareVersion, maxApplicableSoftwareVersion]区间内。
由于候选已升序排列,最终命中的将是满足条件中版本最高的一个(见 OTAProviderExample.cpp)。若没有任何候选命中,
QueryImageResponse的状态会置为updateNotAvailable。对照上述示例 JSON:v10 对应"有效",请求方版本为 0 时会命中 v10;而
softwareVersionValid=false的 v20、v50 永远不会被选中。同时注意,Provider 端版本判定逻辑与 OTA Requestor 侧的行为(仅当响应版本高于当前运行版本时才继续下载)相互配合,共同构成完整的升级前提校验。
软件镜像头(Software Image Header)
Matter 规范(第 11.21.1 节)要求所有 Matter 软件镜像必须携带一个软件镜像头。仓库提供了 ota_image_tool.py 用于在固件上生成所需头部。凡是通过--filepath或--otaImageList提供给 OTA Provider 的镜像,都必须包含该头部——Provider 会从头部解析SoftwareVersion字段并填入QueryImageResponse。
例如,为一个软件版本号为 2 的固件生成带头镜像:
src/app/ota_image_tool.py create -v 0xDEAD -p 0xBEEF -vn 2 -vs "2.0" -da sha256 firmware.bin firmware.ota参数含义:-v指定厂商 ID(0xDEAD)、-p指定产品 ID(0xBEEF)、-vn指定版本号(2)、-vs指定版本字符串("2.0")、-da指定摘要算法(sha256),输出为firmware.ota。
在 Provider 端,镜像头的解析路径为:ParseOTAHeader()以二进制方式读取文件前 1024 字节(常量kOtaHeaderMaxSize),交给OTAImageHeaderParser的AccumulateAndDecode解码(见 OTAProviderExample.cpp)。当使用--filepath直接提供单个镜像时,每次HandleQueryImage都会重新解析镜像头来取得mSoftwareVersion与mSoftwareVersionString(见 OTAProviderExample.cpp)。
如需构建一个携带指定软件版本的 OTA Requestor 应用进行端到端验证,请参考 OTA Requestor Linux README 中 "Generate Images" 一节:其流程是先修改CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION为更大版本号,再重新构建 Requestor 并用ota_image_tool.py为生成的可执行文件打上匹配版本号的镜像头,最后用带--autoApplyImage的旧版本 Requestor 应用发起升级。
访问控制要求(ACL)
OTA Provider 集群的可用性依赖 ACL(Access Control List,访问控制列表)授权。Commissioner 或 Administrator应当在配网时或之后安装必要的 ACL 条目,允许同 Fabric 内的 OTA Requestor 处理QueryImage命令,否则该 Provider 对 Requestor 不可用。
由于ACL属性本身是一个列表,写入时不能只包含新条目,而必须先读取现有条目再连同新条目一并写入。下面是一个写入两条 ACL 条目的完整示例:
out/chip-tool accesscontrol write acl '[{"fabricIndex": 1, "privilege": 5, "authMode": 2, "subjects": [112233], "targets": null}, {"fabricIndex": 1, "privilege": 3, "authMode": 2, "subjects": null, "targets": [{"cluster": 41, "endpoint": null, "deviceType": null}]}]' 0xDEADBEEF 0- 条目 1:配网时自动创建的原始条目,向节点 ID 112233(默认控制器节点 ID)授予
privilege: 5(Administer,管理)权限,覆盖所有 endpoint 上的所有集群; - 条目 2:新增条目,向所有节点(
subjects: null)授予privilege: 3(Operate,操作)权限,目标为cluster: 41——即 OTA Provider 集群(0x0029 的十进制值)——覆盖所有 endpoint。
该示例的适用前提:Provider 位于 fabric index 1,节点 ID 为0xDEADBEEF,endpoint 为 0。authMode: 2表示使用 Case(证书认证会话模式)。注意:endpoint: null与deviceType: null表示不限制 endpoint 与设备类型,targets: null表示不限制目标。
当前限制(Current Limitations)
README 明确了该参考实现目前的已知限制,对应源码也可找到佐证:
- 仅支持同步 BDX 传输:
BdxOtaSender使用轮询式事件处理(默认每 50ms 轮询一次,可经--pollInterval调整),不支持异步/多会话并行; - 不校验 VID/PID:
SelectOTACandidate()的注释明确说明当前以 vendorId/productId 作为查询主键的校验尚未启用("VendorID and ProductID will be the primary key when querying the DCL servers. If not we can add the vendor/product ID checks here.",见 OTAProviderExample.cpp); - 同一时刻仅支持一个传输:
SendQueryImageResponse()中若InitializeTransfer失败(说明已有 BDX 传输正在进行),会将状态临时切换为kBusy(见 OTAProviderExample.cpp);同时HandleApplyUpdateRequest中也留有 TODO 注释,说明尚未通过追踪updateToken来支持多传输(见 OTAProviderExample.cpp)。
测试与验证
该示例附带单元测试,可用于验证镜像列表版本校验与 BDX 会话逻辑:
- TestOTAProviderExample.cpp:覆盖 OTA Provider 示例核心逻辑;
- TestBdxOtaSenderPeerBinding.cpp:覆盖 BDX 发送器的对端绑定与会话行为。
实际验证 OTA 升级链路时,推荐组合使用 OTA Provider 与 OTA Requestor Linux 应用:分别以不同的--discriminator、--secured-device-port、--KVS启动两者并完成配网后,即可观察 QueryImage → BDX 传输 → ApplyUpdate 的完整流程。Requestor 侧的日志(如 "Available update version X is <= current version Y, update ignored")可帮助诊断版本校验失败,而 "Image does not contain a valid header" 则提示镜像缺少头部。
【免费下载链接】connectedhomeipMatter (formerly Project CHIP) creates more connections between more objects, simplifying development for manufacturers and increasing compatibility for consumers, guided by the Connectivity Standards Alliance.项目地址: https://gitcode.com/GitHub_Trending/co/connectedhomeip
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考