简介

升级包安装组件运行在updater分区,其功能主要包括读取misc分区信息获取升级包状态,对升级包进行校验,确保升级包合法有效;然后从升级包中解析出升级的可执行程序,创建子进程并启动升级程序。具体升级的动作由升级脚本控制。本文将介绍如何针对OpenHarmony L2场景适配updater模式。

前置条件

  • 参考OpenHarmony官方指导,完成正常系统编译和内核启动,能进入正常模式,且运行正常。

  • 芯片需配置包含updater分区和misc分区的分区表。updater分区大小不小于32M。

     

1、OTA升级实现原理

1.1 OTA实现主要流程:

 

1.2 升级服务组件

升级服务组件是一个SA(System Ability), 由OHOS 的init 进程负责启动。

升级服务器引擎主要功能包括:

1、查找可用的升级包

2、下载升级包

3、设置/获取升级策略

4、触发升级

代码目录

base/update/updateservice  # 升级服务代码仓目录
├── interfaces             # 升级客户端接口目录
│   ├── kits               # 对外接口封装目录
│   │   └── js             # 提供给升级客户端应用的JS 接口目录
│   └── inner_api          # SA 接口定义和封装目录
├── frameworks             # 部件无独立进程的实现
│   └── js                 # JS API的实现
│       └── napi           # napi代码实现
│           └── client     # 升级客户端napi 接口目录
├── services               # 独立进程的实现
│   ├── callback           # 提供给升级客户端应用的callback接口目录
│   └── engine             # 升级客户端引擎服务目录
│       ├── etc            # 升级客户端引擎rc配置文件目录
│       ├── include        # 升级客户端引擎头文件目录
│       ├── sa_profile     # SA 配置文件目录
│       └── src            # 升级客户端引擎源码目录
├── test                   # 测试代码目录
│   ├── unittest           # 升级客户端UT代码目录
│   └── fuzztest           # 升级客户端FT代码目录
├── BUILD.gn               # 编译入口
└── bundle.json            # 部件描述文件

JS接口说明

 

接口 说明
checkNewVersion 检查是否有可用的升级包版本
download() 下载升级包
upgrade() 将升级命令写入到misc分区,最终调用reboot命令,进入到updater 子系统中。
getNewVersionInfo() 升级完成后,获取升级后的版本信息
setUpgradePolicy 设置升级策略
getUpgradePolicy 获取升级策略

使用说明

1,导入updateclient lib

import client from 'libupdateclient.z.so'

2,获取update对象

let updater = client.getUpdater('OTA');

3,获取新版本信息

updater.getNewVersionInfo(info => {
    info "新版本信息"
});

4,检查新版本

updater.checkNewVersion(info => {
    info "新版本信息"
});

5,下载新版本,并监听下载进程

updater.download();
updater.on("downloadProgress", progress => {
    progress "下载进度信息"
});

6,启动升级

updater.upgrade();
updater.on("upgradeProgress", progress => {
    progress "升级进度信息"
});

7,设置升级策略

updater.setUpgradePolicy(result => {
    result "设置升级策略结果"
});

8,查看升级策略

updater.getUpgradePolicy(policy => {
    policy "升级策略"
});

###

1.3 升级包安装组件

升级包安装组件运行在updater分区,其功能主要包括读取misc分区信息获取升级包状态,对升级包进行校验,确保升级包合法有效;然后从升级包中解析出升级的可执行程序,创建子进程并启动升级程序。具体升级的动作由升级脚本控制。

图 1 升级子系统架构图

 

代码目录

base/update/updater/
├── resources           # 升级子系统用户界面图片资源目录
├── services            # 组件服务层代码目录
│   ├── applypatch      # 升级包数据更新代码目录
│   ├── diffpatch       # 差分还原代码目录
│   ├── etc             # 启动相关配置文件目录
│   ├── flashd          # flashd模式镜像写入和升级功代码目录
│   ├── fs_manager      # 文件系统和分区管理代码目录
│   ├── hdi             # 硬件相关接口定义
│   ├── include         # 升级子系统头文件目录
│   ├── log             # 升级子系统日志模块目录
│   ├── package         # 升级包管理模块目录
│   ├── ptable_parse    # 分区表解析代码目录
│   ├── script          # 升级脚本管理目录
│   ├── ui              # 升级ui界面代码目录
│   └── updater_binary  # 升级可执行程序目录
├── interfaces
│   └── kits            # 对外模块接口定义
└── utils               # 升级子系统通用代码目录
    └── include         # 升级子系统通用函数头文件目录

使用说明

升级包安装组件运行在updater分区里,需要如下的操作

1、创建updater分区

updater是一个独立的分区,分区大小建议不小于20MB。updater分区镜像是ext4 格式文件系统。确保系统内核ext4 文件系统的config 是打开状态。

2、创建misc分区

misc 分区中存储了升级子系统在升级过程中需要的元数据(metadata),如升级命令,掉电续传记录等。 misc 分区的大小约1MB,是一个裸分区,无需制作文件系统, 升级子系统直接访问。

3、分区配置表

升级包安装组件在运行过程中,需要通过分区配置表操作分区。默认的分区配置表文件名是fstab.updater,在编译的时候,打包到升级包安装组件中。

4、升级包安装组件启动

updater分区的init 进程有单独的配置文件 init.cfg,升级包安装进程启动配置在该文件中。

5、升级包安装组件编译

a、在build/subsystem_config.json文件添加配置。

如下:

"updater": {
"project": "hmf/updater",
"path": "base/update/updater",
"name": "updater",
"dir": "base/update"
},

b、 产品中添加需要编译的组件

以Hi3516DV300为例,在productdefine/common/products/Hi3516DV300.json 中添加updater:

     "updater:updater":{},

6、updater分区镜像编译

编译配置在build仓下,build_updater_image.sh 脚本中,该脚本由OHOS 编译系统调用

 

1.4 配置MISC分区

OpenHarmony使用MISC分区保存启动时的指令,默认的MISC分区的结构体为:

struct UpdateMessage {
    char command[32];
    char update[1280];
    char reserved[736];
};

command为当前的命令,update存放升级包路径,reserved存放保留信息。

1.5 镜像编译

使能镜像编译选项,编译脚本会将out/rk3568/packages/phone/updater文件夹作成updater.img镜像。先使用gzip工具压缩,然后使用cpio工具制作镜像。镜像格式为ASCII cpio archive (SVR4 with no CRC)。updater.img没有内核,以根文件系统挂载。out/rk3568/packages/phone/updater目录结构与根文件系统结构一致。过多添加文件到updater.img会导致镜像超过32M编译失败。

1.6 镜像启动流程

完整的启动流程如下:

 

1.7 配置uboot和内核

uboot需要读取MISC分区的指令,如果command为updater,uboot能启动到updater模式。内核需要根据uboot的指令加载updater.img。

Updater 子系统与主系统共用同一个内核,但用户态是从不同的分区启动。

Misc 分区是裸盘,是主系统和updater 子系统的一个沟通介质。当主系统OTA service下载到升级包后,会将升级包的位置记录到Misc分区。系统重启后,将从updater 分区启动并进入Updater子系统。

updater模式启动与正常模式启动流程一致。当正常模式调通后,参考正常模式完成updater模式的调试。 updater.img镜像是ramdisk格式且没有内核,需要uboot先拉起内核,然后加载updater.img到内存,作为根文件系统挂载到内核。如果芯片不支持ramdisk格式,需要进一步适配。

如果缺少misc或updater分区是无法升级的,需要将其加上。

1.8 init服务启动

根文件系统挂载完成,会启动init服务,参考内核启动[内核启动]。 init启动引导组件对应的进程为init进程,是内核完成初始化后启动的第一个用户态进程。init进程启动之后,读取init.cfg配置文件,依次启动各关键系统服务进程。updater模式下的init组件功能与正常模式一致,但是updater模式会根据自身业务对配置文件进行精简,只保留必须的命令。 启动配置文件由以下配置文件组成:

  • 与产品相关的配置文件 以RK3568为例,updater模式的init.cfg文件位于产品目录device/board/hihope/rk3568/updater下。init.cfg是启动的入口,通过import方式导入其他配置文件。init.rk3568.usb.cfg主要是USB和HDC相关的配置,与产品特性有关。

  • updater_common.cfg updater模式通用的启动,代码目录。updater_common.cfg包含了多个基础服务,这些服务与产品解耦,依赖系统组件。

  • 复用正常模式的配置文件 为了降低耦合复用正常模式的配置文件。这部分配置文件适用于正常模式和updater模式,不需要针对性适配。通过BUILD.g设置复制到updater.img,通过import方式调用。 init.usb.cfg:HDC相关的配置 init.usb.configfs.cfg :USB相关的配置 faultloggerd.cfg:faultloggerd服务配置文件,用于捕获updater模式的crash异常 hilogd.cfg:hilogd配置文件,用于打印OpenHarmony所有组件的日志。

配置文件执行顺序:默认依序执行执行pre-init,init,post-init。 常见的设置:

  • ueventd服务:pre-init阶段启动,所有组件的依赖项,不同产品ueventd配置不一样,建议放产品目录

  • 软连接:通过软连接方式将产品的路径转换为openharmony的路径。

  • 挂载分区:updater模式启动阶段不挂载分区,升级流程中根据业务场景选择性挂载。

  • 设置文件权限:如果二进制没有执行权限,服务会无法正常启动,需要在服务启动前设置权限。

  • 执行脚本:updater模式部分场景不挂载data分区,出现异常无法保存日志,会调用脚本保存日志。

 

1.9 升级服务启动

完成配置文件的适配,单板运行时会根据配置文件顺序依次执行配置中的命令。最终拉起升级子系统核心服务updater进程。updater服务启动以后,屏幕会显示UI界面,进行安装包升级,恢厂,重启等功能。升级服务功能如下所示:

 

初次适配会出现进程功能缺失等情况,参考如下方法进行调试

  • 使用ps -A命令查询进程是否启动。

  • 使用dmesg查看内核日志。

  • 使用hilog和faultlogger查看进程是否出现crash。

  • 手动拉起服务,如果服务能启动,检查启动配置文件是否正确。

 

 

2、OTA升级适配

正常模式有两个子程序用于操作misc分区。 执行write_updater命令向misc分区的update字段写入指令,指令格式参考参考write_updater源码base/update/updater/utils/write_updater.cpp。 执行reboot updater命令,向misc分区的command写入指令,单板会重新启动,重启过程中uboot读取misc分区的命令,从command命令判断当前要进入updater模式,依次加载内核和updater.img,完成从正常模式到updater模式的启动流程。 updater进程启动后读取update字段的命令,获取升级包的路径,单板进入升级流程,直至升级成功重启。

2.1 配置uboot和内核

uboot需要读取MISC分区的指令,如果command为updater,uboot能启动到updater模式。内核需要根据uboot的指令加载updater.img。

Updater 子系统与主系统共用同一个内核,但用户态是从不同的分区启动。

Misc 分区是裸盘,是主系统和updater 子系统的一个沟通介质。当主系统OTA service下载到升级包后,会将升级包的位置记录到Misc分区。系统重启后,将从updater 分区启动并进入Updater子系统。

updater模式启动与正常模式启动流程一致。当正常模式调通后,参考正常模式完成updater模式的调试。 updater.img镜像是ramdisk格式且没有内核,需要uboot先拉起内核,然后加载updater.img到内存,作为根文件系统挂载到内核。如果芯片不支持ramdisk格式,需要进一步适配。

如果缺少misc或updater分区是无法升级的,需要将其加上。misc分区建议配置大小为1M,updater分区建议配置大小为32M以上。

 

2.2 升级镜像需要的fstab配置

执行升级的时候,如果没有在该文件中配置对应的镜像,是无法升级成功的。

./device/board/hihope/rk3568/updater/config/fstab.updater

 

2.3 updater模式界面

正常模式只输入reboot updater命令,进入updater模式界面。此模式可以进行SD卡升级。

 

3、升级包制作工具

升级包制作工具是使用python开发,运行在PC端用来制作升级包的工具,功能主要包括:全量升级包制作、差分升级包制作以及变分区升级包制作。它首先会打包各个升级镜像,然后对升级包进行签名,同时生成升级包执行脚本,最后制作出升级包。

  • 全量升级包制作:升级包中只包括镜像全量升级相关数据,用于镜像全量升级;

  • 差分升级包制作:升级包中只包括镜像差分升级相关数据,用于镜像差分升级;

  • 变分区升级包:升级包中包括分区表、镜像全量数据,用于变分区处理和变分区后的镜像恢复。

代码目录

/base/update/packaging_tools
├── lib                         # 制作升级包工具依赖库目录
├── blocks_manager.py           # BlocksManager类定义,用于block块管理
├── build_update.py             # 差分包制作工具入口代码,入口参数定义
├── build_pkcs7.py              # 升级包签名
├── create_update_package.py    # 升级包制作
├── gigraph_process.py          # 生成Stash,重置ActionList的顺序
├── image_class.py              # 全量镜像、稀疏镜像解析处理
├── log_exception.py            # 全局log系统定义,自定义exception
├── patch_package_process.py    # 差分镜像处理,Block差分获取patch差异
├── script_generator.py         # 升级脚本生成器
├── transfers_manager.py        # 创建ActionInfo对象
├── unpack_update_package.py    # 升级包反解
├── update_package.py           # 升级包格式管理、升级包写入
├── utils.py                    # Options管理,其他相关功能函数定义
└── vendor_script.py            # 厂商升级流程脚本扩展

约束

工具运行环境配置:

  • Ubuntu18.04或更高版本系统;

  • python3.5及以上版本;

  • python库xmltodict, 解析xml文件,需要单独安装;

 

使用说明

  • bsdiff可执行程序,差分计算,比较生成patch;

  • imgdiff可执行程序,差分计算,针对zip、gz、lz4类型的文件,对比生成patch;

  • e2fsdroid可执行程序,差分计算,用于生成镜像的map文件。

工具参数配置说明:

 

4、 升级包制作流程

4.1 升级包简介

升级包里面有两个文件,包括build_tools.zip 和update.bin。

build_tools.zip:用来辅助升级的工具,包括升级的可执行文件updater_binary和升级脚本,镜像描述

update.bin:TLV编码的文件,所有的升级内容按照TLV格式序列化存储,最终生成update.bin

工具对update.bin 和最终生成的升级包(zip 压缩文件)分别进行签名。

4.2 制作全量升级包

1.首先在base\update\packaging_tools目录下,创建文件夹target_package和output_package。

2.进入 cd target_package文件夹,创建 mkdir updater_config。

3.进入 cd updater_config 文件夹,

从device\board\hisilicon\hispark_taurus\linux\updater\config路径下,拷贝BOARD.list和VERSION.mbn到base\update\packaging_tools\target_package\updater_config。

从device\board\hisilicon\hispark_taurus\linux\system下拷贝updater_specified_config.xml到base\update\packaging_tools\target_package\updater_config。

3.从out\rk3568\packages\phone\images路径所有文件拷贝至base\update\packaging_tools\target_package。

从out\rk3568\packages\phone\system\bin路径下拷贝文件updater_binary 至base\update\target_package。

4.将文件base\update\updater\test\unittest\test_data\src\rsa_private_key2048.pem拷贝至base\update\packaging_tools下

文件说明:

BOARD.list:存放当前升级包支持的产品list

 

如果是rk3568,请在文件中添加RK3568

 

VERSION.mbn:存放当前升级包所支持的版本范围

 

查询当前设备软件版本,rk3568开机后,点击设置,点击关于设备->软件版本,然后添加到文件里面去。

 

updater_specified_config.xml:分区表文件,结构参考下图

 

updater_specified_config.xml 组件配置文件节点说明

 

在base\update\packaging_tools终端执行升级包制作命令: python3 build_update.py ./target_package/ ./output_package/ -pk ./rsa_private_key2048.pem

 

4.3 手动命令触发全量包升级

  1. 进入设备:hdc_std shell

  2. 切换到 data 文件夹:cd data

  3. 新建 updater 目录:mkdir updater

  4. 退出设备:exit

  5. 传输升级包:hdc_std file send + 升级包地址 /data/updater/updater.zip (在设备侧的文件明一定要是updater.zip)

  6. 进入设备:hdc_std shell

  7. 输入指令:

    write_updater updater /data/updater/updater.zip

  8. 触发升级:

    reboot updater

    获取版本号命令:param get hw_sc.build.os.version

 

升级查看日志文件

  1. 查看日志文件列表:ls -l data/updater/log

  2. 打开日志文件:cat data/updater/log/updater_log

  3. 升级成功标志文件:data/updater/updter_result

     

4.4 升级包安装

升级包安装是升级子系统的核心功能,主要包括:

1.从misc分区获取升级命令,根据不同的命令执行不同的任务。

2.对升级包进行解压和合法性效验。

3.启动升级进程,并解析出升级脚本。

4.根据升级脚本安装各个组件包。

5.完成升级后,进行后处理。如清理升级包,记录升级状态等。

 

 

5、OTA升级UX界面适配

 

5.1 打开升级界面UI开关。

// base/update/updater/updater_default_cfg.gni
​
declare_args() {
  updater_cfg_file = ""
  updater_ui_support = true
}

5.2 修改产品init编译配置项

需要产品init编译配置项增加以下配置

// device/board/hihope/rk3568/updater/BUILD.gn
​
...
updater_usb_init_cfg_path = "//base/startup/init/services/etc/init.usb.cfg"
updater_init_usb_configfs_path_cfg =
    "//drivers/peripheral/usb/cfg/init.usb.configfs.cfg"
​
updater_faultloggerd_cfg =
    "//base/hiviewdfx/faultloggerd/services/config/faultloggerd.cfg"
updater_hilog_cfg = "//base/hiviewdfx/hilog/services/hilogd/etc/hilogd.cfg"
​
ohos_prebuilt_etc("updater_hilog.cfg") {
  source = "${updater_hilog_cfg}"
  install_images = [ "updater" ]
  part_name = "huanglong_sdk"
}
​
ohos_prebuilt_etc("updater_faultloggerd.cfg") {
  source = "${updater_faultloggerd_cfg}"
  install_images = [ "updater" ]
  part_name = "huanglong_sdk"
}
​
ohos_prebuilt_etc("updater_init_usb.cfg") {
  source = "${updater_usb_init_cfg_path}"
  install_images = [ "updater" ]
  part_name = "huanglong_sdk"
}
​
ohos_prebuilt_etc("updater_init_usb_configfs.cfg") {
  source = "${updater_init_usb_configfs_path_cfg}"
  install_images = [ "updater" ]
  part_name = "huanglong_sdk"
}
...
group("init_configs") {
  deps = [
    ":signing_cert.crt",
    ":init_fstab_ramdisk",
    ":init_fstab_vendor",
    ":init_fstab_updater",
    ":updater_init.cfg",
    ":updater_init.wudangstick.usb.cfg",
    ":updater_faultloggerd.cfg", 
    ":updater_hilog.cfg",   
    ":updater_init_usb.cfg",   
    ":updater_init_usb_configfs.cfg"   
  ]
}

5.3 UX界面资源配置项

在`base/update/updater/resources 下配置对应产品的图片资源和界面配置文件。

img

5.4 更改升级UX界面配置文件名

base/update/updater/resources/rk3568/的目录pages文件夹下,更改config.json文件配置。

// base/update/updater/resources/rk3568/pages/config.json
{
    "config" : "/resources/pages/rk3568.json"   //界面文件
}

5.5 更改base/update/updater/resources 编译配置文件。

// base/update/updater/resources/BUILD.gn
...
if (product_name == "rk3568") { // 更改为对应的产品名称
  updater_resources_list += [
    ...
    "${product_name}/pages/config.json",
    "${product_name}/pages/rk3568.json", // 更改对应文件名
    "${product_name}/pages/confirm.json",
    ...
  ]
}
...

 

 

6、示例服务器开发概述

## 基本概念

- 搜包服务:升级服务(UpdateService)提供的服务能力之一,依赖支持TCP和SSL协议的服务器。

- 搜包服务器:即支持搜包服务的通过TCP连接并支持SSL协议的服务器,本概述提及的升级服务示例服务器即是符合要求的搜包服务器之一。

- 下载服务器:普通的HTTP服务器即可满足下载服务器的要求。

- update.serverip.search:系统参数,指升级服务(UpdateService)侧配置的搜包服务器IP地址,默认值:127.0.0.1。

## 约束与限制

- 服务器返回报文格式Json示例,注意verifyInfo字段是升级包的sha256值,size字段是升级包的大小,单位为字节。

{

"searchStatus": 0,

"errMsg": "success",

"checkResults": [{

"versionName": "versionNameSample",

"versionCode": "versionCodeSample", 升级包版本号

"verifyInfo": "verifyInfoSHA256Value1234567", 升级包的sha256

"size": 1234567, 升级包大小

"packageType": 1,

"url": "http://serverAddressSample/packageNameSample.fileTypeSample",

"descriptPackageId": "abcdefg1234567ABCDEFG"

}],

"descriptInfo": [{

"descriptPackageId": "abcdefg1234567ABCDEFG",

"content": "This package is used for update."

}]

}

示例服务器部署到设备上,客户端就可以直接通过127.0.0.1访问

  1. 考虑将服务器生成为一个可执行的二进制文件,我们将模块放到base/update/updater/updateservice下面,制作build.gn文件,需要一些相关依赖。

    img

     

编译后就会生成二进制文件,可以将文件推到设备上运行。

  1. 参考下面文档生成服务器对应的crt和pem文件,和测试服务器文件推到开发板同一个目录。

  2. 使用命令chmod 给二进制文件添加可执行权限。

out目录下编译生成的文件路径:

文档参考目录:

openharmony/docs/zh-cn/application-dev/device/sample-server-guidelines.md

openharmony/docs/zh-cn/application-dev/device/sample-server-overview.md

 

 

Logo

社区规范:仅讨论OpenHarmony相关问题。

更多推荐