修改guide和usage

Signed-off-by: huruitao <huruitao@kaihong.com>
This commit is contained in:
huruitao 2024-09-10 18:07:59 +08:00
parent 4dbdc0948d
commit 0c475a9149
4 changed files with 175 additions and 153 deletions

View File

@ -4,7 +4,7 @@
当开发人员为OpenHarmony系统框架开发某些功能时有时需要将这个功能包装成一个独立的服务进程运行在系统中为了其它应用进程能够调用此服务开发人员需要基于系统IPC通信框架编写一套远程接口调用实现。 Service代码生成工具能够帮助用户生成框架代码提升开发效率。用户只需提供一个定义远程方法的.h头文件工具会自动生成整个Service框架的代码包含Ability注册、proxy/stub类实现、MessageParcel数据包构造、Service子系统编译及开机自启动相关配置文件。用户可基于框架代码专注于业务功能的编写。
![image-20240723101920662](C:\Users\kaihong\AppData\Roaming\Typora\typora-user-images\image-20240723101920662.png)
![image](./docs/figures/service_frame_structure.png)
---
@ -39,137 +39,7 @@ h2sa
运行逻辑
![image-20240723144207759](C:\Users\kaihong\AppData\Roaming\Typora\typora-user-images\image-20240723144207759.png)
js文件中的部分重要函数和参数
~~~
//main.js
let ops = stdio.getopt({
//命令行参数
'filename': { key: 'f', args: 1, description: '.h file', default: '' },
'out': { key: 'o', args: 1, description: 'output directory', default: '.' },
'loglevel': { key: 'l', args: 1, description: 'Log Level: 0~3', default: '1' },
'serviceId': { key: 's', args: 1, description: 'service register id: 9000~16777214', default: '9000' },
'versionTag': { key: 'v', args: 1, description: 'version tag: 4.1 / 3.2', default: '3.2' }
});
function genServiceFile(fileName) {
// 1. h文件解析保存为结构体
let rootInfo = analyze.doAnalyze(fileName, ops);
// 2. 根据结构体生成代码
let fileContent = gen.doGenerate(rootInfo);
// 3. 创建service工程目录
let servicePath = re.pathJoin(ops.out, rootInfo.serviceName.toLowerCase() + 'service');
let etcPath = re.pathJoin(servicePath, 'etc');
let includePath = re.pathJoin(servicePath, 'include');
let interfacePath = re.pathJoin(servicePath, 'interface');
let profilePath = re.pathJoin(servicePath, 'sa_profile');
let srcPath = re.pathJoin(servicePath, 'src');
createFolder(servicePath);
createFolder(etcPath);
createFolder(includePath);
createFolder(interfacePath);
createFolder(profilePath);
createFolder(srcPath);
// 4. 生成代码保存为文件
wirte2Disk(fileContent.serviceCfgGnFile, etcPath);
wirte2Disk(fileContent.proxyHFile, includePath);
wirte2Disk(fileContent.stubHFile, includePath);
...
}
~~~
~~~
// analyze.js
function doAnalyze(hFilePath, cmdParam) {
let parseResult = parseFileAll(hFilePath);
parseResult.isInclude = false;
AllParseFileList.push(parseResult);
let rootInfo = {
'serviceName': '',
'nameSpace': [],
'class': [],
'includes': [],
'using': [],
'serviceId': (cmdParam.serviceId === null || cmdParam.serviceId === undefined) ?
'9002' : cmdParam.serviceId,
'versionTag': (cmdParam.versionTag === null || cmdParam.versionTag === undefined) ?
'3.2' : cmdParam.versionTag,
'rawContent': parseResult.rawContent
};
analyzeNameSpace(rootInfo, parseResult);
analyzeClasses(rootInfo, parseResult.classes);
rootInfo.includes = parseResult.includes;
rootInfo.using = parseResult.using;
return rootInfo; //经过解析后返回的结构体;作为参数,传值调用使用
}
~~~
~~~
//generate.js
let fileContent = {
'iServiceHFile': {},
'proxyHFile': {},
'stubHFile': {},
'serviceHFile': {},
'proxyCppFile': {},
'stubCppFile': {},
'serviceCppFile': {},
'clientCppFile': {},
'buildGnFile': {},
'bundleJsonFile': {},
'profileGnFile': {},
'profileXmlFile': {},
'profileJsonFile': {},
'serviceCfgFile': {},
'serviceCfgGnFile': {},
'iServiceCppFile': {},
};
function doGenerate(rootInfo) {
rootHFileSrc = rootInfo.rawContent;
let lowServiceName = rootInfo.serviceName.toLowerCase();
let upperServiceName = rootInfo.serviceName.toUpperCase();
// 生成文件名
genFileNames(lowServiceName, rootInfo);
// 按模板生成.h和.cpp文件内容框架
let files = genFilesByTemplate(upperServiceName, lowServiceName, rootInfo);
// 替换文件includes
replaceIncludes(files, rootInfo);
...
// 替换namespace
replaceServiceName(files, rootInfo);
// 替换类名
let classInfo = rootInfo.class[0];
replaceClassName(files, classInfo);
...
// 文件内容汇总
fileContent.iServiceHFile.content = files.iServiceH;
fileContent.proxyHFile.content = files.proxyH;
fileContent.stubHFile.content = files.stubH;
fileContent.serviceHFile.content = files.serviceH;
fileContent.proxyCppFile.content = files.proxyCpp;
fileContent.stubCppFile.content = files.stubCpp;
fileContent.serviceCppFile.content = files.serviceCpp;
fileContent.clientCppFile.content = files.clientCpp;
...
return fileContent;
}
![image](./docs/figures/service_runLogic.png)
~~~
@ -228,6 +98,3 @@ function doGenerate(rootInfo) {
├── test_service_proxy.cpp
└── test_service_stub.cpp
~~~

View File

@ -1,4 +1,18 @@
### 工具使用方法说明
### h2sa工具
## 简介
h2sa工具即SERVICE框架生成工具当开发者为OpenHarmony系统框架开发某些功能时有时需要将这个功能包装成一个独立的服务进程运行在系统中为了其它应用进程能够调用此服务开发人员需要基于系统IPC通信框架编写一套远程接口调用实现。实现Service远程调用接口需要开发人员熟悉IPC通信框架了解proxy/stub的继承与实现方式掌握C++类型转为MessageParcel数据包的各种API方法有一定的学习成本。而Service代码生成工具能够帮助使用者生成框架代码提升开发效率。用户只需提供一个定义远程方法的.h头文件工具会自动生成整个Service框架的代码包含Ability注册、proxy/stub类实现、MessageParcel数据包构造、Service子系统编译及开机自启动相关配置文件。
## 约束
系统建议Ubuntu 20.04或者Windows 10
依赖版本VS Code 1.62.0
## 使用方法
#### 命令行
1. 安装python库 CppHeaderParser
@ -20,6 +34,35 @@
4. 将待转换的文件test.h文件拷贝到napi_generator/src/cli/h2sa/src/src/gen目录下
~~~cpp
napi_generator/src/cli/h2sa/examples/test.h
#ifndef TEST_H
#define TEST_H
namespace OHOS {
namespace Example {
/**
* @brief service服务提供IPC调用接口
* @ServiceClass
*/
class test {
public:
int testFunc(int v1, int v2, bool v3);
};
} // namespace Example
} // namespace OHOS
#endif // TEST_H
~~~
注意:.h文件中待生成的主class必须加注释@brief service服务提供IPC调用接口 ,如下所示:
```cpp
/**
* @brief service服务提供IPC调用接口
* @ServiceClass
*/
```
5. 在napi_generator/src/cli/h2sa/src/src/gen目录下执行命令生成service框架代码
~~~
@ -28,27 +71,139 @@
其中,参数详情如下: -f定义远程服务的.h文件 -l, 日志级别0-3默认为1 -o,生成框架代码输入到指定路径下; -s,指定serviceID。 -v,指定版本3.2和4.1默认版本为3.2
6. 输出testservice文件夹
#### 生成物
7. 编译步骤生成的testservice文件夹放在源码根目录下
1. 输出testservice文件夹其中的文件如下所示
8. 配置vendor serviceID
![](./docs/figures/h2sa_outRes.png)
9. 编配置完成后,执行镜像编译命令 ./build.sh --product-name 产品名若编译rk3568开发板则执行 ./build.sh --product-name rk3568
#### 生成物的应用和验证
10. 烧录镜像
1. 编译步骤生成的testservice文件夹放在对应版本的源码根目录下
11. 验证
2. 修改系统公共文件
> 验证一: shell登录开发板。 查看服务端进程是否已正常启动
>
> ~~~
> ps -ef | grep testservice
> ~~~
>
> 验证二:运行客户端
>
> ~~~
> /system/bin/testclient
> ~~~
##### 基础配置
1. 服务配置
在foundation/systemabilitymgr/samgr/interfaces/innerkits/samgr_proxy/include/
system_ability_definition.h增加以下一行
```
TEST_SERVICE_ID = 9016,
```
其中TEST_SERVICE_ID宏值与用户定义的serviceID一致。
2. 子系统配置
在build/subsystem_config.json中增加以下内容。
```
"testservice": {
"path":"testservice",
"name": "testservice"
}
```
3. 产品配置如rk3568
在vendor/kaihong/rk3568/config.json中增加以下内容
```
{
"subsystem": "testservice",
"components": [
{
"component": "testservice_part",
"features": []
}
]
}
```
4. 权限配置
在相应的产品目录的vendor/kaihong/rk3568/security_config/high_privilege_process_list.json中增加以下内容
```
{
"name": "testservice",
"uid": "system",
"gid": ["root", "system"]
}
```
##### selinux权限配置
上述基础配置时关闭了selinux 权限配置用户新增服务时需根据自身需求配置selinux 权限 。
若要配置selinux权限首先应将vendor/hihope/rk3568/config.json中"build_selinux"属性改为true然后修改以下文件
1. testservice/etc/sample_service.cfg
```
"secon" : "u:r:testservice:s0"
```
2. base/security/selinux_adapter/sepolicy/base/public/service_contexts
```
9016 u:object_r:sa_testservice:s0
```
3. base/security/selinux_adapter/sepolicy/base/public/service.te
```
type sa_testservice, sa_service_attr;
```
4. base/security/selinux_adapter/sepolicy/ohos_policy/startup/init/system/init.te
```
allow init testservice:process { getattr rlimitinh siginh transition };
```
5. base/security/selinux/sepolicy/base/public/type.te
```
type testservice, sadomain, domain;
```
6. /base/security/selinux/sepolicy/base/te目录下增加新service的te文件新增文件名即为服务名例如testservice.te
```
allow testservice init_param:file { map open read };
allow testservice sa_testservice:samgr_class { add get };
```
3. 编码完成后,执行镜像编译命令
~~~
./build.sh --product-name 产品名
若编译Hi3516DV300开发板则执行
./build.sh --product-name Hi3516DV300
若编译rk3568开发板则执行
./build.sh --product-name rk3568
~~~
4. 烧录镜像
5. 运行验证
> 验证一: shell登录开发板。 查看服务端进程是否已正常启动
>
> ~~~
> ps -ef | grep testservice
> system 288 1 0 00:02:13 ? 00:00:00 testservice_sa --- 服务进程已正常运行
> ~~~
>
> 验证二:运行客户端
>
> ~~~
> /system/bin/testclient
> ~~~

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB