RCSP OTA 升级流程
本页面完整记录 Jieli 杰理 HarmonyOS OTA SDK 中基于 RCSP(Real-time Command/Service Protocol)协议的固件升级流程,涵盖从蓝牙连接、RCSP 初始化、升级命令交互、断线重连到升级完成回调的端到端机制。
Purpose and Scope
本页面聚焦于 RCSP OTA 升级流程 这一核心能力,说明以下内容:
BluetoothOTAManager如何组织蓝牙事件与 RCSP/OTA 模块之间的桥接;OTAWrapper如何封装jl-ota(JL_OTA)、jl-rcsp(JL_RCSP)与jl-auth(JLAuth)三个 SDK 库并驱动升级;- RCSP 初始化、设备信息获取、强制升级判断、升级启动、进度回调、断线回连(内部回连与自定义回连)的完整流程;
- 蓝牙层(BLE/SPP)数据收发如何与 RCSP 协议栈对接。
与本页面相邻但不在本页展开的主题:蓝牙扫描/连接的底层实现请参阅 "蓝牙连接管理" 相关页面;升级包文件解析与校验属于 "OTA 升级文件处理" 页面;认证流程属于 "JL Auth 认证" 页面。
Overview
RCSP 是杰理(Jieli)设备的实时命令/服务协议,承载 OTA 升级、设备信息查询、认证等指令。在 HarmonyOS 应用中,RCSP OTA 升级流程由三层协作完成:
- 蓝牙通信层(
BluetoothManager、BleImpl、SppImpl):负责扫描、连接、收发原始数据; - 协议适配层(
BluetoothOTAManager):把蓝牙事件(扫描到设备、连接状态变化、特征值通知)翻译成 OTA 层能理解的回调,同时把 OTA 层要发送的数据通过 BLE/SPP 通道发出去; - OTA 协议层(
OTAWrapper+JL_OTA.RcspOTAManager):真正执行 RCSP 升级指令序列(获取目标信息、下发固件、校验、重启、回连、上报进度)。
整个流程的关键设计意图是:SDK 内部只管 RCSP 协议逻辑,而把"如何扫描、如何连接、如何收发字节"全部抽象成 OTAWrapperOption 回调,由上层(BluetoothOTAManager)按实际的蓝牙类型(BLE 或 SPP)实现。这样协议层与传输层完全解耦,同一套 RCSP 升级逻辑可以同时跑在 BLE 和 EDR/SPP 两种物理链路上。
Architecture
下图展示了 RCSP OTA 升级流程的组件架构与数据流向:
flowchart TD
subgraph sg_App["应用层"]
App["业务页面 / 调用方"]
bluetoothOTAManager["bluetoothOTAManager 单例"]
end
subgraph sg_Adapter["协议适配层"]
BTOManager["BluetoothOTAManager"]
OTAWrapper["OTAWrapper"]
end
subgraph sg_SDK["RCSP SDK 库"]
RcspOTAManager["JL_OTA.RcspOTAManager"]
RcspImpl["JL_RCSP.RcspImpl"]
Auth["JLAuth.Auth"]
CallbackMgr["JL_RCSP.RcspCallbackManager"]
end
subgraph sg_BT["蓝牙通信层"]
BluetoothManager["bluetoothInstance"]
BleImpl["BleImpl (BLE)"]
SppImpl["SppImpl (SPP/EDR)"]
end
App -->|"startOTA()"| bluetoothOTAManager
bluetoothOTAManager -->|"构造 OTAWrapperOption"| BTOManager
BTOManager -->|"startOTA / 回调转发"| OTAWrapper
OTAWrapper -->|"new RcspOTAManager(rcspImpl)"| RcspOTAManager
OTAWrapper -->|"getRCSPImpl / RcspImpl"| RcspImpl
OTAWrapper -->|"认证 / 设备信息"| Auth
OTAWrapper -->|"统一分发 RCSP 回调"| CallbackMgr
RcspOTAManager -->|"sendData / 断开 / 连接"| OTAWrapper
OTAWrapper -->|"otaWrapperOption 回调"| BTOManager
BTOManager -->|"bleImpl.sendData / sppImpl.sendData"| BluetoothManager
BluetoothManager --> BleImpl
BluetoothManager --> SppImpl
BleImpl -->|"NOTIFY 特征值数据"| BTOManager
SppImpl -->|"SOCKET UUID 数据"| BTOManager
各组件职责说明:
| 组件 | 职责 | 关键证据 |
|---|---|---|
BluetoothOTAManager | 单例入口,注册蓝牙事件回调,实现 OTAWrapperOption(扫描/连接/断开/发送数据),转发升级回调 | BluetoothOTAManager.ets |
OTAWrapper | 协议封装层,按 deviceId 管理 RcspOTAManager/RcspImpl/Auth 实例,把 SDK 回调桥接给上层 | OtaWrapper.ets |
JL_OTA.RcspOTAManager | RCSP 升级状态机核心,驱动升级指令序列 | 由 OTAWrapper.startOTA() 创建 |
JL_RCSP.RcspImpl | 单设备 RCSP 协议实例,负责指令编解码与设备信息维护 | _RcspImplMap 按 deviceId 管理 |
BleImpl / SppImpl | 物理传输:BLE 特征值读写 / SPP socket 读写 | BluetoothOTAManager.ets |
设计上采用按 deviceId 分片的实例管理:OTAWrapper 内部用 _RcspOTAManagerMap、_RcspImplMap、_AuthMap 等 Map 分别缓存每个设备的协议对象,从而支持多设备并发升级,且每个设备的回调互不干扰。lib_rcsp/Index.ets 统一对外导出 JL_OTA、JL_RCSP、JLAuth 三个库,应用只需 import 一个入口即可访问全部 SDK 能力:
import * as JL_OTA from "jl-ota";
import * as JL_RCSP from "jl-rcsp"
import * as JLAuth from "jl-auth"
export {
JL_OTA,
JL_RCSP,
JLAuth,
}
Source: lib_rcsp/Index.ets
核心实现详解
RCSP 初始化(init → initRcsp)
BluetoothOTAManager 的 init() 是升级能力的总入口,它分两步完成初始化:
public init() {
//蓝牙 初始化
this.initBluetooth()
//OTAWrapper 初始化
this.initRcsp()
}
Source: BluetoothOTAManager.ets
initBluetooth() 同时向 BLE 与 SPP 两条链路注册事件监听:扫描状态变化(SCAN_STATE_CHANGE)、发现设备(SCAN_DEVICE_FIND)、连接状态变化(CONNECT_STATE_CHANGE)、BLE 特征值变化(CONNECT_BLE_CHARACTERISTIC_CHANGE)以及 SPP 数据读取(CONNECT_DATA_READ_CHANGE)。这些回调随后会被转发给 OTAWrapper,从而把"蓝牙物理事件"翻译成"OTA 协议事件"。
initRcsp() 构造 OTAWrapperOption——这是协议层与传输层的解耦契约,共 5 个必须实现的回调:
| 回调 | 语义 | 实现要点(源码证据) |
|---|---|---|
isUseAuth | 是否需要认证 | 固定返回 true,升级前会走 JLAuth 认证 |
isInnerReconnect | 是否使用 SDK 内部回连 | 固定返回 true;返回 false 时走自定义回连分支(Reconnect 类) |
sanDevice | 扫描设备 | 先扫 BLE(10 秒),若 communicationWay == "EDR" 再补扫 EDR,覆盖升级中设备以 BLE 广播存在的场景 |
connectDevice | 连接设备 | 目前回连只有 BLE 设备,构造 BleDevice 后调用 bluetoothInstance.connect |
disconnectDevice | 断开设备 | 根据已连接列表判断设备是 BLE 还是 SPP,分别断开 |
sendData | 发送 RCSP 数据 | BLE 走 RCSP_UUID_SERVICE/RCSP_UUID_WRITE 特征值,SPP 走 RCSP_SOCKET_UUID |
/**发送数据**/
sendData: (device: OTADevice, data: Uint8Array) => {
// 此处需判断设备类型
const isBle =
this.bluetoothInstance.bleImpl.getConnectedDevice().findIndex(item => item.deviceId == device.deviceId) != -1
const deviceId = device.deviceId
if (isBle) {
this.bluetoothInstance.bleImpl.sendData(new BleDevice(deviceId), RCSP_UUID_SERVICE, RCSP_UUID_WRITE, data)
} else {
this.bluetoothInstance.sppImpl.sendData(new SppDevice(deviceId), RCSP_SOCKET_UUID, data)
}
}
Source: BluetoothOTAManager.ets
initRcsp() 最后执行 this.otaWrapper = new OTAWrapper(this.otaWrapperOption),把传输层能力注入协议层。注意:此阶段还没有建立任何 RCSP 连接,真正的 RCSP 握手发生在设备连接成功、RcspImpl 被创建并初始化之后。
升级启动(startOTA)
BluetoothOTAManager.startOTA(deviceId, otaCallback) 是业务方触发升级的唯一入口。它完成三件事:
- 构造
JL_OTA.OtaConfig并开启新回连方式:otaConfig.isSupportNewRebootWay = true; - 包装业务回调为
OTAUpgradeCallback(补齐回连、RCSP 初始化等 SDK 需要的内部逻辑); - 调用
this.otaWrapper?.startOTA(new OTADevice(deviceId), otaConfig, tempOtaUpgradeCallback)。
public startOTA(deviceId: string, otaCallback: OTAUpgradeCallback) {
const otaConfig: JL_OTA.OtaConfig = new JL_OTA.OtaConfig()
otaConfig.isSupportNewRebootWay = true //支持新回连方式
const tempOtaUpgradeCallback: OTAUpgradeCallback = {
onStartOTA: () => {
otaCallback.onStartOTA()
},
// ... 其余回调包装
onReadData: (offset: number, size: number): Uint8Array | undefined => {
return otaCallback.onReadData(offset,size)
},
onRCSPInit:(deviceId: string, isInit: boolean)=>{
if (isInit) {// Rcsp回调-初始化成功
this._ReconnectMap.forEach(reconnect => {
reconnect.onDeviceConnected(new BluetoothDevice(deviceId))
})
}
}
}
this.otaWrapper?.startOTA(new OTADevice(deviceId), otaConfig, tempOtaUpgradeCallback)
}
Source: BluetoothOTAManager.ets
值得注意的设计细节:
onReadData是"拉模式"读取固件数据:SDK 在需要下发固件块时按(offset, size)向业务方要数据,业务方可以从文件、网络或内存中按需提供,避免一次性加载整个固件到内存;onRCSPInit驱动回连唤醒:升级过程中设备重启后重新连接,一旦 RCSP 在新连接上初始化成功,就遍历_ReconnectMap通知所有等待中的Reconnect实例"设备已上线",回连流程随即完成。
RCSP 实例管理与设备信息(OTAWrapper)
OTAWrapper 内部按 deviceId 维护五个 Map,形成"一设备一协议栈"的模型:
export class OTAWrapper implements OTAWrapperOption, IOTAWrapper {
private _RcspOTAManagerMap = new Map<string, JL_OTA.RcspOTAManager>()
private _RcspOTAUpgradeCallbackMap = new Map<string, OTAUpgradeCallback>()
private _RcspImplMap = new Map<string, JL_RCSP.RcspImpl>()
private _ReconnectMap = new Map<string, Reconnect>()
private _AuthMap = new Map<string, JLAuth.Auth>()
private _RcspCallbackManager = new JL_RCSP.RcspCallbackManager()
private _OTAWrapperOption: OTAWrapperOption;
}
Source: OtaWrapper.ets
升级前,业务层通常会先调用两个前置检查:
isRCSPInit(device):确认该设备是否已完成 RCSP 初始化(内部检查RcspImpl.getDeviceInfo(device)是否存在);isNeedMandatoryUpgrade(device):读取设备的目标信息,判断mandatoryUpgradeFlag是否为CmdGetTargetInfo.FLAG_MANDATORY_UPGRADE,决定是普通升级还是强制升级(强制升级不允许用户取消)。
/**是否需要强制升级**/
isNeedMandatoryUpgrade(device: BluetoothDevice): Promise<boolean> {
return new Promise<boolean>((resolve, reject) => {
const rcspImpl = this._getRCSPImpl(device.deviceId)
if (rcspImpl) {
const deviceInfo = rcspImpl.getDeviceInfo(device)
if (deviceInfo) {
resolve(deviceInfo.mandatoryUpgradeFlag == JL_RCSP.CmdGetTargetInfo.FLAG_MANDATORY_UPGRADE)
} else {
reject(new JL_RCSP.RCSPError(JL_RCSP.RCSPErrorCode.ERR_OTHER, "No DeviceInfo."))
}
} else {
reject(new JL_RCSP.RCSPError(JL_RCSP.RCSPErrorCode.ERR_OTHER, "No has RCSPImpl."))
}
})
}
Source: OtaWrapper.ets
OTAWrapper.startOTA() 内部对 RcspImpl 做三重防御检查后才真正创建升级管理器:rcspImpl 不存在、usingDevice 为空、设备信息未初始化,任一不满足都会直接返回并打印错误日志(如 'rcspImpl 没有初始化成功'),这是为了防止开发者在未建立 RCSP 会话时就误触发升级。
public startOTA(device: BluetoothDevice, otaConfig: JL_OTA.OtaConfig, otaUpgradeCallback: OTAUpgradeCallback) {
const rcspImpl = this._getRCSPImpl(device.deviceId)
if (rcspImpl == undefined) {
Log.e(TAG, 'rcspImpl undefined')
return
}
const usingDevice = rcspImpl.getUsingDevice()
if (usingDevice == null) {
return
}
if (rcspImpl.getDeviceInfo(usingDevice) == undefined) {
Log.e(TAG, 'rcspImpl 没有初始化成功')
return
}
const OTAManager = new JL_OTA.RcspOTAManager(rcspImpl)
this._RcspOTAManagerMap.set(device.deviceId, OTAManager)
this._RcspOTAUpgradeCallbackMap.set(device.deviceId, otaUpgradeCallback)
// ...构造 OnUpgradeCallback 并启动升级
}
Source: OtaWrapper.ets
升级启动后,SDK 内部会依次执行 RCSP 指令序列:读取目标信息(CmdGetTargetInfo)→ 认证 → 下发升级包(分块 onReadData 拉取数据)→ 校验 → 断开连接 → 设备重启 → 回连 → 上报完成。onNeedReconnect 回调正是"断开后需要重连"的触发点。
核心流程(Core Flow)
下图以序列图展示一次完整的 RCSP OTA 升级,从业务发起、RCSP 初始化、固件下发、重启回连到最终完成:
sequenceDiagram
participant App as 业务页面
participant Mgr as BluetoothOTAManager
participant Wrp as OTAWrapper
participant RCSP as JL_RCSP.RcspImpl
participant OTA as JL_OTA.RcspOTAManager
participant BT as BleImpl/SppImpl
participant Dev as 杰理设备
App->>Mgr: startOTA(deviceId, callback)
Mgr->>Wrp: startOTA(OTADevice, OtaConfig, callback)
Wrp->>Wrp: 校验 rcspImpl / usingDevice / DeviceInfo
Wrp->>OTA: new RcspOTAManager(rcspImpl)
OTA->>RCSP: 获取设备信息 / 认证 (JLAuth)
RCSP->>BT: sendData(RCSP_UUID_WRITE, 指令)
BT->>Dev: BLE/SPP 写特征值
Dev-->>BT: NOTIFY 响应
BT-->>Mgr: onBLECharacteristicInfo (RCSP_UUID_NOTIFY)
Mgr-->>Wrp: onReceiveData
Wrp-->>OTA: RCSP 指令应答
OTA-->>App: onStartOTA
loop 固件分块下发
OTA->>App: onReadData(offset, size)
App-->>OTA: Uint8Array 固件块
OTA->>BT: 下发固件数据
BT-->>App: onProgress(type, progress)
end
OTA-->>App: onExecuteDisconnectDevice
OTA-->>App: onNeedReconnect(ReconnectInfo)
App->>BT: 断开旧连接
Dev-->>BT: 设备重启
BT-->>Mgr: 扫描到新广播 (D60541544F4C4A 厂商包)
Mgr->>Wrp: onConnectStateSuccess / 连接
Wrp->>RCSP: 新连接上重建 RcspImpl
OTA->>RCSP: updateRcspImpl(新实例)
RCSP-->>App: onRCSPInit(deviceId, true)
OTA-->>App: onStopOTA / onCancelOTA / onError 结束
流程关键点解读:
- 前置检查:
OTAWrapper.startOTA必须确认 RCSP 实例与设备信息就绪,否则直接拒绝启动——这避免在设备尚未完成 RCSP 握手时下发升级指令导致协议错乱; - 认证前置:
isUseAuth返回true,升级前 SDK 通过JLAuth完成设备认证,防止未授权设备被刷写; - 固件拉取模式:升级数据不整包入参,而是 SDK 按
(offset, size)回调onReadData按需取块,业务方可从文件流/网络流读取,显著降低内存占用; - 断线重连是升级成败关键:设备在烧写后必然重启,原 BLE 连接失效,SDK 通过
onNeedReconnect触发回连,回连成功后必须调用OTAManager.updateRcspImpl(rcspImpl)把新连接上的协议实例同步给升级状态机,否则后续指令仍走旧连接。
蓝牙数据回调的分发路径
接收方向的数据流遵循"物理通道 → 协议适配 → OTA 层"的单向管道:
- BLE 路径:
onBLECharacteristicInfo收到特征值变化后,只认RCSP_UUID_NOTIFY特征值的数据,其余特征值一律忽略,然后调用otaWrapper?.onReceiveData(...); - SPP 路径:
onSppDataReadInfo只认RCSP_SOCKET_UUID的数据并同样转发给onReceiveData。
private onBLECharacteristicInfo(bleCharacteristicInfo: BLECharacteristicInfo) {
// 蓝牙回调-数据回调
const deviceId = bleCharacteristicInfo.device.deviceId
const data = bleCharacteristicInfo.characteristicChangeReq?.characteristicValue
const characteristicUuid = bleCharacteristicInfo.characteristicChangeReq?.characteristicUuid
if (data && characteristicUuid === RCSP_UUID_NOTIFY) {
this.otaWrapper?.onReceiveData(new OTADevice(deviceId), new Uint8Array(data))
}
}
private onSppDataReadInfo(sppDataReadInfo: SppDataReadInfo) {
// 蓝牙回调-数据回调
const deviceId = sppDataReadInfo.device.deviceId
const data = sppDataReadInfo.data
const characteristicUuid = sppDataReadInfo.uuid
if (data && characteristicUuid === RCSP_SOCKET_UUID) {
this.otaWrapper?.onReceiveData(new OTADevice(deviceId), new Uint8Array(data))
}
}
Source: BluetoothOTAManager.ets
连接状态变化同样被翻译为 OTA 层语义:CONNECT_STATE_SUCCESS → onConnectStateSuccess,失败/断开分别映射到 onConnectStateFailed / onConnectStateDisconnect,并同步通知 _ReconnectMap 中的回连实例。
回连机制详解
回连是 RCSP OTA 升级中最复杂的环节,源码中实现了两套回连策略,由 isInnerReconnect 开关选择:
策略一:SDK 内部回连(默认)
OTAWrapper 内 _OTAWrapperOption.isInnerReconnect() 返回 true 时,onNeedReconnect 内部直接构造 ReconnectOp(扫描回调、设备识别回调、连接回调)并启动 Reconnect。识别目标设备的关键在于解析广播包中的厂商标识与 MAC:
- 新回连方式(
isSupportNewReconnectADV为 true):设备升级后以特殊广播包出现,广播数据中包含D60541544F4C4A标志("JLOTA" 的 ASCII 十六进制),从该标志后 8 字节提取反转后的 MAC 与旧 MAC 比对; - 同时做两级模糊匹配(广播包包含旧 MAC 或其反转、扫描到的 deviceId 前缀与旧 MAC 前 10 位一致)用于日志优化。
const oldDeviceMac = reConnectMsg.deviceBleMac?.toUpperCase().replace(/:/g, "");
const oldDeviceMacReverse = oldDeviceMac?.split('')?.reverse()?.join(''); //mac反转
const oldDeviceMacPrefix = oldDeviceMac?.substring(0, 10);
// ...
if (reConnectMsg.isSupportNewReconnectADV) { //新回连方式
const advertisStr = (JL_OTA.toHexString(scanDevice.advertiseData) as string).toUpperCase();
const index = advertisStr.indexOf("D60541544F4C4A");
if (index != -1 && scanDevice.advertiseData) {
const unit8Array = new Uint8Array(scanDevice.advertiseData);
const macArray = unit8Array.slice((index / 2) + 8, (index / 2) + 14).reverse();
result = oldDeviceMac == JL_OTA.toHexString(macArray).toUpperCase();
}
}
Source: OtaWrapper.ets
回连成功后,ReconnectCallback.onReconnectSuccess 会把新 deviceId 通过 reconnectCallback.onResult(deviceId) 交回 SDK,OTAWrapper 随即执行 OTAManager.updateRcspImpl(rcspImpl) 让升级状态机切换协议通道,然后删除 _ReconnectMap 中对应的回连实例。
策略二:自定义回连
当 isInnerReconnect() 返回 false 时,BluetoothOTAManager.startOTA 包装的回调走自定义分支:它把 ReconnectInfo 转交给业务方回调 otaCallback.onNeedReconnect(reConnectMsg, reconnectCallback),业务方拿到 reconnectCallback 后可自行实现扫描/识别/连接(示例代码完整实现了同样的 MAC 匹配逻辑,并管理一个 _ReconnectMap)。无论哪种策略,最终都必须:
- 连接成功 →
reconnectCallback.onResult(新 deviceId); - 超时/失败 →
reconnectCallback.onError(JL_OTA.OtaError.ERR_OTA_RECONNECT_DEVICE_TIMEOUT, ...)。
onRCSPInit 回调是回连完成的关键信号:设备在新连接上完成 RCSP 初始化后,_ReconnectMap 中所有 Reconnect 实例会收到 onDeviceConnected,从而结束等待。
回连超时与终止
- 超时控制:
reconnect.startReconnect(JL_OTA.OTAImpl.RECONNECT_DEVICE_TIMEOUT)启动带超时的回连; - 终止时机:
onStopOTA、onCancelOTA、onError三个终态回调中都会调用reconnect?.stopReconnect()清理回连实例,防止升级结束后扫描/连接动作泄漏; - 失败恢复:
onDeviceConnectFailed与onDeviceConnectDisconnected都会重新触发sanDevice()扫描,即"连接失败就重新扫",直到超时。
配置选项
RCSP OTA 升级的可配置项集中在 OTAWrapperOption(协议适配契约)与 JL_OTA.OtaConfig(升级行为配置)两处。
OTAWrapperOption
| 选项 | 类型 | 默认值(示例工程) | 说明 |
|---|---|---|---|
isUseAuth | () => boolean | true | 升级前是否执行 JLAuth 认证 |
isInnerReconnect | () => boolean | true | true 用 SDK 内部回连,false 用业务自定义回连 |
sanDevice | () => void | 扫 BLE 10s;EDR 通信时补扫 EDR | 回连/升级时的设备扫描策略 |
connectDevice | (device) => void | 按 BLE 连接 | 建立物理连接 |
disconnectDevice | (device) => void | 按已连接列表判断 BLE/SPP | 断开物理连接 |
sendData | (device, data: Uint8Array) => void | BLE 写 RCSP_UUID_WRITE / SPP 写 RCSP_SOCKET_UUID | RCSP 指令字节的物理下发 |
全部实现证据见 BluetoothOTAManager.ets
OtaConfig 与常量
| 配置/常量 | 类型 | 值 | 说明 |
|---|---|---|---|
otaConfig.isSupportNewRebootWay | boolean | true | 启用"新回连方式"(依赖 D60541544F4C4A 广播包携带 BLE 地址) |
SUPPORT_RESET_FLAG | boolean | true | 是否支持重置认证标志命令,由 OtaWrapper.ets 导出 |
JL_OTA.OTAImpl.RECONNECT_DEVICE_TIMEOUT | number | SDK 内置 | 回连超时时长,传入 Reconnect.startReconnect() |
RCSP_UUID_SERVICE / RCSP_UUID_WRITE / RCSP_UUID_NOTIFY | string | 见 BleConnectSettingConfigure | BLE 侧 RCSP 服务的三个 UUID |
RCSP_SOCKET_UUID | string | 见 SppConnectSettingConfigure | SPP 侧 RCSP socket UUID |
API 参考
BluetoothOTAManager
public init(): void
初始化蓝牙事件监听与 OTAWrapper。必须在 startOTA 之前调用。
public startOTA(deviceId: string, otaCallback: OTAUpgradeCallback): void
启动 RCSP OTA 升级。
- 参数:
deviceId目标设备地址;otaCallback升级回调集合(含onStartOTA、onExecuteDisconnectDevice、onNeedReconnect、onProgress、onStopOTA、onCancelOTA、onError、onReadData、onRCSPInit)。 - 前置条件:设备已完成蓝牙连接且 RCSP 已初始化(
OTAWrapper.startOTA内部会校验并静默返回)。
public getOTAWrapper(): IOTAWrapper | undefined
获取内部 OTAWrapper 实例,可用于调用 isRCSPInit / isNeedMandatoryUpgrade 等协议层能力。
OTAWrapper
isRCSPInit(device: BluetoothDevice): Promise<boolean>
判断设备是否完成 RCSP 初始化。返回 true 表示 RcspImpl.getDeviceInfo(device) 已就绪。
isNeedMandatoryUpgrade(device: BluetoothDevice): Promise<boolean>
判断设备是否处于强制升级状态(mandatoryUpgradeFlag == FLAG_MANDATORY_UPGRADE)。
- Rejects:
JL_RCSP.RCSPError(ERR_OTHER, "No DeviceInfo.")或"No has RCSPImpl."。
startOTA(device: BluetoothDevice, otaConfig: JL_OTA.OtaConfig, otaUpgradeCallback: OTAUpgradeCallback): void
创建 JL_OTA.RcspOTAManager 并驱动升级;内部会把 onNeedReconnect 的回连成功结果通过 updateRcspImpl 同步给升级状态机。
Reconnect(自定义回连工具类)
startReconnect(timeout: number): void // 启动带超时的回连
stopReconnect(): void // 终止回连
onDeviceConnected(device: BluetoothDevice): void // RCSP 初始化成功后唤醒
onDiscoveryDevices(devices: Array<OTADevice>): void // 注入扫描结果
onScanStop(): void // 扫描结束信号
失败模式、边界情况与并发
失败模式与错误处理
| 失败场景 | 处理方式 | 源码证据 |
|---|---|---|
rcspImpl 不存在 | 打印 'rcspImpl undefined' 并直接返回,不启动升级 | OtaWrapper.ets |
usingDevice 为空 | 直接返回 | 同上 #L82-L86 |
| 设备信息未初始化 | 打印 'rcspImpl 没有初始化成功' 并返回,防止误触发升级 | 同上 #L88-L91 |
| 回连超时 | onReconnectFailed 回调 reconnectCallback.onError(ERR_OTA_RECONNECT_DEVICE_TIMEOUT),并删除 Map 中的回连实例 | BluetoothOTAManager.ets |
| 回连连接失败/断开 | 重新调用 sanDevice() 继续扫描 | 同上 #L211-L216 |
| 升级终态(完成/取消/出错) | 一律 stopReconnect(),避免回连任务残留 | 同上 #L226-L239 |
| 设备未返回 BLE 地址 | 注释明确"RCSP协议未拿到设备的BLE地址",新回连匹配失败,退回模糊匹配 | 同上 #L176-L178 |
边界情况
- 新回连广播解析:
D60541544F4C4A标志的定位与 MAC 提取依赖广播包格式(标志后第 8~14 字节反转 MAC),若设备固件不支持新广播,则isSupportNewReconnectADV为false,退化为"旧 deviceId 相同即命中"; - MAC 大小写:比较前统一
toUpperCase()并去除冒号,广播包内 MAC 为反转序,需要reverse()后再比较; - BLE/SPP 双栈:
sendData/disconnectDevice通过查询bleImpl.getConnectedDevice()判断设备归属,若同一 deviceId 同时出现在两条链路会出现误判——示例工程中回连固定为 BLE 以规避此问题; - 扫描超时:
sanDevice固定 10 秒,onScanState在SCAN_STATE_FINISH时通知onSanDeviceStop与Reconnect.onScanStop,避免回连逻辑在扫描结束后挂起。
并发与一致性
- 多设备隔离:
OTAWrapper所有实例均按deviceId存于 Map,多个设备可并行升级互不干扰; - 回连状态同步:
onRCSPInit会遍历_ReconnectMap唤醒所有等待实例——这是有意为之的"广播"设计,但在多设备同时升级时,一个设备的 RCSP 初始化可能唤醒另一设备的回连,依赖Reconnect.onDeviceConnected(deviceId)内部再次比对设备地址来收敛; - 回调线程模型:蓝牙事件(
onBLECharacteristicInfo等)与 SDK 协议回调在同一 HarmonyOS 事件循环中串行分发,示例未引入额外锁,业务回调中不应执行耗时操作以免阻塞数据接收。
性能与运维注意事项
- 内存优化:固件采用
onReadData(offset, size)拉取模式,业务方务必按块读取(如文件流 seek),避免整包载入内存导致 OOM;大固件建议配合分片大小与进度上报做 UI 节流; - 日志降噪:源码中回连匹配日志做了两级模糊匹配过滤(仅打印"前缀相似"或"包含旧 MAC"的设备),生产环境建议保持
Log.i级别,避免扫描期间海量广播日志拖垮性能; - 升级不可中断:
onExecuteDisconnectDevice之后设备进入烧写窗口,此时应避免用户切换页面销毁BluetoothOTAManager(单例生命周期应长于升级会话); - 扫描窗口:回连扫描 10 秒窗口内不应并发启动业务侧其他扫描,
SCAN_STATE_FINISH是回连扫描结束的唯一可靠信号。
扩展点
- 自定义回连:将
isInnerReconnect改为返回false,在otaCallback.onNeedReconnect中实现自己的扫描/识别/连接逻辑,复用Reconnect/ReconnectOp/ReconnectCallback工具类(示例工程已给出完整参考实现); - 自定义传输:替换
OTAWrapperOption的sendData/connectDevice/disconnectDevice/sanDevice,即可把 RCSP 协议栈跑在任意通道上(如 TCP、经典蓝牙)而不改动协议层; - 升级策略扩展:升级前通过
isRCSPInit与isNeedMandatoryUpgrade组合业务规则(如强制升级时隐藏取消按钮、弱网时提示用户保持蓝牙连接); - 多固件支持:
OtaConfig与onReadData的组合可支持双分区/多固件包升级,只需按offset/size语义在业务侧切换数据源。
Related Links
- BluetoothOTAManager.ets — 本流程的核心适配层源码
- OtaWrapper.ets — 协议封装层与 RCSP 实例管理
- lib_rcsp/Index.ets — SDK 库统一导出入口
- BleConnectSettingConfigure.ets — RCSP BLE UUID 定义
- SppConnectSettingConfigure.ets — RCSP SPP socket UUID 定义
- 相关主题页:蓝牙连接管理(扫描/连接/断开)、JL Auth 认证流程、OTA 升级文件处理