- IPC/RPC组件机制
1.基本概念
IPC:设备内的进程间通信(Inter-Process Communication)。
RPC:设备间的进程间通信(Remote Procedure Call)。
IPC/RPC用于实现跨进程通信,不同的是前者使用Binder驱动,用于设备内的跨进程通信,后者使用软总线驱动,用于跨设备跨进程通信。需要跨进程通信的原因是因为每个进程都有自己独立的资源和内存空间,其他进程不能随意访问不同进程的内存和资源,IPC/RPC便是为了突破这一点。
Client-Server 软件模型外加 Interface 接口共同组成了 OpenHarmony 诸多子系统的架构,使其系统的稳定性、扩展性以及进程通信能力得到加强。子系统中均可见到 Interface 接口目录、Framework 客户端目录以及 Service 服务端目录,工作重点一般集中在 Service 服务端,如下图所示:
1.1.实现原理
IPC和RPC通常采用客户端-服务端(Client-Server)模型,在使用时,请求Client端进程可获取Server端所在进程的代理(Proxy),并通过此代理读写数据来实现进程间的数据通信,更具体的讲,首先客户端会建立一个服务端的代理对象,这个代理对象具备和服务端一样的功能,若想访问服务端中的某一个方法,只需访问代理对象中对应的方法即可,代理对象会将请求发送给服务端;然后服务端处理接受到的请求,处理完之后通过驱动返回处理结果给代理对象;最后代理对象将请求结果进一步返回给客户端。
如下图所示: 通常,Stub会先注册系统能力(System Ability)到系统能力管理者(System Ability Manager,缩写SAMgr)中,SAMgr负责管理这些SA并向Client提供相关的接口。Client要和某个具体的SA通信,必须先从SAMgr中获取该SA的代理Proxy对象,然后使用代理Proxy对象和SA通信。在整个通信过程中,如果使用的是IPC通信,则依赖的是Binder驱动,使用的是RPC通信,则依赖的是软总线驱动。
说明: 以下为IPC与RPC的典型使用场景:
- IPC典型使用场景在后台服务,应用的后台服务通过IPC机制提供跨进程的服务调用能力。
- RPC典型使用场景在多端协同,多端协同通过RPC机制提供远端接口调用与数据传递能力。
IPC通信机制架构图:
目录结构:
/foundation/communication/ipc
├── interfaces # 对外接口存放目录
│ └── innerkits # 对内部子系统暴露的头文件存放目录
│ ├── ipc_core # ipc 接口存放目录
│ └── libdbinder # dbinder 接口存放目录
├── ipc # ipc 框架代码
│ ├── native # ipc native 实现存放目录
│ ├── src # ipc native 源代码存放目录
│ └── test # ipc native 单元测试用例存放目录
│ └── test # ipc native 模块测试用例存放目录
├── service # dbinder 实现存放目录
│ └── dbinder # dbinder 源代码存放目录
1.2.Binder机制
Binder机制通常采用客户端-服务器(Client-Server)模型,服务请求方(Client)可获取服务提供方(Server)的代理 (Proxy),并通过此代理读写数据来实现进程间的数据通信。通常,系统能力(SystemAbility)Server侧会先注册到系统能力管理者(System Ability Manager,缩写SAMgr)中,SAMgr负责管理这些SA并向Client提供相关的接口(添加,查询,获取,删除等)。Client要和某个具体的SA通信,必须先从SAMgr中获取该SA的代理,然后使用代理和SA通信。
注:SAMgr本身也是IPC的Server端,Client通过SAMgr的代理,调用SAMgr的接口。
Binder机制架构图:
2.系统服务管理子系统
系统服务管理子系统由两部分构成:
- 系统服务框架组件(safwk):定义了SystemAbility的实现方法,并提供启动、发布等接口实现。
- 系统服务管理组件(samgr): 提供系统服务注册、查询等功能。
系统服务管理架构图如下:
代码目录:
/foundation/systemabilitymgr
│── safwk # 组件目录
│ ├── bundle.json # 组件描述及编译脚本
│ ├── etc # 配置文件
│ ├── interfaces # 对外接口目录
│ ├── services # 框架实现
│ ├── test # 测试用例
├── samgr
│ ├── bundle.json # 部件描述及编译文件
│ ├── frameworks # 框架实现存在目录
│ ├── interfaces # 接口目录
│ ├── services # 组件服务端目录
│ ├── test # 测试代码存放目录
│ ├── utils # 工具类目录
2.1.系统服务框架组件
SystemAbility实现一般采用XXX.cfg + saId.xml + libXXX.z.so的方式由init进程解析对应的XXX.cfg文件拉起SystemAbility所依赖的进程。(注:多个系统服务可能跑在同一个进程里。比如AbilityManagerService、BatteryService、WindowManagerService都在foundation进程,MMIService在独立的进程multimodalinput中。)
SystemAbility类图如下:
备注:
SystemAbility子类需要重写OnStart()和OnStop()方法,并且在OnStart()方法中调用Publish(sptr systemAbility)方法把系统服务发布出去。
2.2.系统服务实现步骤
以AbilityManagerService为例说明SystemAbility的实现。
2.2.1.定义IPC对外接口IXXX
定义该服务对外提供的能力集合函数,统一继承IPC接口类IRemoteBroker;同时声明该IPC对外接口唯一标识符DECLARE_INTERFACE_DESCRIPTOR(XXX);该标识符用于IPC通信的校验等目的。
foundation\ability\ability_runtime\interfaces\inner_api\ability_manager\include\ability_manager_interface.h:69 class IAbilityManager : public OHOS::IRemoteBroker {70 public:71 DECLARE_INTERFACE_DESCRIPTOR(u"ohos.aafwk.AbilityManager")7273 /**74 * StartAbility with want, send want to ability manager service.75 *76 * @param want, the want of the ability to start.77 * @param userId, Designation User ID.78 * @param requestCode, Ability request code.79 * @return Returns ERR_OK on success, others on failure.80 */81 virtual int StartAbility(82 const Want &want,83 int32_t userId = DEFAULT_INVAL_VALUE,84 int requestCode = DEFAULT_INVAL_VALUE) = 0;8586 ...87 }
- 定义客户端代码XXXProxy
foundation\ability\ability_runtime\services\abilitymgr\include\ability_manager_proxy.h:30 class AbilityManagerProxy : public IRemoteProxy<IAbilityManager> {31 public:32 explicit AbilityManagerProxy(const sptr<IRemoteObject> &impl) : IRemoteProxy<IAbilityManager>(impl)33 {}3435 virtual ~AbilityManagerProxy()36 {}3746 virtual int StartAbility(47 const Want &want,48 int32_t userId = DEFAULT_INVAL_VALUE,49 int requestCode = DEFAULT_INVAL_VALUE) override;5051 ...52 }
foundation\ability\ability_runtime\services\abilitymgr\src\ability_manager_proxy.cpp42 int AbilityManagerProxy::StartAbility(const Want &want, int32_t userId, int requestCode)43 {44 int error;45 MessageParcel data;46 MessageParcel reply;47 MessageOption option;4849 if (!WriteInterfaceToken(data)) {50 return INNER_ERR;51 }52 if (!data.WriteParcelable(&want)) {53 HILOG_ERROR("want write failed.");54 return INNER_ERR;55 }5657 if (!data.WriteInt32(userId)) {58 HILOG_ERROR("userId write failed.");59 return INNER_ERR;60 }6162 if (!data.WriteInt32(requestCode)) {63 HILOG_ERROR("requestCode write failed.");64 return INNER_ERR;65 }6667 error = SendRequest(AbilityManagerInterfaceCode::START_ABILITY, data, reply, option);72 return reply.ReadInt32();73 }
AbilityManagerProxy::StartAbility()实现代码中会调用Remote()->SendRequest(IAbilityManager::START_ABILITY, data, reply, option);把消息码和数据发送给服务端。
-定义服务端代码XXXStub
foundation\ability\ability_runtime\services\abilitymgr\include\ability_manager_stub.h34 class AbilityManagerStub : public IRemoteStub<IAbilityManager> {35 public:36 AbilityManagerStub();37 ~AbilityManagerStub();38 virtual int OnRemoteRequest(39 uint32_t code, MessageParcel &data, MessageParcel &reply, MessageOption &option) override;4048 virtual int DoAbilityForeground(const sptr<IRemoteObject> &token, uint32_t flag) override;4950 ...51 }
foundation\ability\ability_runtime\services\abilitymgr\src\ability_manager_stub.cpp:332 int AbilityManagerStub::OnRemoteRequest(uint32_t code, MessageParcel &data, MessageParcel &reply, MessageOption &option)333 {334 std::u16string abilityDescriptor = AbilityManagerStub::GetDescriptor();335 std::u16string remoteDescriptor = data.ReadInterfaceToken();336 if (abilityDescriptor != remoteDescriptor && extensionDescriptor != remoteDescriptor) {337 HILOG_ERROR("local descriptor is not equal to remote");338 return ERR_INVALID_STATE;339 }340341 auto itFunc = requestFuncMap_.find(code);342 if (itFunc != requestFuncMap_.end()) {343 auto requestFunc = itFunc->second;344 if (requestFunc != nullptr) {345 return (this->*requestFunc)(data, reply);346 }347 }348 HILOG_WARN("default case, need check.");349 return IPCObjectStub::OnRemoteRequest(code, data, reply, option);350 }
requestFuncMap_[START_ABILITY] = &AbilityManagerStub::StartAbilityInner。消息码START_ABILITY对应的函数为AbilityManagerStub::StartAbilityInner。
600 int AbilityManagerStub::StartAbilityInner(MessageParcel &data, MessageParcel &reply)601 {602 Want *want = data.ReadParcelable<Want>();603 if (want == nullptr) {604 HILOG_ERROR("want is nullptr");605 return ERR_INVALID_VALUE;606 }607 int32_t userId = data.ReadInt32();608 int requestCode = data.ReadInt32();609 int32_t result = StartAbility(*want, userId, requestCode);610 reply.WriteInt32(result);611 delete want;612 return NO_ERROR;613 }
StartAbility()的实现在AbilityManagerStub的实现类AbilityManagerService中。
- SystemAbility的实现类
foundation\ability\ability_runtime\services\abilitymgr\include\ability_manager_service.h76 class AbilityManagerService : public SystemAbility,77 public AbilityManagerStub,78 public AppStateCallback,79 public std::enable_shared_from_this<AbilityManagerService> {80 DECLARE_DELAYED_SINGLETON(AbilityManagerService)81 DECLEAR_SYSTEM_ABILITY(AbilityManagerService)82 public:83 void OnStart() override;84 void OnStop() override;8586 virtual void OnAddSystemAbility(int32_t systemAbilityId, const std::string& deviceId) override;8788 virtual void OnRemoveSystemAbility(int32_t systemAbilityId, const std::string& deviceId) override;8990 ServiceRunningState QueryServiceState() const;91100 virtual int StartAbility(101 const Want &want, int32_t userId = DEFAULT_INVAL_VALUE, int requestCode = DEFAULT_INVAL_VALUE) override;102 ...103 }
AbilityManagerService同时继承了SystemAbility和AbilityManagerStub。在重写的SystemAbility的接口函数OnStart()中,调用Publish(instance_)把自己发布出去。
foundation/ability/ability_runtime/services/abilitymgr/src/ability_manager_service.cpp268 void AbilityManagerService::OnStart()269 {270 if (state_ == ServiceRunningState::STATE_RUNNING) {271 HILOG_INFO("AMS has already started.");272 return;273 }274 HILOG_INFO("AMS starting.");275 if (!Init()) {276 HILOG_ERROR("Failed to init AMS.");277 return;278 }279 state_ = ServiceRunningState::STATE_RUNNING;280 /* Publish service maybe failed, so we need call this function at the last,281 * so it can't affect the TDD test program */282 instance_ = DelayedSingleton<AbilityManagerService>::GetInstance().get();287 bool ret = Publish(instance_); 292293 SetParameter(BOOTEVENT_A // In namespace OHOS true");294 WatchParameter(BOOTEVENT namespace AAFwk {} ), AAFwk::ApplicationUtil::AppFwkBootEventCallback, nullptr);295 AddSystemAbilityListener(BACKGROUND_TASK_MANAGER_SERVICE_ID);296 AddSystemAbilityListener(DISTRIBUTED_SCHED_SA_ID);297 AddSystemAbilityListener(BUNDLE_MGR_SERVICE_SYS_ABILITY_ID);298 HILOG_INFO("AMS start success.");299 }
注:在实现SystemAbility的时候,必须调用宏REGISTER_SYSTEM_ABILITY_BY_ID或者SystemAbility::MakeAndRegisterAbility()把SystemAbility注册到LocalAbilityManager中。可参考如下代码:
const bool REGISTER_RESULT = SystemAbility::MakeAndRegisterAbility(DelayedSingleton::GetInstance().get());
- SystemAbility配置
以c++实现的SA必须配置相关SystemAbility的profile配置文件才会完成SA的自动加载注册逻辑,否则没有编写配置文件的SystemAbility不会完成自动加载注册。配置方法如下:
在子系统的根目录新建一个以sa_profile为名的文件夹,然后在此文件夹中新建两个文件:一个以saId为前缀的xml文件,另外一个为BUILD.gn文件。比如AbilityManagerService,saId为ABILITY_MGR_SERVICE_ID(即180),对应的配置文件为180.xml。内容如下:
foundation/ability/ability_runtime/services/sa_profile/BUILD.gn:
14 import("//build/ohos/sa_profile/sa_profile.gni")1516 ohos_sa_profile("ams_sa_profile") {17 sources = [18 "180.json",19 "182.json",20 "183.json",21 "184.json",22 "501.json",23 ]2425 part_name = "ability_runtime"26 }
描述说明:
- part_name为相应部件名称;
- sources表示当前子系统需要配置的SystemAbility列表,可支持配置多个SystemAbility。
foundation/ability/ability_runtime/services/sa_profile/180.json:1 {2 "process": "foundation",3 "systemability": [4 {5 "name": 180,6 "libpath": "libabilityms.z.so",7 "run-on-create": true,8 "distributed": false,9 "dump_level": 110 }11 ]12 }
描述说明:
- 进程名字:该SystemAbility要运行的进程空间,此字段是必填选项。即AbilityManagerService跑在foundation进程中。
- 一个SystemAbility配置文件只能配置一个SystemAbility节点,配置多个会导致编译失败。
- SystemAbility的name为对应的saId必须与代码中注册的saId保持一致,必配项。
- libpath为SystemAbility的加载路径,必配项。
- run-on-create:true表示进程启动后即向samgr组件注册该SystemAbility;false表示按需启动,即在其他模块访问到该SystemAbility时启动,必配项。
- distributed:true表示该SystemAbility为分布式SystemAbility,支持跨设备访问;false表示只有本地跨进程访问。
- bootphase:可不设置;可以设置的值有三种:BootStartPhase、CoreStartPhase、OtherStartPhase(默认类型),三种优先级依次降低,在同一个进程中,会优先拉起注册配置BootStartPhase的SystemAbility,然后是配置了CoreStartPhase的SystemAbility,最后是OtherStartPhase;当高优先级的SystemAbility全部启动注册完毕才会启动下一级的SystemAbility的注册启动。
- dump-level:表示systemdumper支持的level等级,默认配置1。
以上步骤完成后,全量编译代码后会在out路径下生成一个以进程名为前缀的xml文件(比如foundation.xml),路径为:out\…\system\profile\foundation.xml。该文件整合了所有需要在该进程中运行的SA的saId.xml文件内容。(比如AbilityManagerService,WindowManagerService,PowerManagerService等SA的配置文件都会被集成到foundation.xml中)。
- Cfg配置文件
cfg配置文件为linux提供的native进程拉起策略,开机启动阶段由init进程解析cfg文件把目标进程拉起(动态加载的除外)。foundation进程的配置文件在systemabilitymgr子系统中。
foundation\systemabilitymgr\safwk\etc\profile\foundation.cfg34 "services" : [{35 "name" : "foundation",36 "path" : ["/system/bin/sa_main", "/system/profile/foundation.json"],37 "importance" : -20,38 "uid" : "foundation",39 "permission" : [40 "ohos.permission.INPUT_MONITORING",41 "ohos.permission.PERMISSION_USED_STATS",42 "ohos.permission.DISTRIBUTED_SOFTBUS_CENTER",43 "ohos.permission.DISTRIBUTED_DATASYNC",44 "ohos.permission.MICROPHONE",45 "ohos.permission.WRITE_CALL_LOG",46 "ohos.permission.READ_CONTACTS",47 "ohos.permission.READ_DFX_SYSEVENT",48 "ohos.permission.GRANT_SENSITIVE_PERMISSIONS",49 "ohos.permission.REVOKE_SENSITIVE_PERMISSIONS",50 "ohos.permission.MANAGE_SECURE_SETTINGS",51 "ohos.permission.GET_BUNDLE_INFO_PRIVILEGED",52 "ohos.permission.START_ABILITIES_FROM_BACKGROUND",53 "ohos.permission.ACCESS_SERVICE_DM",54 "ohos.permission.STORAGE_MANAGER",55 "ohos.permission.PROXY_AUTHORIZATION_URI",56 "ohos.permission.ABILITY_BACKGROUND_COMMUNICATION",57 "ohos.permission.USE_USER_IDM",58 "ohos.permission.MANAGE_LOCAL_ACCOUNTS",59 "ohos.permission.LISTEN_BUNDLE_CHANGE",60 "ohos.permission.GET_TELEPHONY_STATE",61 "ohos.permission.SEND_MESSAGES",62 "ohos.permission.CONNECT_CELLULAR_CALL_SERVICE",63 "ohos.permission.SET_TELEPHONY_STATE"
refer to
- https://gitee.com/openharmony/communication_ipc
- https://blog.csdn.net/procedurecode/article/details/130222081
- https://gitee.com/openharmony/docs/blob/39467f023bec8cfca8ec2f97b99039b1dbd141e5/zh-cn/application-dev/ipc/ipc-rpc-overview.md
- https://forums.openharmony.cn/forum.php?mod=viewthread&tid=2980
- https://www.51cto.com/article/701821.html