> 文档中心 > 鸿蒙Service Ability的前世今生--进阶篇

鸿蒙Service Ability的前世今生--进阶篇


二、SA的配置

​ SA的运行需要配合多个配置项,此节专门对此进行说明。

​ OpenHarmony中SA一般由两个配置文件和一个so组成。上一章节已介绍了生成so中代码。此节描述下另外两个配置文件(.cfg或.rc、xml)。

​ SA的启动一般采用.cfg或.rc + .xml + libxxx.z.so方式由OpenHarmony的init进程执行对应的xxx.cfg文件拉起相关的SA进程的方式。

2.1 .xml

接上文CoreService的服务生成的llibtel_core_service.z.so.

此SA的serviceId为

// foundation\systemabilitymgr\samgr\interfaces\innerkits\samgr_proxy\include\system_ability_definition.hTELEPHONY_CORE_SERVICE_SYS_ABILITY_ID     = 4010,

一般会在此SA所在的子系统下,创建sa_profile目录,添加4010.xml文件与BUILD.gn。4010.xml与BUILD.gn内容所示如下

2.1.1 4001.xml

// base\telephony\core_service\sa_profile\4010.xml<info>    <process>telephony</process>    <systemability>  <name>4010</name> <libpath>libtel_core_service.z.so</libpath> <run-on-create>true</run-on-create> <distributed>false</distributed> <dump-level>1</dump-level>    </systemability></info>
  • 进程名字即该SA要运行的进程空间,此字段为必填字段。
  • 一个xml文件只能一个节点,配置多个会导致编译失败。
  • 的name为对应的serviceId。必填项
  • 为SA的加载路径。必填项
  • : true表示进程启启动后即向samgr注册该SA,false表示按需启动,即在其它模块访问该SA时启动。必填项
  • :true表示该SA为分布式SA,false表示只有本地跨IPC访问。
  • :可不设置,可以设置三种:BootStartPhase、CoreStartPhase、OtherStartPhase(默认类型),三者优先级依次降低,当同一进程中,会优先拉起注册配置BootStartPhase的SA,然后是配置CoreStartPhase的SA,最后是OtherStartPhase。当高优先级的SA全部启动注册完成后才会启动下一级的SA的注册。
  • :表示hidumper支持的level等级,默认配置1就可以

2.1.2 BUILD.gn

# base\telephony\core_service\sa_profile\BUILD.gnimport("//build/ohos/sa_profile/sa_profile.gni")ohos_sa_profile("core_service_sa_profile") {  sources = [ "4010.xml" ]  part_name = "core_service"}
  • sources:表示当前子系统需要配置的SA的xml文件列表。可支持多个SA
  • subsystem_name:为当前子系统
  • part_name:为当前子系统所属的子模块

最后将该BUILD.gn添加至该子系统的bundle.json中参与编译。

// base\telephony\core_service\bundle.json"//base/telephony/core_service:tel_core_service","//base/telephony/core_service/sa_profile:core_service_sa_profile",

编译完成后,out路径会生成一个以进程名为前缀的xml文件telephony.xml。

2.2 .cfg或.rc

.cfg配置文件主要应用在L2设备上,L3-L5采用.rc配置实现,两者区别不大。本文主要对L2的cfg进行介绍。

// base\telephony\core_service\services\etc\init\telephony.cfg{    "jobs" : [{     "name" : "early-boot",     "cmds" : [  "mkdir /data/service/el1/public/telephony 0711 radio radio",  "start telephony_sa"  ] }    ],    "services" : [{     "name" : "telephony_sa",     "path" : ["/system/bin/sa_main", "/system/profile/telephony.xml"],     "uid" : "radio",     "gid" : ["radio", "shell"],     "permission" : [  "ohos.permission.COMMONEVENT_STICKY",  "ohos.permission.CONNECTIVITY_INTERNAL",  "ohos.permission.GET_TELEPHONY_STATE",  "ohos.permission.PERMISSION_USED_STATS",  "ohos.permission.RECEIVE_SMS",  "ohos.permission.SET_TELEPHONY_STATE",  "ohos.permission.MANAGE_SECURE_SETTINGS"     ],     "secon" : "u:r:telephony_sa:s0" }    ]}

cfg配置文件为OpenHarmony提供的native进程拉起策略,为设备在开机启动阶段由init进程解析配置的cfg文件进行拉起。

  • jobs配置项为key对应的action类型,即在标识为name的事件满足后,触发cmds类型配置的actions,具体的配置参数可查看OpenHarmony的启动子系统(base/startup/init)。
  • services为init拉起(fork)进程执行实体代码,path为sa_main可执行程序以及对应的参数。该xml为sa_main进程启动后需要解析的配置xml文件。
  • uid、gid、permission、secon涉及用户及selinux等权限管理,以后有时间会单独开一篇进行说明。

配置完成cfg,需要添加到BUILD.gn中参与编译

// base\telephony\core_service\services\etc\init\BUILD.gnimport("//build/ohos.gni")## Install telephony.cfg to /system/etc/init/telephony.cfgohos_prebuilt_etc("telephony.cfg") {  source = "telephony.cfg"  relative_install_dir = "init"  part_name = "core_service"  subsystem_name = "telephony"}
// base\telephony\core_service\bundle.json"//base/telephony/core_service/services/etc/init:telephony.cfg",
  • 如果需要进行调试的话,可以直接将telephony.cfg文件push至设备/system/etc/init目录,telephony.xml文件push至设备/system/profile目录下。然后重启手机,通过ps -ef | grep telephony查看此进程是否启动。
  • 进程正常启动后,通过hdc shell hidumper -ls过滤said为1410,SA是否已经注册成功。(如不支持hidumper命令,可以自行去编译生成)

三、SA中生命周期

在这里插入图片描述
上图为Ability的生命周期与状态转换图。本例中CoreService继承SystemAbility。SystemAbility是运行在后台的服务,没有前端与后台之分,参照SystemAbility类中代码所得,SA的生命周期为OnStart,OnStop与OnDump。

至此,在实现SA时需要在服务类(CoreService)中实现OnStart,OnStop与OnDump此三个方法完成SA启动结束与异常的处理。

四、SA的回调

如何在SA中注册一个回调函数,当SA的服务完成某些功能后执行注册的回调函数。

由于SA是跨进程IPC通信,所以SA的接口不能简单的set一个函数指针,从而回调此函数指针。此时只需要实现一个回调的SA类,将此回调的SA的类set进入SA内,此时SA就可以通过IPC的方式回调注册进SA的回调函数。

回调的SA的实现要素同SA的实现类似。

  • 定义对外的IPC接口类

  • 定义客户端通信代理proxy类

  • 定义服务端通信stub类

  • SA服务的实现类

同样以CoreService添加回调接口为例,在CoreService中有一个接口

// base\telephony\core_service\services\core\include\core_service.hint32_t GetNetworkSearchInformation(int32_t slotId, const sptr &callback) override;

此接口中传入的INetworkSearchCallback即为第一项对外的IPC接口类

4.1 回调的IPC接口类

// base\telephony\core_service\interfaces\innerkits\include\i_network_search_callback.hclass INetworkSearchCallback : public IRemoteBroker {public:    virtual ~INetworkSearchCallback() = default;    enum class NetworkSearchCallback { GET_AVAILABLE_RESULT = 0, GET_NETWORK_MODE_RESULT, SET_NETWORK_MODE_RESULT, GET_RADIO_STATUS_RESULT, SET_RADIO_STATUS_RESULT, GET_PREFERRED_NETWORK_MODE_RESULT, SET_PREFERRED_NETWORK_MODE_RESULT,    };    virtual int32_t OnNetworkSearchCallback(NetworkSearchCallback requestId, MessageParcel &data) = 0;public:    DECLARE_INTERFACE_DESCRIPTOR(u"OHOS.Telephony.INetworkSearchCallback");};

4.2 回调的代理proxy类

// base\telephony\core_service\services\network_search\include\network_search_callback_proxy.hclass NetworkSearchCallBackProxy : public IRemoteProxy {public:    explicit NetworkSearchCallBackProxy(const sptr &impl);    virtual ~NetworkSearchCallBackProxy() = default;    int32_t OnNetworkSearchCallback( NetworkSearchCallback requestId, MessageParcel &callBackParcel) override;private:    static inline BrokerDelegator delegator_;};

4.3 回调的stub类

// base\telephony\core_service\interfaces\innerkits\include\i_network_search_callback_stub.hclass INetworkSearchCallbackStub : public IRemoteStub {public:    static const int32_t DEFAULT_ERROR = -1;    static const int32_t DEFAULT_RESULT = 0;    INetworkSearchCallbackStub() = default;    virtual ~INetworkSearchCallbackStub() = default;    int32_t OnNetworkSearchCallback(NetworkSearchCallback requestId, MessageParcel &data) override;    int OnRemoteRequest( uint32_t code, MessageParcel &data, MessageParcel &reply, MessageOption &option) override final;    virtual void OnSetNetworkModeCallback(const bool setResult, const int32_t errorCode);    virtual void OnGetNetworkModeCallback(const int32_t searchModel, const int32_t errorCode);    virtual void OnSetRadioStateCallback(const bool setResult, const int32_t errorCode);    virtual void OnGetRadioStateCallback(const bool setResult, const int32_t errorCode);    virtual void OnGetNetworkSearchInformation( const sptr &networkSearchResult, const int32_t errorCode);    virtual void OnSetPreferredNetworkCallback(const bool result, const int32_t errorCode);    virtual void OnGetPreferredNetworkCallback(const int32_t networkMode, const int32_t errorCode);private:    void OnSetNetworkModeCallback(MessageParcel &data);    void OnGetNetworkModeCallback(MessageParcel &data);    void OnSetRadioStateCallback(MessageParcel &data);    void OnGetRadioStateCallback(MessageParcel &data);    void OnGetNetworkSearchInformation(MessageParcel &data);    void OnSetPreferredNetworkCallback(MessageParcel &data);    void OnGetPreferredNetworkCallback(MessageParcel &data);};

4.4 回调服务的实现类

// base\telephony\core_service\frameworks\js\network_search\include\get_network_search_info_callback.hclass GetNetworkSearchInfoCallback : public INetworkSearchCallbackStub {public:    explicit GetNetworkSearchInfoCallback(GetSearchInfoContext *context);    void OnGetNetworkSearchInformation( const sptr &networkSearchResult, const int32_t errorCode) override;private:    GetSearchInfoContext *asyncContext_;};
// base\telephony\core_service\frameworks\js\network_search\include\get_preferred_network_callback.hclass GetPreferredNetworkCallback : public INetworkSearchCallbackStub {public:    explicit GetPreferredNetworkCallback(PreferredNetworkModeContext *asyncContext);    void OnGetPreferredNetworkCallback(const int32_t networkMode, const int32_t errorCode) override;private:    PreferredNetworkModeContext *asyncContext_;};

4.5 SA的回调注册

// base\telephony\core_service\frameworks\js\network_search\src\napi_radio.cppstatic void NativeGetNetworkSearchInformation(napi_env env, void *data){    auto asyncContext = static_cast(data);    if (!IsValidSlotId(asyncContext->slotId)) { TELEPHONY_LOGE("NativeGetNetworkSearchInformation slotId is invalid"); asyncContext->errorCode = ERROR_SLOT_ID_INVALID; return;    }    std::unique_ptr callback = std::make_unique(asyncContext);    std::unique_lock callbackLock(asyncContext->callbackMutex);    asyncContext->errorCode = DelayedRefSingleton::GetInstance().GetNetworkSearchInformation( asyncContext->slotId, callback.release());    if (asyncContext->errorCode == TELEPHONY_SUCCESS) { asyncContext->cv.wait_for(     callbackLock, std::chrono::seconds(WAIT_TIME_SECOND), [asyncContext] { return asyncContext->callbackEnd; }); TELEPHONY_LOGI("NativeGetNetworkSearchInformation after callback end");    }    TELEPHONY_LOGI("NativeGetNetworkSearchInformation end");}使用方式就比较简单直接 std::make_unique(asyncContext);然后通过CoreService的SA的接口GetNetworkSearchInformation注册回调的SA类GetNetworkSearchInfoCallback进CoreService的SA。到此SA实现了回调方式的IPC调用。