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

news2024/9/25 7:25:52

二、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中参与编译

// 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调用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/430629.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Python 编程必不可少的unittest测试框架

一段表面看起来平平无常的代码&#xff0c;很可能暗藏很多bug无法一眼看透&#xff0c;没有经过测试的代码是不可靠的代码。上一篇讲过pytest测试框架这次我们换个框架。 unittest 是一个单元测试框架&#xff0c;单元测试完成对一个模块、一个类或一个函数的运行结果进行检验…

SpringSecurity之注销与自动登录

前言 前面我们讲解了用户的登录以及用户授权的过程。今天我们看一下&#xff0c;系统中常用的注销与自动登录各功能&#xff01; 注销操作 注销操作&#xff0c;首先需要在我们的配置类中添加上我们的注销路径。主要配置详见下方标红处代码&#xff0c;标记了退出的路径以及…

java轻量级框架MiniDao的详解

MiniDao是一款基于Java语言开发的轻量级持久层框架&#xff0c;它的目标是简化数据库操作流程&#xff0c;提高开发效率&#xff0c;减少代码量。MiniDao采用简单的注解配置方式&#xff0c;可以很容易地与Spring等常用框架集成使用。 MiniDao的主要特点包括&#xff1a; 简单…

浙大数据结构网课第一周入门

题目详情 Given a sequence of K integers { N1​, N2​, ..., NK​ }. A continuous subsequence is defined to be { Ni​, Ni1​, ..., Nj​ } where 1≤i≤j≤K. The Maximum Subsequence is the continuous subsequence which has the largest sum of its elements. For …

RBF-UKF径向基神经网络结合无迹卡尔曼滤波估计锂离子电池SOC(附MATLAB代码)RBF神经网络训练部分

1.清空变量 close all clear,clc 2.导入数据用以RBF神经网络训练&#xff0c;一共14组&#xff0c;训练数据P&#xff08;第一列为电压值&#xff0c;第二列为SOC值&#xff0c;第三列为电流值。&#xff09;&#xff0c;并将所有数据存储在变量PP中&#xff0c;所有电压数据…

蓝桥杯:优秀的拆分

蓝桥杯&#xff1a;优秀的拆分https://www.lanqiao.cn/problems/801/learning/ 目录 题目描述 输入描述 输出描述 输入输出样例 输入 输出 输入 输出 题目分析&#xff08;位运算&#xff09; AC代码&#xff08;Java) 题目描述 一般来说&#xff0c;一个正整数可以拆…

【】:addService 和 getService

一次完整的 Binder IPC 通信过程通常是这样&#xff1a; 首先 Binder 驱动在内核空间创建一个数据接收缓存区&#xff1b; 接着在内核空间开辟一块内核缓存区&#xff0c;建立内核缓存区和内核中数据接收缓存区之间的映射关系&#xff0c;以及内核中数据接收缓存区和接收进程用…

无线传感器网络硬件设计简介

无线传感器网络硬件设计简介 无线传感器网络因其巨大的应用前景越来越受到学术界和工业界的广泛关注。本文介绍了无线传感器网络节点的体系结构&#xff0c;分析比较了国内外当前典型的硬件平台&#xff0c;重点讨论了目前无线传感器网络节点常用的处理器、射频芯片、电源和传…

孩子为什么不能玩抖音精彩回答,共勉

2 可是&#xff0c;为什么我的同学、哥哥姐姐…… 反正身边好多人都在玩&#xff1f; 我不知道你父母有没有告诉你这个道理&#xff1a; 你把时间花在哪儿&#xff0c; 你就会成为什么样的人。 他们爱玩&#xff0c;是因为两个字&#xff1a; 空虚。 想象一下&#xff…

02、Cadence使用记录之创建元器件---原理图和封装(OrCAD Capture CIS)

02、Cadence使用记录之创建元器件—器件原理图符号和封装&#xff08;OrCAD Capture CIS&#xff09; 参考的教程是B站的视频&#xff1a;allegro软件入门视频教程全集100讲 前置教程&#xff1a; ## 01、Cadence使用记录之新建工程与基础操作&#xff08;原理图绘制&#xf…

新闻文本分类任务:使用Transformer实现

❤️觉得内容不错的话&#xff0c;欢迎点赞收藏加关注&#x1f60a;&#x1f60a;&#x1f60a;&#xff0c;后续会继续输入更多优质内容❤️ &#x1f449;有问题欢迎大家加关注私戳或者评论&#xff08;包括但不限于NLP算法相关&#xff0c;linux学习相关&#xff0c;读研读博…

13.网络爬虫—多进程详讲(实战演示)

网络爬虫—多进程详讲 一进程的概念二创建多进程三进程池四线程池五多进程和多线程的区别六实战演示北京新发地线程池实战 前言&#xff1a; &#x1f3d8;️&#x1f3d8;️个人简介&#xff1a;以山河作礼。 &#x1f396;️&#x1f396;️:Python领域新星创作者&#xff0c…

Spark SQL实战(07)-Data Sources

1 概述 Spark SQL通过DataFrame接口支持对多种数据源进行操作。 DataFrame可使用关系型变换进行操作&#xff0c;也可用于创建临时视图。将DataFrame注册为临时视图可以让你对其数据运行SQL查询。 本节介绍使用Spark数据源加载和保存数据的一般方法&#xff0c;并进一步介绍…

node安装

一、下载nodejs的安装包&#xff1a; 下载地址&#xff1a;https://nodejs.org/zh-cn/download 根据自己电脑系统及位数选择&#xff0c;一般都选择windows64位.msi格式安装包 二、改变nodejs的下载依赖包路径 安装完nodejs后&#xff0c;也同时安装了npm&#xff0c; npm是…

半监督语义分割_paper reading part1

Assignment 要解决的问题思路方法结果自己的想法 01 A Survey on Semi-Supervised Semantic Segmentation University of Granada, 18071, Granada, Spain 2023.02出版 problem to solve ss先前的&#xff08;19年&#xff09;不适用先前的调研包含弱监督&#xff0c;ss不…

Docker Desktop使用PostgreSql配合PGAdmin的使用

在看此教程之前&#xff0c;请先下载安装Docker Desktop 安装成功可以查看版本 然后拉取postgresql的镜像&#xff1a;docker pull postgres:14.2 版本可以网上找一个版本&#xff0c;我的不是最新的 发现会报一个问题 no matching manifest for windows/amd64 10.0.19045 i…

小心,丢失的消息!RocketMQ投递策略帮你解决问题!博学谷狂野架构师

RocketMQ消息投递策略 作者: 博学谷狂野架构师GitHub&#xff1a;GitHub地址 &#xff08;有我精心准备的130本电子书PDF&#xff09;只分享干货、不吹水&#xff0c;让我们一起加油&#xff01;&#x1f604; 前言 RocketMQ的消息投递分分为两种&#xff1a;一种是生产者往MQ …

java中级面试题

1.假如有两个线程共同操作数据库&#xff0c;以乐观锁的角度考虑&#xff0c;怎么确保不会发生并发问题&#xff1f; PS&#xff1a;考点是CAS&#xff0c;比较并替换。CAS中有三个值&#xff0c;内存中的值&#xff0c;新值&#xff0c;旧值。 假如内存中的值是2000&#xf…

[C++]string类的模拟实现和相关函数的详解

目录string总体架构具体实现默认成员函数构造函数构造拷贝函数析构函数赋值重载[]相关操作函数c_str() && size()reserve() && resize()push_back() && append()find()inserterase() && clear其余操作符重载< 、 <、 >、 >、 !<…

【系统集成项目管理工程师】项目整体管理

&#x1f4a5;十大知识领域&#xff1a;项目整体管理 项目整体管理包括以下 6 个过程: 制定项目章程定项目管理计划指导与管理项目工作监控项目工作实施整体变更控制结束项目或阶段过程 一、制定项目章程 制定项目章程。编写一份正式文件的过程&#xff0c;这份文件就是项目章程…