> 文档中心 > 鸿蒙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.cppsptr 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(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.cppint32_t FaceAuthService::SetBufferProducer(sptr &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.hclass 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.hclass CoreServiceProxy : public IRemoteProxy {public:    explicit CoreServiceProxy(const sptr &impl) : IRemoteProxy(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 delegator_;};
  • 首先继承IRemoteProxy,如上所示
  • 定义私有静态内联成员BrokerDelegator delegator_;
  • 实现接口中的虚函数GetPsRadioTech、GetCsRadioTech,如下所示,通过MessageParcel对参数进行序列化操作,然后通过remote->SendRequest将接口对应的code码与对应的序列化参数传送至stub的实现类
// base\telephony\core_service\frameworks\native\src\core_service_proxy.cppint32_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.hclass CoreServiceStub : public IRemoteStub {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 memberFuncMap_;};
  • 服务端stub类继承IRemoteStub,实现虚函数OnRemoteRequest完成接口code码与对应处理函数的对应
// base\telephony\core_service\services\core\src\core_service_stub.cppint32_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.cppvoid 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.cppint32_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.hclass 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.cppint32_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.cppSystemAbility::MakeAndRegisterAbility(DelayedSingleton::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.hTELEPHONY_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.cppvoid CoreService::OnStart(){...    if (!registerToService_) { bool ret = Publish(DelayedSingleton::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_BEGINSUBSYS_子系统名_SYS_ABILITY_ID_END
  • 新加的ServiceId不能出现重复,否则会出现Service分布式访问不通情况。