一、初识ServiceAbility
在OpenHarmony中基于framework层提供的服务都是基于Service Abiltiy实现的。Service Ability以下简称SA。SA在一个设备中只会存在一个实例的。开发者通过SA的机制可以实现跨进程的通信。
以下通过的例子方式说明如何使用OpenHarmony开源代码中提供现有SA。后续如果开发了自定义的SA,也可以通过此种方法对自定义的SA进行测试接口。
1.1 如何使用ServiceAbility
以下节选自OpenHarmony v3.2 Release版本。
// base\useriam\face_auth\services\src\face_auth_service.cpp
sptr<AppExecFwk::IBundleMgr> FaceAuthService::GetBundleMgr()
{
IAM_LOGI("start");
if (bundleMgr_ != nullptr) {
return bundleMgr_;
}
auto sam = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();
if (sam == nullptr) {
IAM_LOGE("GetSystemAbilityManager return nullptr");
return nullptr;
}
auto bundleMgrSa = sam->GetSystemAbility(BUNDLE_MGR_SERVICE_SYS_ABILITY_ID);
if (bundleMgrSa == nullptr) {
IAM_LOGE("GetSystemAbility return nullptr");
return nullptr;
}
bundleMgr_ = iface_cast<AppExecFwk::BundleMgrProxy>(bundleMgrSa);
return bundleMgr_;
}
-
首先通过单实例SystemAbilityManagerClient的方法GetSystemAbilityManager获取System ability的管理类实例sam。
-
然后通过SA定义相对应的ID号,获取远程的代理基类指针sptr。
-
通过类型转换成SA对应的代理类
-
此时即可通过bundleMgr_访问SA提供的方法。如bundleMgr->CheckIsSystemAppByUid(uid)
// base\useriam\face_auth\services\src\face_auth_service.cpp
int32_t FaceAuthService::SetBufferProducer(sptr<IBufferProducer> &producer)
{
...
int32_t uid = IPCSkeleton::GetCallingUid();
auto bundleMgr = GetBundleMgr();
if (bundleMgr == nullptr) {
IAM_LOGE("bundleMgr is nullptr");
return FACE_AUTH_ERROR;
}
if (!bundleMgr->CheckIsSystemAppByUid(uid)) {
IAM_LOGE("the caller is not a system application");
return FACE_AUTH_CHECK_SYSTEM_PERMISSION_FAILED;
}
...
return FACE_AUTH_SUCCESS;
}
1.2 SA的主要构成
一个SA主要由四个部份组成。
- 定义对外的IPC接口类
- 定义客户端通信代理proxy类
- 定义服务端通信stub类
- SA服务的实现类
以上四个类达成了跨进程通信与内容封装的整个过程,以下会通过OpenHarmony V3.2中的开源代码中的电话子系统其中的CoreService服务进行实例说明。
1.2.1 IPC接口类
// base\telephony\core_service\interfaces\innerkits\include\i_core_service.h
class ICoreService : public IRemoteBroker {
public:
DECLARE_INTERFACE_DESCRIPTOR(u"");
public:
virtual ~ICoreService() = default;
virtual int32_t GetPsRadioTech(int32_t slotId, int32_t &psRadioTech) = 0;
virtual int32_t GetCsRadioTech(int32_t slotId, int32_t &csRadioTech) = 0;
...
enum class InterfaceID {
GET_PS_RADIO_TECH = 0,
GET_CS_RADIO_TECH,
...
};
...
};
- 首先定义IPC接口类ICoreService,继承OpenHarmony统一对外提供的接口类IRemoteBroker。
- 同时实现该IPC对外接口的唯一标识符,该标识符用于IPC通信的校验等目的。通过DECLARE_INTERFACE_DESCRIPTOR提供标识符的定义与获取标识符的对外接口GetDescriptor。
- 定义该服务对外提供的能力集合函数。本例中GetPsRadioTech、GetCsRadioTech等接口即是。
- 定义的枚举为接口的code码,该code码用于IPC通信中,proxy端与stub端进行接口识别。对外有几个接口就需要定义几个code码。
1.2.2 代理proxy类
// base\telephony\core_service\interfaces\innerkits\include\core_service_proxy.h
class CoreServiceProxy : public IRemoteProxy<ICoreService> {
public:
explicit CoreServiceProxy(const sptr<IRemoteObject> &impl) : IRemoteProxy<ICoreService>(impl) {}
virtual ~CoreServiceProxy() = default;
int32_t GetPsRadioTech(int32_t slotId, int32_t &psRadioTech) override;
int32_t GetCsRadioTech(int32_t slotId, int32_t &csRadioTech) override;
...
private:
static inline BrokerDelegator<CoreServiceProxy> delegator_;
};
- 首先继承IRemoteProxy,如上所示
- 定义私有静态内联成员BrokerDelegator delegator_;
- 实现接口中的虚函数GetPsRadioTech、GetCsRadioTech,如下所示,通过MessageParcel对参数进行序列化操作,然后通过remote->SendRequest将接口对应的code码与对应的序列化参数传送至stub的实现类
// base\telephony\core_service\frameworks\native\src\core_service_proxy.cpp
int32_t CoreServiceProxy::GetPsRadioTech(int32_t slotId, int32_t &psRadioTech)
{
MessageParcel data;
MessageParcel reply;
MessageOption option;
if (!WriteInterfaceToken(data)) {
TELEPHONY_LOGE("GetPsRadioTech WriteInterfaceToken is false");
return TELEPHONY_ERR_WRITE_DESCRIPTOR_TOKEN_FAIL;
}
data.WriteInt32(slotId);
auto remote = Remote();
if (remote == nullptr) {
TELEPHONY_LOGE("GetPsRadioTech Remote is null");
return TELEPHONY_ERR_IPC_CONNECT_STUB_FAIL;
}
int32_t st = remote->SendRequest(uint32_t(InterfaceID::GET_PS_RADIO_TECH), data, reply, option);
if (st != ERR_NONE) {
TELEPHONY_LOGE("GetPsRadioTech failed, error code is %{public}d ", st);
return TELEPHONY_ERR_IPC_CONNECT_STUB_FAIL;
}
int32_t result = reply.ReadInt32();
if (result == TELEPHONY_ERR_SUCCESS) {
psRadioTech = reply.ReadInt32();
}
return result;
}
1.2.3 stub类
// base\telephony\core_service\services\core\include\core_service_stub.h
class CoreServiceStub : public IRemoteStub<ICoreService> {
public:
CoreServiceStub();
virtual ~CoreServiceStub() {}
int32_t OnRemoteRequest(uint32_t code, MessageParcel &data, MessageParcel &reply, MessageOption &option) override;
...
private:
using CoreServiceFunc = int32_t (CoreServiceStub::*)(MessageParcel &data, MessageParcel &reply);
int32_t OnGetPsRadioTech(MessageParcel &data, MessageParcel &reply);
int32_t OnGetCsRadioTech(MessageParcel &data, MessageParcel &reply);
...
private:
std::map<uint32_t, CoreServiceFunc> memberFuncMap_;
};
- 服务端stub类继承IRemoteStub,实现虚函数OnRemoteRequest完成接口code码与对应处理函数的对应
// base\telephony\core_service\services\core\src\core_service_stub.cpp
int32_t CoreServiceStub::OnRemoteRequest(
uint32_t code, MessageParcel &data, MessageParcel &reply, MessageOption &option)
{
TELEPHONY_LOGI("CoreServiceStub OnRemoteRequest code %{public}u", code);
std::u16string myDescripter = CoreServiceStub::GetDescriptor();
std::u16string remoteDescripter = ();
if (myDescripter != remoteDescripter) {
TELEPHONY_LOGE("descriptor checked fail");
return TELEPHONY_ERR_DESCRIPTOR_MISMATCH;
}
auto itFunc = memberFuncMap_.find(code);
if (itFunc != memberFuncMap_.end()) {
auto memberFunc = itFunc->second;
if (memberFunc != nullptr) {
return (this->*memberFunc)(data, reply);
}
}
return IPCObjectStub::OnRemoteRequest(code, data, reply, option);
}
- code:IPC接口类定义的接口定义码,标识服务端的能力
- data:表示proxy传递到服务端序列化之后的参数列表
- reply:表示此次proxy的IPC请求,服务端响应服务传回proxy的应答序列化参数列表
- option:表示此次IPC通信的通信方式,SYNC同步或ASYCN异步,默认为SYNC。同步表示客户端IPC跨进程调用会阻塞客户端,直至服务端处理完此次业务逻辑才返回,异步方式为客户端proxy发起IPC通信后即返回,不需要等待服务端响应。
- 构造时通过map映射接口定义code与stub的处理函数的对应
// base\telephony\core_service\services\core\src\core_service_stub.cpp
void CoreServiceStub::AddHandlerNetWorkToMap()
{
memberFuncMap_[uint32_t(InterfaceID::GET_PS_RADIO_TECH)] = &CoreServiceStub::OnGetPsRadioTech;
memberFuncMap_[uint32_t(InterfaceID::GET_CS_RADIO_TECH)] = &CoreServiceStub::OnGetCsRadioTech;
...
}
- 实现OnGetPsRadioTech等响应函数,对data进行反序列化得到入参列表,调用服务实现业务逻辑,将返回值序列化后返回结果
// base\telephony\core_service\services\core\src\core_service_stub.cpp
int32_t CoreServiceStub::OnGetPsRadioTech(MessageParcel &data, MessageParcel &reply)
{
auto slotId = data.ReadInt32();
int32_t radioTech = 0;
int32_t result = GetPsRadioTech(slotId, radioTech);
reply.WriteInt32(result);
if (result == TELEPHONY_ERR_SUCCESS) {
reply.WriteInt32(radioTech);
}
return result;
}
1.2.4 SA服务的实现类
// base\telephony\core_service\services\core\include\core_service.h
class CoreService : public SystemAbility, public CoreServiceStub {
DECLARE_DELAYED_SINGLETON(CoreService)
DECLARE_SYSTEM_ABILITY(CoreService)
public:
void OnStart() override;
void OnStop() override;
int32_t GetPsRadioTech(int32_t slotId, int32_t &psRadioTech) override;
int32_t GetCsRadioTech(int32_t slotId, int32_t &csRadioTech) override;
...
}
-
服务类需继承safwk的SystemAbility以及1.2.3提及的服务端stub类CoreServiceStub
由于CoreServiceStub继承自ICoreService,但是只是实现了OnRemoteRequest的虚函数,像GetPsRadioTech、GetCsRadioTech等虚函数未实现,CoreServiceStub仍然只是抽象类,不能实例化对象。因此实例化的操作就交由CoreService来完成,即由CoreService实现GetPsRadioTech、GetCsRadioTech等虚函数。
// base\telephony\core_service\services\core\src\core_service.cpp
int32_t CoreService::GetPsRadioTech(int32_t slotId, int32_t &psRadioTech)
{
...
}
int32_t CoreService::GetCsRadioTech(int32_t slotId, int32_t &csRadioTech)
{
...
}
- 需要调用safwk提供的宏REGISTER_SYSTEM_ABILITY_BY_ID对该SA进行注册。
REGISTER_SYSTEM_ABILITY_BY_ID(CoreService, TELEPHONY_CORE_SERVICE_SYS_ABILITY_ID, true);
此宏定义如下:
// foundation\systemabilitymgr\safwk\services\safwk\include\system_ability.h
#define REGISTER_SYSTEM_ABILITY_BY_ID(abilityClassName, systemAbilityId, runOnCreate) \
const bool abilityClassName##_##RegisterResult = \
SystemAbility::MakeAndRegisterAbility(new abilityClassName(systemAbilityId, runOnCreate));
故在实例中,CoreService采用直接使用MakeAndRegisterAbility的方式
// base\telephony\core_service\services\core\src\core_service.cpp
SystemAbility::MakeAndRegisterAbility(DelayedSingleton<CoreService>::GetInstance().get());
- 在REGISTER_SYSTEM_ABILITY_BY_ID中,第一个参数代表ability的类名,第二个参数为当前SA的serviceId,此serviceId由子系统进行分配,每个子系统大约有100个左右的serviceId,一般定义在foundation\systemabilitymgr\samgr\interfaces\innerkits\samgr_proxy\include\system_ability_definition.h中。本例中
// foundation\systemabilitymgr\samgr\interfaces\innerkits\samgr_proxy\include\system_ability_definition.h
TELEPHONY_CORE_SERVICE_SYS_ABILITY_ID = 4010,
第三个参数runOnCreate,该参数标识SA是否随进程而启动。true表示进程启动时拉起该服务。一般设置为true,设为常驻服务。false表示按需启动,访问到该SA时才会被拉起。该注册仅仅为向sa_main进程注册,即需要向samgr注册请求的SA,调用Publish才真正完成samgr的注册。
-
实现基类SystemAbility的OnStart,此函数由sa_main进程统一调用,需要调用SystemAbility基类提供的Publish方法实现SA向samgr注册
// base\telephony\core_service\services\core\src\core_service.cpp void CoreService::OnStart() { ... if (!registerToService_) { bool ret = Publish(DelayedSingleton<CoreService>::GetInstance().get()); if (!ret) { TELEPHONY_LOGE("CoreService::Init Publish failed!"); return; } registerToService_ = true; } ... }
至此,SA代码部份介绍完成。
1.2.5 基它
ServiceId配置策略
-
每个子系统可配置的Id为100个,理论上足够了。
-
新加子系统SA配置,必须以如下开头和结尾。
SUBSYS_子系统名_SYS_ABILITY_ID_BEGIN SUBSYS_子系统名_SYS_ABILITY_ID_END
-
新加的ServiceId不能出现重复,否则会出现Service分布式访问不通情况。