• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# 模块升级概述
2
3## 模块升级简介
4
5模块化升级主要向OEM厂家的系统开发者提供。模块是指SDK public API层以下的系统服务或库,而非传统应用包。
6模块化升级允许系统开发者在满足依赖接口ABI(Application Binary Interface)稳定的前提下,将系统按模块、服务、或库进行解耦,实现独立打包、独立升级的能力。模块化升级可解决系统的模块解耦,各模块可独立于OTA版本进行发布和演进。
7
8## 模块升级基本概念
9
10- ABI(Application Binary Interface)稳定性
11
12  ABI稳定性指应用二进制接口稳定性,为了保证模块能独立升级,升级模块对OS其他模块依赖的接口和对其他模块提供的接口要保证稳定,防止模块独立升级后,因为接口兼容性问题导致运行异常。
13
14
15- 模块包
16
17  模块包是升级包的最小单位,包含独立升级的SystemAbility服务或系统库。
18
19
20- HMP(Harmony Module Package)包
21
22  为了便于模块包的批量打包和升级发布,对若干模块包进行组合打包和管理,统称为HMP包。HMP所包含的模块包范围由具体业务决定,是一个独立升级模块的集合。
23
24
25- 安全校验
26
27  模块包主要包含两重校验,一个是模块包要满足升级包本身签名校验,只有校验通过才允许安装;另一个是模块包中的image镜像,该镜像通过[HVB](https://gitee.com/openharmony/startup_hvb/blob/master/README_zh.md)(OpenHarmony Verified Boot)机制进行校验,保证模块包要满足HVB校验后才能挂载运行。
28
29## 实现原理
30
31模块包的制作包含模块定义和模块打包两部分:
32
331. 通过统一的编译模板(gn模板),定义模块包所包含的基本二进制,以及相关模块基本配置属性(config.json),公私钥定义和签名证书(仅示例,实际产品化时,需要替换为产品化签名密钥对和证书)。通过stable关键词标记,定义依赖接口的ABI稳定性。
342. 模块打包分为单模块包和多个模块包二次打包的HMP包。单模块包是能够升级的最小单位,为了OEM厂家在一次升级过程中能够管理多个模块,系统支持多个模块包统一打包发布。
35  模块包涉及到的需要保证ABI兼容的二进制,通过ABI校验工具,生成相关二进制的ABI信息文件作为基线,并归档到特定仓。每次模块包编译时,实时生成对应二进制的ABI信息文件,并与基线对比,不一致则编译失败。防止在一个OS大版本内相关的二进制的ABI发生变化,从而导致系统异常。
36
37## 约束与限制
38
391. 当前模块升级能力仅支持SystemAbility的升级,独立库形态由于加载依赖机制不同,目前待规划支持。
402. 仅预制到版本中的模块才允许升级,禁止新增模块升级。
413. 当前仅支持模块包升级本地安装和调试,暂未支持云端连接和升级包下载。
42# 模块包定义和调试
43
44## 新增模块包
45
46新增模块包的主要步骤:
47
481. 梳理模块及其依赖,依赖要尽可能少,且依赖链上的要以ABI兼容的模块结尾(具体说明参见[依赖传递和ABI兼容性检查](#依赖传递和abi兼容性检查)章节)。这其中可能还涉及到ABI不兼容的库改造为ABI兼容的库的工作。
49
502. 确保SA所在进程对应的cfg文件中配置了模块升级属性,cfg文件的编写方法参见:[引导启动配置文件](https://gitee.com/openharmony/docs/blob/master/zh-cn/device-dev/subsystems/subsys-boot-init-cfg.md)。模块升级属性需要添加到service属性中,service的配置具体参见:[服务管理](https://gitee.com/openharmony/docs/blob/master/zh-cn/device-dev/subsystems/subsys-boot-init-service.md),举例如下:
51
52   ```
53   "services" : [{
54       ......
55       "module-update" : 1                // 模块升级属性,配置为1则生效,默认不生效
56       ......
57   }
58   ```
59
60
613. 编写gn模板,打包编译模块包(具体说明参见[模块包配置](#模块包配置)章节)和HMP包(具体说明参见[HMP包配置](#hmp包配置)章节)
624. 本地调试(具体说明参见[模块升级本地调试](#模块升级本地调试))
63
64过程中涉及到的一些常见问题可以参见[常见问题](#常见问题)。
65
66## 模块包配置
67
68模块升级提供了两个编译模板,模块开发者可以通过在gn中使用编译模板并执行编译命令对该模块进行打包和签名。
69
70模块包使用编译模板`ohos_module_package`对单模块进行编译打包以及签名。
71
72模板字段详细说明:
73
74| 模板字段            | 含义                  | 赋值            | 必选   |
75| :-------------- | :------------------ | :------------ | :--- |
76| libraries       | 二进制库集合              | 二进制库目标target  | 否    |
77| binaries        | 可执行文件集合             | 可执行目标target   | 否    |
78| prebuilts       | 预编译文件集合             | 预编译目标target   | 否    |
79| etc             | 配置文件集合              | 配置目标target    | 否    |
80| module_config   | 指定模块配置文件config.json | 文件路径          | 是    |
81| zip_private_key | zip包签名私钥            | 文件路径          | 是    |
82| sign_cert       | zip包签名证书            | 文件路径          | 是    |
83| img_private_key | 镜像HVB签名私钥           | 文件路径          | 否    |
84| img_public_key  | 镜像HVB签名公钥           | 文件路径          | 否    |
85| empty           | 是否打包成空包,默认不打空包      | true or false | 否    |
86| part_name       | 部件名                 | 部件名           | 是    |
87| subsystem_name  | 子系统名                | 子系统名          | 是    |
88
89配置文件(config.json)说明:
90
91| 配置项     | 含义          | 规范                  |
92| :------ | :---------- | :------------------ |
93| name    | 升级模块(SA)的名称 | 与系统SA名称保持一致         |
94| id      | 升级模块(SA)的id | 与sa_profile配置id保持一致 |
95| version | 升级模块版本号     | a.v.p格式             |
96
97```plain
98{
99  "name": "sa_name",
100  "id": sa_id,
101  "version": "a.v.p"
102}
103```
104
105gn使用编译模板示例:
106
107```plain
108import("//build/templates/update/module_update.gni")
109ohos_module_package("wms_package") {
110  libraries = [
111    ":libwms",
112  ]
113  etc = [
114    "//foundation/window/window_manager/sa_profile:wms_sa_profile",
115  ]
116  module_config = "config.json"
117  zip_private_key = "key/rsa_private_key2048.pem"
118  sign_cert = "sign_cert/signing_cert.crt"
119  img_private_key = "key/test_priv.pem"
120  img_public_key = "key/test_pub.pem"
121  part_name = "window_manager"
122  subsystem_name = "window"
123}
124```
125
126## HMP包配置
127
128HMP包使用编译模板`ohos_hmp`对多个模块包进行打包。
129
130模板字段详细说明:
131
132| 模板字段            | 含义                 | 赋值          | 必选   |
133| :-------------- | :----------------- | :---------- | :--- |
134| module_packages | 指定需要包含在HMP中的模块包    | 模块包目标target | 是    |
135| pack_info       | 指定HMP配置文件pack.info | 文件路径        | 是    |
136
137配置文件(pack.info)说明:
138
139| 配置项     | 含义         | 规范        |
140| :------ | :--------- | :-------- |
141| name    | 总升级zip包名称  | 符合系统包名规范  |
142| version | 总升级zip包版本号 | a.v.p.c格式 |
143
144```plain
145{
146  "pacakge":
147  {
148    "name": "zip_name",
149    "version": "a.v.p.c"
150  }
151}
152```
153gn使用编译模板示例:
154```plain
155import("//build/templates/update/module_update.gni")
156ohos_hmp("demo_hmp") {
157  module_packages = [
158    "//foundation/window/window_manager/wmserver:wms_package",
159  ]
160  pack_info = "pack.info"
161}
162```
163
164## 依赖传递和ABI兼容性检查
165
166![](figures/module_package_ABI_compatibility.png)
167
168升级包要能独立于系统单独升级需要有一些约束,即整体升级包对外提供的接口是稳定的,也就是说对外的ABI要能兼容。除了对外稳定,整体升级包调用的接口也需要是稳定的,也就是说升级包依赖的库的ABI也需要是兼容的。
169
170如上图所示,升级包作为一个整体对上提供和对下调用的都需要是稳定的接口。拆开来看,其中:
171
172- 升级包内只有Library B对外提供接口给Library A,所以Library B需要保证ABI兼容性,否则升级后,Library A调用Library B会失败。
173- 升级包内只有Library C对外调用了接口且只依赖了Library D,则Library D需要保证ABI兼容性,否则升级后,Library C调用Library D会失败。
174
175如上图所示,Library B和Library D需要声明自身是ABI兼容的。可以通过在编译脚本里加上`stable = true`来声明一个二进制对外的ABI是兼容的。
176
177```plain
178ohos_shared_library("LibraryX") {
179  ......
180  stable = true
181  ......
182}
183```
184这样在编译时对相应的二进制做ABI兼容性校验,校验不通过的则编译失败。
185
186ABI兼容性校验通过[libabigail](https://gitee.com/openharmony/third_party_libabigail/blob/master/README_OpenHarmony.md)提供的工具abidw和abidiff来完成。abidw可以将二进制的ABI信息导出为一个文本文件,abidiff可以比较2个ABI信息文件是否相同。所以通过abidw生成前后2个版本的ABI信息文件,然后通过abidiff比较就可以知道二进制的ABI是否发生了变化。为此需要将系统里声明了ABI兼容的二进制的ABI信息文件放入单独的仓中作为基线,这个基线是代码上库时随源码一起合入的。在编译代码时,会生成当前最新的二进制的ABI信息文件并与基线中对应的文件比较,如果失败则表示二进制的ABI发生了变化。当然在开发阶段,这个基线是可以更新的。
187
188具体的以上面图里的Library B作为例子,Library B首次上库时的步骤:
189
1901. 开发完源码后,在Library B的编译脚本里加上:stable = true。
1912. 编译代码,发现编译失败,原因是缺少作为基线的ABI信息文件,导致无法比较ABI。
1923. 根据编译输出的错误提示,从out目录下拷贝对应的ABI信息文件到基线目录。
1934. 再次编译代码,发现编译成功。
1945. 将Library B的源码还有对应仓里作为基线的ABI信息文件一起上库。
195  当后续再次修改Library B时,如果修改改变了ABI,编译会报错,原因是当前的ABI与基线中的不一致,这时重复上述步骤3到步骤5即可。
196
197需要说明的是,作为基线的ABI信息文件在版本发布后则会冻结,只有在下一个版本中才允许修改。
198
199
200
201有时对外提供接口的二进制对下直接依赖的可能不是ABI兼容的二进制,如上图里的Library B,直接依赖了Library C,虽然Library C依赖了ABI兼容的Library D,但Library C本身不是ABI兼容的。所以在编译打包时,Library C也会随Library B一起打包,这样升级包整体对外调用的接口才能保证是ABI兼容的。这样做虽然保证了升级包的可用,但增加了升级包的大小。
202
203![](figures/module_package_dependence_transmit.png)
204
205依赖检查是一个递归的过程,以升级包直接依赖的二进制作为起点,以它依赖链上的ABI兼容的二进制作为终点,起点和终点会做ABI兼容性校验,校验通过后将除了终点以外的其他节点都打包到最终的升级包中。如上图,最终打包到升级包中的二进制为:A、B、C、D、E、H。所以在规划一个模块是否可以独立升级时,除了要将对外提供接口的二进制改造为ABI兼容的之外,首先需要梳理它们的依赖,总的原则是依赖要少,且依赖的最好都是ABI兼容的二进制,即上述的起点到终点的距离要短。
206
207## 模块升级本地调试
208
209模块升级调测工具(module_update_tool)是为本地调测模块提供升级功能的二进制工具,不随版本发布,可通过编译版本代码获得。生成的路径(以产品名RK3568为例):
210
211```
212out/rk3568/sys_installer/module_update_tool
213```
214
215调测时需要事先从编译产物中取出module_update_tool二进制文件,推送到设备的/system/bin目录下,并增加可执行权限:
216
217```shell
218chmod +x module_update_tool
219```
220- HMP包的安装
221
222  1. 推送待安装的HMP包到/data/目录,或/data目录下自己新建目录均可,如HMP包:wms_sa.zip
223
224     ```
225     hdc file send wms_sa.zip /data/wms_sa.zip
226     ```
227
228  2. 执行安装命令
229
230     ```
231     /system/bin/module_update_tool install /data/wms_sa.zip
232     ```
233
234     查看返回结果,失败返回对应错误码。
235
236- 卸载HMP包
237
238  指定HMP包的名字(pack.info中"name"对应的值)可以卸载HMP包。
239
240  ```
241  /system/bin/module_update_tool uninstall wms_sa
242  ```
243
244
245- 查询已升级HMP包信息
246
247  1. 查询所有已升级的HMP包信息:
248
249     ```
250     /system/bin/module_update_tool show
251     ```
252
253     输出示例:
254
255     ```
256     try to show module update info
257     Got 1 upgraded modules info
258     demo_hmp
259      {saName:wms saId:4606 version:101}
260
261     success
262     ```
263
264  2. 查询指定的HMP包的信息
265
266     ```
267     /system/bin/module_update_tool show wms_sa
268     ```
269
270     输出示例(当前只有一个HMP包的场景):
271
272     ```
273     try to show module update info
274     Got 1 upgraded modules info
275     demo_hmp
276      {saName:wms saId:4606 version:101}
277
278     success
279     ```
280
281# 常见问题
282
283## 编译失败:Exception: ABI info in xxx_abi_info.dump and xxx_abi_info.dump are different!
284
285问题现象:
286
287编译失败,具体报错log如下:
288
289```shell
290[OHOS ERROR] Traceback (most recent call last):
291[OHOS ERROR]   File "/home/xxx/code/openharmony_master/out/rk3568/../../build/ohos/update/check_abi_and_copy_deps.py", line 239, in <module>
292[OHOS ERROR]     sys.exit(main())
293[OHOS ERROR]   File "/home/xxx/code/openharmony_master/out/rk3568/../../build/ohos/update/check_abi_and_copy_deps.py", line 224, in main
294[OHOS ERROR]     copy_list = traverse_and_check(check_list, args.clang_readelf, abidiff_bin, abidw_bin, args.abi_dumps_path)
295[OHOS ERROR]   File "/home/xxx/code/openharmony_master/out/rk3568/../../build/ohos/update/check_abi_and_copy_deps.py", line 172, in traverse_and_check
296[OHOS ERROR]     do_check(target_out_dir, target_name, stripped_dir, readelf, abidiff, abidw, abi_dumps_path)
297[OHOS ERROR]   File "/home/xxx/code/openharmony_master/out/rk3568/../../build/ohos/update/check_abi_and_copy_deps.py", line 124, in do_check
298[OHOS ERROR]     raise Exception("ABI info in " + out_file + " and " + base_file + " are different!")
299[OHOS ERROR] Exception: ABI info in obj/device/soc/rockchip/rk3568/hardware/isp/librkaiq_abi_info.dump and ../../prebuilts/abi_dumps/ohos_clang_arm/librkaiq_abi_info.dump are different!
300```
301
302
303可能原因:
304
305更改了二进制的ABI。
306
307
308
309解决措施:
310
311先确认ABI是否有必要更改,如果确实需要则将out下的ABI信息文件拷贝覆盖到基线仓中并随源码一起上库。如上面的例子,需要执行:
312
313```shell
314cp out/rk3568/obj/device/soc/rockchip/rk3568/hardware/isp/librkaiq_abi_info.dump prebuilts/abi_dumps/ohos_clang_arm/librkaiq_abi_info.dump
315```
316
317
318
319## 编译失败:File xxx_abi_info.dump not exists!
320
321问题现象:
322
323编译失败,具体报错log如下:
324
325```shell
326[OHOS ERROR] Traceback (most recent call last):
327[OHOS ERROR]   File "/home/xxx/code/openharmony_master/out/rk3568/../../build/ohos/update/check_abi_and_copy_deps.py", line 239, in <module>
328[OHOS ERROR]     sys.exit(main())
329[OHOS ERROR]   File "/home/xxx/code/openharmony_master/out/rk3568/../../build/ohos/update/check_abi_and_copy_deps.py", line 224, in main
330[OHOS ERROR]     copy_list = traverse_and_check(check_list, args.clang_readelf, abidiff_bin, abidw_bin, args.abi_dumps_path)
331[OHOS ERROR]   File "/home/xxx/code/openharmony_master/out/rk3568/../../build/ohos/update/check_abi_and_copy_deps.py", line 172, in traverse_and_check
332[OHOS ERROR]     do_check(target_out_dir, target_name, stripped_dir, readelf, abidiff, abidw, abi_dumps_path)
333[OHOS ERROR]   File "/home/xxx/code/openharmony_master/out/rk3568/../../build/ohos/update/check_abi_and_copy_deps.py", line 121, in do_check
334[OHOS ERROR]     raise Exception("File " + base_file + " not exists!")
335[OHOS ERROR] Exception: File ../../prebuilts/abi_dumps/ohos_clang_arm/ace_napi_abi_info.dump not exists!
336```
337
338
339可能原因:
340
341基线仓中未找到对应的ABI信息文件。
342
343
344
345解决措施:
346
347在out下搜索xxx_abi_info.dump文件,然后拷贝到基线仓中,拷贝方法参见[上个问题](#编译失败exception-abi-info-in-xxx_abi_infodump-and-xxx_abi_infodump-are-different)。
348
349
350
351## 运行时错误:SELinux校验失败
352
353问题现象:
354
355升级失败,hilog中报错了相关的SELinux权限校验失败的日志。
356
357
358
359可能原因:
360
361未配置相应的SELinux权限。
362
363
364
365解决措施:
366
367使能模块升级的SA进程需要配置相应的SELinux权限,才能在SA启动时从升级目录加载so库。举例如下:
368
369```plain
370allow foundation module_update_file:dir { search };
371allow foundation module_update_file:file { open read getattr };
372allow foundation module_update_lib_file:dir { search };
373allow foundation module_update_lib_file:file { open read getattr map execute };
374```
375由于模块升级相关权限已被最小化管控,SA进程还需要在以下neverallow规则中配置例外才能使上面配置的权限生效:
376```plain
377# sa process which support module update should add itself here
378neverallow { domain -init -module_update_service -foundation debug_only(`-hdcd -sh') } { module_update_file
379  module_update_bin_file module_update_lib_file }:{ file dir } *;
380```
381
382
383
384## 编译失败:log中报错依赖的Python库asn1crypto或cryptography找不到
385
386问题现象:
387
388编译失败,编译日志中提示asn1crypto或cryptography找不到。
389
390
391
392可能原因:
393
394未安装Python依赖的asn1crypto或cryptography库。
395
396
397
398解决措施:
399
400执行build下的prebuilts_download.sh401
402```
403bash build/prebuilts_download.sh
404```
405
406