鸿蒙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.h
TELEPHONY_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.gn
import("//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中参与编译

扫描二维码关注公众号,回复: 16606151 查看本文章
// base\telephony\core_service\services\etc\init\BUILD.gn
import("//build/ohos.gni")

## Install telephony.cfg to /system/etc/init/telephony.cfg
ohos_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.h
int32_t GetNetworkSearchInformation(int32_t slotId, const sptr<INetworkSearchCallback> &callback) override;

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

4.1 回调的IPC接口类

// base\telephony\core_service\interfaces\innerkits\include\i_network_search_callback.h
class 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.h
class NetworkSearchCallBackProxy : public IRemoteProxy<INetworkSearchCallback> {
public:
    explicit NetworkSearchCallBackProxy(const sptr<IRemoteObject> &impl);
    virtual ~NetworkSearchCallBackProxy() = default;
    int32_t OnNetworkSearchCallback(
        NetworkSearchCallback requestId, MessageParcel &callBackParcel) override;

private:
    static inline BrokerDelegator<NetworkSearchCallBackProxy> delegator_;
};

4.3 回调的stub类

// base\telephony\core_service\interfaces\innerkits\include\i_network_search_callback_stub.h
class INetworkSearchCallbackStub : public IRemoteStub<INetworkSearchCallback> {
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> &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.h
class GetNetworkSearchInfoCallback : public INetworkSearchCallbackStub {
public:
    explicit GetNetworkSearchInfoCallback(GetSearchInfoContext *context);
    void OnGetNetworkSearchInformation(
        const sptr<NetworkSearchResult> &networkSearchResult, const int32_t errorCode) override;

private:
    GetSearchInfoContext *asyncContext_;
};
// base\telephony\core_service\frameworks\js\network_search\include\get_preferred_network_callback.h
class 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.cpp
static void NativeGetNetworkSearchInformation(napi_env env, void *data)
{
    auto asyncContext = static_cast<GetSearchInfoContext *>(data);
    if (!IsValidSlotId(asyncContext->slotId)) {
        TELEPHONY_LOGE("NativeGetNetworkSearchInformation slotId is invalid");
        asyncContext->errorCode = ERROR_SLOT_ID_INVALID;
        return;
    }
    std::unique_ptr<GetNetworkSearchInfoCallback> callback =
        std::make_unique<GetNetworkSearchInfoCallback>(asyncContext);
    std::unique_lock<std::mutex> callbackLock(asyncContext->callbackMutex);
    asyncContext->errorCode = DelayedRefSingleton<CoreServiceClient>::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<GetNetworkSearchInfoCallback>(asyncContext);

然后通过CoreService的SA的接口GetNetworkSearchInformation注册回调的SA类GetNetworkSearchInfoCallback进CoreService的SA。到此SA实现了回调方式的IPC调用。

猜你喜欢

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