> 文章列表 > 鸿蒙Service Ability的前世今生--基础篇

鸿蒙Service Ability的前世今生--基础篇

鸿蒙Service Ability的前世今生--基础篇

一、初识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"ohos.telephony.ICoreService");
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 = data.ReadInterfaceToken();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分布式访问不通情况。