杰理 SDK 文档中心
首页
首页
  • 项目概述

    • 项目简介与核心能力
    • 运行环境与SDK版本
  • 快速开始

    • 工程导入与依赖配置
    • 权限配置与示例运行
  • 平台架构

    • SDK分层架构与RCSP协议
    • 蓝牙连接库
    • 健康SDK核心库 JL_Watch
    • 健康服务器与云端服务
  • 健康与运动数据

    • 健康数据同步
    • 运动数据同步
    • 本地数据持久化
  • 设备管理功能

    • 表盘管理
    • 闹钟与健康提醒
    • 消息与联系人同步
    • 天气同步
    • 设备查找
    • 支付宝集成
  • 传输与媒体处理

    • 文件传输与文件管理
    • 音乐传输与播放控制
    • 图像转换库
    • 音频编解码与解密
  • OTA 升级

    • 固件空中升级流程
    • 4G模块与差分升级
  • AI 能力

    • AI表盘与云服务
    • AI语音助手
  • 示例应用

    • HealthAide 健康助手应用
    • WatchTestTool 测试工具
  • 开发者指南

    • 自定义命令扩展
    • 调试技巧与问题排查
    • 版本历史与兼容性

固件空中升级流程

本文档介绍 JL Health 手表应用(Android-JL_Health)中固件空中升级(OTA)的完整实现:从固件文件准备、蓝牙连接与重连、OTAManager 升级驱动,到网络模块(4G)OTA 的扩展流程,以及贯穿全程的状态机与回调机制。

Purpose and Scope

本页覆盖"固件空中升级"这一能力的端到端实现,包括:

  • 升级前置条件与固件文件约定(.ufw / .zip 包)
  • OTAManager 对蓝牙 OTA SDK(jl_bt_ota)的封装与配置
  • 升级状态机 OtaState 与回调链 IUpgradeCallback
  • 升级过程中的蓝牙回连/地址映射(BLE/EDR)机制
  • 网络模块(4G)OTA 的查询、选包与升级流程

不在此页范围内的相邻主题:

  • 蓝牙连接与设备管理的底层封装(BluetoothHelper)属于蓝牙连接能力,请参见相关蓝牙页面
  • 升级包的云端下发/HTTP 下载机制(OtaFileMsg、jl_health_http)属于服务器交互能力,本页仅引用其结果
  • 升级 UI 页面(进度条、动画等)属于 UI 层,不在本页深入展开

Overview

固件空中升级(Firmware Over-The-Air, OTA)是智能手表 App 的核心能力之一。本仓库(Jieli-Tech/Android-JL_Health)基于杰理科技的 jl_bt_ota 外接库(libs/JL/jl_bt_ota_V1.11.0_11015-release.aar)实现:

  • 普通固件升级:通过 BluetoothOTAManager 的子类 OTAManager 驱动,将 .ufw 固件文件经蓝牙(BLE/SPP)发送到手表端完成烧录。
  • 资源/双备份判断:isSingleOTA() 依据设备信息 TargetInfoResponse.isSupportDoubleBackup() 判断设备是否支持双备份,从而决定升级策略。
  • 网络模块(4G)升级:通过 NetworkOpImpl 执行,支持从 zip 包中按 sdkMap.json 匹配与设备版本一致的固件文件后再升级。

升级流程整体分为四个阶段:准备(PREPARE)→ 下载(DOWNLOAD)→ 升级(UPGRADE)→ 工作(WORKING)/停止(STOP),中间可能穿插 重连(RECONNECT) 步骤。OtaState 用一组整数常量完整刻画了这些阶段。

Architecture

flowchart TD
    subgraph sg_App["应用层 (HealthAide)"]
        UI["升级 UI / ViewModel"] --> OtaState["OtaState 状态模型"]
        OtaState --> OTAManager["OTAManager<br/>(extends BluetoothOTAManager)"]
        OTAManager --> CB["CustomUpgradeCallback<br/>(IUpgradeCallback 包装)"]
    end

    subgraph sg_SDK["杰理 OTA SDK (jl_bt_ota aar)"]
        BOM["BluetoothOTAManager"] --> Cfg["BluetoothOTAConfigure"]
        BOM --> RCSP["RCSP 指令协议"]
    end

    subgraph sg_BT["蓝牙层 (BluetoothHelper)"]
        BE["BluetoothEventListener"] -->|"onConnection/onReceiveData/onBleMtuChange"| OTAManager
        OTAManager -->|"getConnectedDevice/getConnectedBluetoothGatt"| BH["BluetoothHelper"]
    end

    subgraph sg_Net["网络模块升级 (4G)"]
        NVM["NetworkOtaViewModel"] --> NOP["NetworkOpImpl"]
        NOP --> NOL["OnNetworkListener / OnNetworkOTACallback"]
    end

    OTAManager --> BOM
    BOM --> BE
    OTAManager -->|"watchManager.getCmdSnGenerator()"| SN["SN 生成器(共享)"]
    NVM --> WatchMgr["WatchManager"]
    SN --> WatchMgr

架构要点说明:

  • OTAManager 继承 BluetoothOTAManager,把 SDK 需要的设备连接、数据收发、MTU 变化等能力桥接到应用自有的 BluetoothHelper 单例上,这样 SDK 无需感知应用层的连接管理方式(源码见 OTAManager.java)。
  • BluetoothEventListener 把蓝牙层的三类事件(连接状态、数据接收、MTU 变化)转发给 OTA SDK,onConnection 中插入 300ms 延时,等待设备其他模块初始化完成后再上报连接状态。
  • SN(命令序号)生成器从 WatchManager 取出并注入 OTA 配置,保证两个库(RCSP 与 OTA)使用同一个命令序号生成器,避免协议层序号冲突(源码见 OTAManager.java)。
  • 网络模块 OTA 走独立通道 NetworkOpImpl,与普通固件升级解耦,但同样依赖 WatchManager 提供的设备连接与配置信息。

升级状态机与核心流程

状态定义

OtaState 用连续的整数常量定义升级的完整生命周期(源码见 OtaState.java):

常量值含义
OTA_STATE_IDLE0空闲,无升级任务
OTA_STATE_PREPARE1升级准备(检查固件、配置等)
OTA_STATE_DOWNLOAD2下载固件包(网络获取)
OTA_STATE_UPGRADE3进入升级流程
OTA_STATE_START4OTA 已启动(SDK 回调 onStartOTA)
OTA_STATE_WORKING5升级进行中(进度回调)
OTA_STATE_STOP6升级结束(成功/失败/取消)
OTA_STATE_RECONNECT7等待/执行设备重连(双模切换)

状态转换图:

stateDiagram-v2
    [*] --> IDLE: 初始化
    IDLE --> PREPARE: 开始升级任务
    PREPARE --> DOWNLOAD: 需要下载固件
    PREPARE --> UPGRADE: 固件已就绪
    DOWNLOAD --> UPGRADE: 下载完成
    UPGRADE --> START: SDK onStartOTA
    START --> WORKING: onProgress
    WORKING --> RECONNECT: 需切换连接方式
    RECONNECT --> WORKING: 重连成功继续
    WORKING --> STOP: onStopOTA / onCancelOTA / onError
    STOP --> IDLE: 清理资源
    RECONNECT --> STOP: 重连失败

端到端时序

sequenceDiagram
    participant UI as 升级UI/ViewModel
    participant OM as OTAManager
    participant SDK as BluetoothOTAManager (SDK)
    participant BH as BluetoothHelper
    participant DEV as 手表设备

    UI->>OM: startOTA(callback)
    OM->>OM: checkDevice(device) 同步MTU
    OM->>SDK: super.startOTA(CustomUpgradeCallback)
    SDK-->>UI: onStartOTA()
    SDK->>DEV: 发送升级指令/固件数据
    SDK-->>UI: onProgress(index, progress)
    SDK->>OM: onNeedReconnect(addr, isNewADV)
    OM->>BH: 更新HistoryRecord(BLE映射地址)
    OM->>BH: connectDeviceWithoutRecord(BLE)
    BH-->>OM: onConnection(CONNECTED)
    OM->>SDK: onBtDeviceConnection(OK)
    SDK->>DEV: 继续发送固件数据
    SDK-->>UI: onProgress(...) 直到100%
    SDK-->>UI: onStopOTA()
    OM->>OM: 删除本地固件文件(.ufw)
    UI->>OM: release() 移除监听器

关键流程说明:

  1. 启动前同步 MTU:checkDevice() 在升级前读取当前 GATT 连接的 MTU,并以 mtu + 3 调用 onMtuChanged 通知 SDK,让 SDK 按实际 MTU 分包(源码见 OTAManager.java)。
  2. 回连地址映射:onNeedReconnect 回调中,若设备历史记录不是 BLE 连接,则把 BLE 地址写入历史记录(setConnectType(BLE)、setAddress(bleAddress)、setMappedAddress(edrAddress)),再交由上层回调触发重连。这是双模(BLE/EDR)手表升级前切 BLE 通道的关键一步(源码见 OTAManager.java)。
  3. 升级结束清理:onStopOTA 中删除本地固件文件,避免残留占用存储(源码见 OTAManager.java)。

核心实现详解

OTAManager 的构造与配置

OTAManager 构造时注册蓝牙事件监听并调用 configureOTA() 完成 SDK 配置(源码见 OTAManager.java):

private void configureOTA() {
    BluetoothOTAConfigure configure = BluetoothOTAConfigure.createDefault();
    int connectWay = mBluetoothHelper.getBluetoothOp().getBluetoothOption().getPriority();
    if (mBluetoothHelper.isConnectedDevice()) {
        BluetoothDevice device = mBluetoothHelper.getConnectedBtDevice();
        connectWay = mBluetoothHelper.getBluetoothOp().isConnectedSppDevice(device) ? BluetoothConstant.PROTOCOL_TYPE_SPP : BluetoothConstant.PROTOCOL_TYPE_BLE;
    }
    configure.setPriority(connectWay)
            .setNeedChangeMtu(false)
            .setMtu(BluetoothConstant.BLE_MTU_MIN)
            .setUseAuthDevice(false)
            .setUseReconnect(false)
            .setBleScanMode(2);
    String otaDir = HealthUtil.createFilePath(HealthApplication.getAppViewModel().getApplication(), HealthConstant.DIR_UPDATE);
    File dir = new File(otaDir);
    boolean isExistDir = dir.exists();
    if (!isExistDir) {
        isExistDir = dir.mkdir();
    }
    if (isExistDir) {
        String otaFilePath = HealthUtil.obtainUpdateFilePath(otaDir, OTA_FILE_SUFFIX);
        if (null == otaFilePath) {
            otaFilePath = otaDir + File.separator + OTA_FILE_NAME;
        }
        configure.setFirmwareFilePath(otaFilePath);
    }
    //保证两个库的SN生成器是同一个
    final CmdSnGenerator snGenerator = WatchManager.getInstance().getCmdSnGenerator();
    configure.setSnGenerator(snGenerator::getRcspCmdSeq);
    configure(configure);

    if (mBluetoothHelper.isConnectedDevice()) {
        final BluetoothDevice connectedDev = mBluetoothHelper.getConnectedBtDevice();
        onBtDeviceConnection(connectedDev, StateCode.CONNECTION_OK);
        checkDevice(connectedDev);
    }
}

Source: OTAManager.java

设计意图解读:

  • 连接方式自适应:优先使用蓝牙配置中的默认优先级;若已连接设备是 SPP 设备则使用 SPP 协议,否则使用 BLE。这保证升级时选择与当前连接一致的物理通道。
  • 固定 MTU 与关闭重连:setNeedChangeMtu(false) + setMtu(BLE_MTU_MIN) 避免升级过程中再次协商 MTU 引入不确定性;setUseReconnect(false) 表示 SDK 不自行重连,重连完全交由应用层 BluetoothHelper 控制——这是为了让应用层统一管理连接状态与 UI 反馈。
  • 固件路径约定:固件存放在应用私有目录 DIR_UPDATE 下,优先查找已有 .ufw 文件,找不到则使用默认名 update.ufw。
  • SN 生成器共享:RCSP 指令与 OTA 指令共用 WatchManager 的 CmdSnGenerator,避免两个库各自计数导致指令序号冲突。

回调包装 CustomUpgradeCallback

startOTA() 把外部回调包装为 CustomUpgradeCallback,在转发给上层的同时附加资源清理与地址映射逻辑:

  • onStopOTA():清除 OTA 映射地址、删除固件文件后转发;
  • onCancelOTA():清除 otaAddress 后转发;
  • onError(BaseError):若错误码为 SUB_ERR_NEED_UPDATE_RESOURCE(需要更新资源),清除映射地址;随后清除 otaAddress 并转发错误(源码见 OTAManager.java)。

蓝牙事件桥接

mBluetoothEventListener 是连接 BluetoothHelper 与 OTA SDK 的桥梁:

  • onBleMtuChange → onMtuChanged(gatt, mtu, status),保持 SDK 对 MTU 的感知;
  • onConnection:设备已连接时先 SystemClock.sleep(300) 等待其他模块初始化,再用 HealthUtil.convertOtaConnectStatus(status) 把应用层连接状态码转换为 OTA SDK 的状态码后上报;
  • onReceiveData → onReceiveDeviceData(device, data),把蓝牙数据透传给 SDK 解析。

配置选项

OTA 相关的可配置项集中在 BluetoothOTAConfigure 与文件路径约定中:

配置项类型默认值说明
priorityint蓝牙选项优先级连接协议优先级(SPP/BLE),已连接设备时按实际类型取
needChangeMtubooleanfalse升级前是否需要 SDK 主动改 MTU
mtuintBLE_MTU_MIN使用的 MTU 下限值
useAuthDevicebooleanfalse是否使用认证设备流程
useReconnectbooleanfalseSDK 是否自行重连(false 表示由应用层接管重连)
bleScanModeint2BLE 扫描模式(2 为低功耗扫描)
firmwareFilePathStringDIR_UPDATE/update.ufw固件文件路径,优先复用已有 .ufw 文件
snGeneratorCmdSnGeneratorWatchManager 的生成器RCSP/OTA 共用的命令序号生成器
OTA_FILE_SUFFIXString.ufw固件文件后缀
OTA_ZIP_SUFFIXString.zip网络模块固件压缩包后缀

常量定义见 OTAManager.java,配置逻辑见 OTAManager.java。

网络模块(4G)OTA

除主控固件外,部分手表带有 4G 网络模块,其升级由 NetworkOpImpl 独立驱动。NetworkOTADemo 给出了标准调用序列(源码见 NetworkOTADemo.java):

@Test
public void startNetWorkOTA(String filePath) {
    //Step0. WatchManager是WatchOpImpl的子类,须在1.3配置好sdk
    WatchManager watchManager = WatchManager.getInstance();
    //Step1. 初始化网络模块升级操作对象
    NetworkOpImpl networkOp = NetworkOpImpl.instance(watchManager);
    if (networkOp.isNetworkOTA()) {
        //正在OTA
        return;
    }
    //执行开始网络OTA
    //filePath --- 4G模块升级文件路径
    networkOp.startNetworkOTA(watchManager.getConnectedDevice(), new OTAParam(filePath), new OnNetworkOTACallback() {
        @Override
        public void onStart() {
            //回调OTA开始
        }

        @Override
        public void onProgress(int progress) {
            //回调OTA进度
        }

        @Override
        public void onCancel() {
            //回调OTA被取消
        }

        @Override
        public void onStop() {
            //回调OTA成功
        }

        @Override
        public void onError(int code, String message) {
            //回调OTA异常
            //code --- 错误码
            //message --- 错误描述
        }
    });
}

Source: NetworkOTADemo.java

网络模块升级前置检查

升级前需要确认设备支持网络模块:读取缓存的手表配置 WatchConfigure.getFunctionOption().isSupportNetworkModule(),若配置未缓存可调用 requestDeviceConfigure() 主动请求(源码见 NetworkOTADemo.java)。

压缩包解压与版本匹配

NetworkOtaViewModel.startOTA() 处理 zip 包:解压到临时目录 tmp,读取包内 sdkMap.json,把 map 列表中的每个固件版本号与设备当前网络模块版本号(networkInfo.getVersion())比对,版本一致即为要升级的固件包(源码见 NetworkOtaViewModel.java):

final List<SdkMapInfo.MapDTO> mapList = sdkMapInfo.getMap();
if (null != mapList) {
    for (SdkMapInfo.MapDTO map : mapList) {
        long mapVersion = convertVersionCode(networkInfo.getVid(), map.getVersion());
        long firmwareVersion = convertVersionCode(networkInfo.getVid(), networkInfo.getVersion());
        if (mapVersion == firmwareVersion) {
            otaFilePath = AppUtil.obtainUpdateFilePath(otaDirPath, map.getPakage());
            break;
        }
    }
}

Source: NetworkOtaViewModel.java

convertVersionCode() 使用正则 \d+ 提取版本字符串中的全部数字拼接为 long,如 "1.0.3" → 103,用于版本比较。升级结束或出错时删除解压的临时目录,避免垃圾文件残留;onCancel 错误码为 WatchError.ERR_CANCEL_OP。

网络模块升级异常监听

设备在升级失败后会主动上报异常,App 通过 OnWatchCallback.onNetworkModuleException 获知并提示用户执行强制升级(源码见 NetworkOTADemo.java)。

API 参考

OTAManager(继承 BluetoothOTAManager)

方法返回说明
OTAManager(Context context)-构造,注册蓝牙监听并执行 configureOTA()
getConnectedDevice()BluetoothDevice返回当前连接的设备
getConnectedBluetoothGatt()BluetoothGatt返回当前连接设备的 GATT
connectBluetoothDevice(BluetoothDevice)void记录 OTA 映射地址后连接设备(不写入历史)
disconnectBluetoothDevice(BluetoothDevice)void断开指定设备
sendDataToDevice(BluetoothDevice, byte[])boolean向设备发送数据
startOTA(IUpgradeCallback)void包装回调并启动升级
release()void释放资源并移除蓝牙监听
isSingleOTA()boolean设备是否不支持双备份(单备份升级)

OtaState

方法返回说明
getState()/setState(int)int / OtaState升级阶段状态(见状态表)
getMessage()/setMessage(OtaFileMsg)OtaFileMsg / OtaState升级文件消息(云端下发信息)
getOtaFilePath()/setOtaFilePath(String)String / OtaState本地固件文件路径
getOtaType()/setOtaType(int)int / OtaState升级类型(固件/资源/网络模块)
getOtaProgress()/setOtaProgress(float)float / OtaState升级进度 0~1
getStopResult()/setStopResult(int)int / OtaState结束结果(成功/失败/取消)
getError()/setError(BaseError)BaseError / OtaState错误信息
getOtaTotal()/getOtaIndex()/getOtaFileInfo()见左文件总数、当前序号、文件信息

NetworkOpImpl(网络模块 OTA)

方法说明
instance(WatchManager)获取单例(绑定 WatchManager)
queryNetworkInfo(device, callback)查询网络模块基础信息(VID/版本/是否强制升级)
startNetworkOTA(device, OTAParam, OnNetworkOTACallback)启动网络模块升级
cancelNetworkOTA(device, callback)取消升级
isNetworkOTA()是否正在升级
addOnNetworkListener(OnNetworkListener)注册网络信息/升级状态监听
removeOnNetworkListener(OnNetworkListener)移除监听

失败模式、边界情况与并发

常见失败模式

场景处理方式源码依据
固件文件缺失配置回退到默认路径 update.ufw;网络模块 zip 找不到匹配固件时以 SUB_ERR_FILE_NOT_FOUND 结束OTAManager.java、NetworkOtaViewModel.java
需要更新资源onError 中 SUB_ERR_NEED_UPDATE_RESOURCE 触发清除 OTA 映射地址,交由上层进入资源更新流程(OTA_TYPE_OTA_UPDATE_RESOURCE)OTAManager.java
升级中断开连接通过 onNeedReconnect 更新历史记录地址并交由应用层重连;重连失败进入 STOPOTAManager.java
4G 模块升级异常设备上报 onNetworkModuleException,App 提示强制升级(NetworkInfo.isMandatoryOTA())NetworkOTADemo.java

边界情况与并发注意

  • 重复启动防护:NetworkOpImpl.isNetworkOTA() 在启动前检查,防止重复触发升级;startOTA() 还校验当前状态非 IDLE/STOP 才允许继续。
  • 连接状态转换:onConnection 中 300ms 延时是为了等待设备端其他模块(如外设信息上报)初始化完成,避免 OTA 指令与初始化流程竞争;状态码需经 convertOtaConnectStatus() 从应用层语义转换到 OTA SDK 语义。
  • 地址映射竞态:updateHistoryRecord 仅在历史记录存在、且映射地址非法或与目标地址不一致时才更新,避免反复写历史记录;onStopOTA/onError 会清除 otaAddress,保证下一次升级从干净状态开始。
  • SN 序号并发:RCSP 与 OTA 两个库共享同一个 CmdSnGenerator,指令序号全局递增,防止并发指令序号冲突导致设备端丢弃指令。
  • 文件清理:升级结束(成功/取消/失败)都会删除本地固件或临时解压目录;网络模块 zip 解压的 tmp 目录在回调结束时统一删除。

性能与运维注意事项

  • MTU 策略:升级使用 BLE_MTU_MIN 且禁止 SDK 动态改 MTU,分包大小固定可预测,降低大固件传输时的协商开销与失败概率;升级前由应用层上报实际 MTU(mtu + 3)。
  • 大文件传输:.ufw 固件文件存放在应用私有目录 DIR_UPDATE,升级完成后立即删除,避免占用存储;下载阶段(OTA_STATE_DOWNLOAD)依赖 jl_health_http 云服务,进度通过 OtaState 的 otaProgress 上报 UI。
  • 进度粒度:onProgress(int index, float progress) 同时携带文件序号与总体进度,支持多文件(固件+资源)升级场景下 UI 展示"第 x/y 个文件"(otaTotal/otaIndex)。
  • 重连开销:双模设备升级需从 EDR 切到 BLE,onNeedReconnect 中的地址映射与历史记录更新保证切换后能快速回连;300ms 延时是经验值,用于平衡"等待初始化"与"连接状态及时性"。

扩展点

  1. 升级类型扩展:OtaState.OTA_TYPE_* 已预留固件(1)、资源(2)、4G 模块(3)、网络模块(4)四类;新增升级目标时扩展该常量并在上层分发即可。
  2. 回调链定制:OTAManager.startOTA() 内部通过 CustomUpgradeCallback 包装外部回调,可在不侵入 SDK 的前提下插入资源清理、地址映射等横切逻辑——这是自定义行为的推荐位置。
  3. SDK 替换/升级:业务代码只依赖 jl_bt_ota 的公开接口(BluetoothOTAManager、BluetoothOTAConfigure、IUpgradeCallback),升级 SDK 版本时只需适配 configureOTA() 中的配置项。
  4. 网络模块升级策略:NetworkOtaViewModel 的 zip 解析 + sdkMap.json 版本匹配逻辑独立于 UI,可复用为后台静默升级服务。

Related Links

  • OTAManager.java(核心 OTA 管理器)
  • OtaState.java(升级状态模型)
  • NetworkOTADemo.java(网络模块 OTA 示例)
  • NetworkOtaViewModel.java(网络模块升级逻辑)
  • OTAManager.java(WatchTestTool 版本)
  • 杰理 OTA 外接库 (Android) 开发文档
  • jl_bt_ota SDK AAR
Next
4G模块与差分升级