鸿蒙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分布式访问不通情况。

猜你喜欢

转载自blog.csdn.net/procedurecode/article/details/130222081