《OpenHarmony 3GPP协议开发深度剖析》之--PLMN业务源码解读 原创 精华

开源夏德旺
发布于 2022-3-23 16:39
浏览
7收藏

接续上一篇<<搜网流程之PLMN选择>>
搜网流程可以简单概括为PLMN选择、小区搜索、网络注册,而PLMN选择主要在cp侧完成,而PLMN在OpenHarmony源码中(即AP侧)主要涉及到的业务就是搜网模块中的运营商相关信息获取的业务,比如我们常见的手机状态栏上的运营商名称显示。

下面来针对AP侧中搜网相关的PLMN业务解读下源码流程。

Ril架构回顾

在进行代码解读之前,还是对电话子系统的架构再讲解下,如下图

《OpenHarmony 3GPP协议开发深度剖析》之--PLMN业务源码解读-鸿蒙开发者社区

modem厂商针对OpenHarmony定制自己的lib库,lib库里面主要就是一些AT指令操作,在hril层会根据当前设备的modem指定加载modem对应的厂商库,从而屏蔽直接与modem打交道。而hril层通过HDF驱动框架与tel_ril层进行通信,tel_ril层以上就是AP侧的具体业务逻辑处理了,再往上走就是tel framework层了,该层和tel_ril层主要通过proxy-stub架构进行通信,再framework层就可以定义一些上层api提供给上层应用调用,比如说定义一个api为getOperatorName提供给上层app获取运营商的名称,然后在systemui中调用该api就可以获取到运营商名称并且更新手机状态栏上的运营商名称显示,比如显示“中国移动”。

1.完成Modem初始化,并创建监听modem业务事件监听。

首先从modem侧出发,modem会对电话子系统相关业务事件进行主动上报。

在ril的驱动初始化的时候会加载modem厂商库,见hri_hdf.c中的代码,如下:

struct HdfDriverEntry g_rilAdapterDevEntry = {
    .moduleVersion = 1,
    .moduleName = "hril_hdf",
    .Bind = RilAdapterBind,
    .Init = RilAdapterInit,
    .Release = RilAdapterRelease,
};
HDF_INIT(g_rilAdapterDevEntry);

static int32_t RilAdapterInit(struct HdfDeviceObject *device)
{
	......
    LoadVendor();//加载厂商库
    return HDF_SUCCESS;
}

static void LoadVendor(void)
{
	......
    rilInitOps = (const HRilOps *(*)(const struct HRilReport *))dlsym(g_dlHandle, "RilInitOps");
    if (rilInitOps == NULL) {
        dlclose(g_dlHandle);
        TELEPHONY_LOGE("RilInit not defined or exported");
        return;
    }
    ops = rilInitOps(&g_reportOps);	//初始化modem厂商库
    HRilRegOps(ops);
    TELEPHONY_LOGI("HRilRegOps completed");
}

在LoadVendor中调用了RilInitOps接口,modem厂商库运行的入口在vendor_adapter.c文件中的const HRilOps *RilInitOps(const struct HRilReport * reportOps)接口,代码如下:

const HRilOps *RilInitOps(const struct HRilReport *reportOps)
{
    pthread_attr_t attr;
    SetReportOps(reportOps);
    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
    int32_t ret = pthread_create(&g_eventListeners, &attr, (void *(*)(void *))EventListeners, NULL);	//创建g_eventListeners线程监听modem上报的业务事件
    if (ret < 0) {
        TELEPHONY_LOGE("EventListeners create failed %d \n", ret);
    }
    if (g_hrilOps.smsOps == NULL) {
        TELEPHONY_LOGE("g_hrilOps.smsOps is null");
    }
    TELEPHONY_LOGI("g_hrilOps.smsOps:%{public}p", g_hrilOps.smsOps);
    return &g_hrilOps;
}

在创建g_eventListeners线程传入的EventListeners里面有如下核心代码:

... 
if (devicePath != NULL) {
                g_fd = open(devicePath, O_RDWR);// 打开设备节点,入参g_devicePath是Modem设备节点
 }
...
int32_t ret = ATStartReadLoop(g_fd, OnNotifyOps);//启动循环,读取处理Modem上报的消息。
ModemInit();//modem初始化

ATStartReadLoop的代码如下:

int32_t ATStartReadLoop(int32_t fd, OnNotify func)
{
    int32_t ret = 0;
    g_atFd = fd;
    g_onNotifyFunc = func;
    pthread_attr_t t;
    pthread_attr_init(&t);
    pthread_attr_setdetachstate(&t, PTHREAD_CREATE_DETACHED);
    ret = pthread_create(&g_reader, &t, (void *(*)(void *))ReaderLoop, &t);//创建g_reader线程循环读取处理Modem上报的消息。
    if (ret < 0) {
        TELEPHONY_LOGE("create pthread error code: %{public}d", ret);
        return VENDOR_ERR_PROCESS;
    }
    return VENDOR_SUCCESS;
}

ModemInit函数的代码如下:

static int32_t ModemInit(void)
{
    ResponseInfo *pResponse = NULL;
    int32_t err = SetRadioState(HRIL_RADIO_POWER_STATE_ON, 0);
    if (err == -1) {
        TELEPHONY_LOGE("RadioState set failed");
    }

    err = SendCommandLock("ATE0Q0V1", NULL, 0, &pResponse);
    if (err != 0 || !pResponse->success) {
        TELEPHONY_LOGE("ATE0Q0V1 send failed");
    }
    FreeResponseInfo(pResponse);
    /* Network registration events */
    err = SendCommandLock("AT+CREG=2", NULL, 0, &pResponse);
    if (err != 0 || !pResponse->success) {
        SendCommandLock("AT+CREG=2", NULL, 0, &pResponse);
    }
    FreeResponseInfo(pResponse);

    /* GPRS registration events */
    err = SendCommandLock("AT+CGREG=2", NULL, 0, &pResponse);
    if (err != 0 || !pResponse->success) {
        SendCommandLock("AT+CGREG=2", NULL, 0, &pResponse);
    }
    FreeResponseInfo(pResponse);
    /* Enable the extended format of incoming calls */
    SendCommandLock("AT+CRC=1", NULL, 0, NULL);
    /* Set the SMS service type to Phase 2+ version */
    SendCommandLock("AT+CSMS=1", NULL, 0, NULL);
    /* Set the new SMS reporting method to +CMTI */
    SendCommandLock("AT+CNMI=1,2,0,1,1", NULL, 0, NULL);
    /* Enable active reporting of (U)SIM status */
    SendCommandLock("AT^SIMST=1", NULL, 0, NULL);
    /* Disabled  auto-answer */
    SendCommandLock("ATS0=0", NULL, 0, NULL);
    /* Extended errors */
    SendCommandLock("AT+CMEE=1", NULL, 0, NULL);
    /* Set to signal  reporting */
    SendCommandLock("AT^HCSQ=3,10", NULL, 0, NULL);
    SendCommandLock("AT^CURCEX=2,F7FFFFFFFFFFFF", NULL, 0, NULL);
    /* IMS registration events */
    SendCommandLock("AT+CIREG=2", NULL, 0, NULL);
    /*  Call Waiting notifications */
    SendCommandLock("AT+CCWA=1", NULL, 0, NULL);
    /* Disabled muted */
    SendCommandLock("AT+CMUT=0", NULL, 0, NULL);
    /* Enabled CSSU unsolicited supp service notifications */
    SendCommandLock("AT+CSSN=0,1", NULL, 0, NULL);
    /* Set SMS PDU mode */
    SendCommandLock("AT+CMGF=0", NULL, 0, NULL);
    /* Set UNICODE character */
    SendCommandLock("AT+CSCS=\"IRA\"", NULL, 0, NULL);
    /* Set sms memory */
    SendCommandLock("AT+CPMS=\"SM\",\"SM\",\"ME\"", NULL, 0, NULL);
    /* Set to open network time reporting */
    SendCommandLock("AT^TIME=1", NULL, 0, NULL);
    /* Set to open network time zone reporting */
    SendCommandLock("AT+CTZR=1", NULL, 0, NULL);
    /* Enabled SRVCC status to report actively: This command complies with the 3GPP TS 27.007 protocol. */
    SendCommandLock("AT+CIREP=1", NULL, 0, NULL);

    sleep(SLEEP_TIME);
    TELEPHONY_LOGI("enter to : ModemInit OnModemReport %{public}d", g_radioState);
    struct ReportInfo reportInfo = {0};
    reportInfo.notifyId = HNOTI_MODEM_RADIO_STATE_UPDATED;
    reportInfo.type = HRIL_NOTIFICATION;
    reportInfo.error = HRIL_ERR_SUCCESS;
    OnModemReport(GetSlotId(NULL), reportInfo, (const uint8_t *)&g_radioState, sizeof(HRilRadioState));
    return err;
}

从代码可以看出,这里初始化就是发出一些列AT指令给modem。

2.modem上报业务事件的回调函数

其中请求参数reportOps为RIL Adapter传入的事件回调函数指针,这个函数回调指针在hri_hdc.c中进行定义,如下:

// 定义Modem厂商库回调函数指针
static struct HRilReport g_reportOps = {
    OnCallReport,    // 通话相关业务回调函数
    OnDataReport,    // 蜂窝数据相关业务回调函数
    OnModemReport,   // Modem相关业务回调函数
    OnNetworkReport, // 搜网相关业务回调函数
    OnSimReport,     // SIM卡相关业务回调函数
    OnSmsReport      // 短信相关业务回调函数
};

在hril.h中定义了结构体HRilReport,代码如下:

struct HRilReport {
    void (*OnCallReport)(int32_t slotId, struct ReportInfo reportInfo, const uint8_t *data, size_t dataLen);
    void (*OnDataReport)(int32_t slotId, struct ReportInfo reportInfo, const uint8_t *data, size_t dataLen);
    void (*OnModemReport)(int32_t slotId, struct ReportInfo reportInfo, const uint8_t *data, size_t dataLen);
    void (*OnNetworkReport)(int32_t slotId, struct ReportInfo reportInfo, const uint8_t *data, size_t dataLen);
    void (*OnSimReport)(int32_t slotId, struct ReportInfo reportInfo, const uint8_t *data, size_t dataLen);
    void (*OnSmsReport)(int32_t slotId, struct ReportInfo reportInfo, const uint8_t *data, size_t dataLen);
    void (*OnTimerCallback)(HRilCallbackFun func, uint8_t *param, const struct timeval *tv);
};

其中OnNetworkReport就是我们要研究的搜网相关业务,而PLMN业务就包含在其中。

在Modem设备节点读取线程g_reader里调用OnNotifyOps()解析具体的Modem上报事件,判断AT命令类型,并调用OnXxxReport()把解析得到的各模块事件上报给hril业务层。

在vendor_report.c中的OnNotifyOps接口代码如下:

void OnNotifyOps(const char *s, const char *smsPdu)
{
    char *str = NULL;
    struct ReportInfo reportInfo = {0};
    ReportInfoInit(&reportInfo);
    if (GetRadioState() == HRIL_RADIO_POWER_STATE_UNAVAILABLE) {
        return;
    }
    str = strdup(s);
    if (IsCallNoticeCmd(s)) {
        CallReportInfoProcess(s);
    } else if (ReportStrWith(s, "+CMT:")) {
        HRilSmsResponse smsResponse = {};
        smsResponse.pdu = (char *)smsPdu;
        reportInfo.notifyId = HNOTI_SMS_NEW_SMS;
        OnSmsReport(GetSlotId(NULL), reportInfo, (const uint8_t *)&smsResponse, strlen(smsResponse.pdu));
    } else if (ReportStrWith(s, "+CDS:")) {
  ........
       OnNotifyNetWorksOps(s, str); //上报网络业务事件
}

继续分析OnNotifyNetWorksOps的代码,如下

 reportInfo.error = HRIL_ERR_SUCCESS;
    reportInfo.type = HRIL_NOTIFICATION;
    if (ReportStrWith(s, "+CREG:")) {
        OnCsRegStatusNotify(reportInfo, ret, str, s);
    } else if (ReportStrWith(s, "+CGREG:")) {
        OnPsRegStatusNotify(reportInfo, ret, str, s);
    } else if (ReportStrWith(s, "^TIME:")) {
        OnNotifyNetWorksOpsJudgeTwo(reportInfo, infoStr, responseData);
    } else if (ReportStrWith(s, "+CTZV:")) {

此时会发现里面也是对AT命令进行判断,比如这个OnCsRegStatusNotify就是CS域注册状态通知,继续看OnCsRegStatusNotify的代码,如下

static void OnCsRegStatusNotify(struct ReportInfo reportInfo, int32_t ret, char *str, const char *s)
{
    reportInfo.notifyId = HNOTI_NETWORK_CS_REG_STATUS_UPDATED;	//上报业务事件中的业务标识,这里表示CS域状态更新
    HRilRegStatusInfo regStatusInfo;
    ret = ProcessRegStatus(str, &regStatusInfo);
    if (ret == 0) {
        OnNetworkReport(GetSlotId(NULL), reportInfo, (const uint8_t *)(&regStatusInfo), sizeof(HRilRegStatusInfo)); //上报网络业务事件
    } else {
        TELEPHONY_LOGW("CREG notify str format  unexpected: %{public}s", s);
    }
}

从上面代码发现有一个上报事件的业务标识符HNOTI_NETWORK_CS_REG_STATUS_UPDATED。

3. hril层通过HDF框架与tel_rel层

继续去追踪这个标识符的代码,就可以定位到tel_ril层了。

在hril_network.cpp中有如下代码:

void HRilNetwork::AddHandlerToMap()
{
    // indication
    notiMemberFuncMap_[HNOTI_NETWORK_CS_REG_STATUS_UPDATED] = &HRilNetwork::NetworkCsRegStatusUpdated;
    notiMemberFuncMap_[HNOTI_NETWORK_SIGNAL_STRENGTH_UPDATED] = &HRilNetwork::SignalStrengthUpdated;
    notiMemberFuncMap_[HNOTI_NETWORK_TIME_UPDATED] = &HRilNetwork::NetworkTimeUpdated;
    notiMemberFuncMap_[HNOTI_NETWORK_TIME_ZONE_UPDATED] = &HRilNetwork::NetworkTimeZoneUpdated;
    ......

这里将HNOTI_NETWORK_CS_REG_STATUS_UPDATED的回调交由HRilNetwork::NetworkCsRegStatusUpdated来处理,下面继续追此代码,如下

int32_t HRilNetwork::NetworkCsRegStatusUpdated(
    int32_t indType, const HRilErrNumber e, const void *response, size_t responseLen)
{
    ......
    indType = static_cast<int32_t>(ConvertIntToRadioNoticeType(indType));
    if (!HdfSbufWriteInt32(dataSbuf, indType)) {
        TELEPHONY_LOGE("HdfSbufWriteInt32 in NetworkCsRegStatusUpdated is failed!");
        HdfSbufRecycle(dataSbuf);
        return HRIL_ERR_GENERIC_FAILURE;
    }
    int32_t ret = ServiceNotifyDispatcher(HNOTI_NETWORK_CS_REG_STATUS_UPDATED, dataSbuf);
    if (ret != HRIL_ERR_SUCCESS) {
        TELEPHONY_LOGE("ret is not equal to HRIL_ERR_SUCCESS!");
        HdfSbufRecycle(dataSbuf);
        return HRIL_ERR_GENERIC_FAILURE;
    }
    if (dataSbuf != nullptr) {
        HdfSbufRecycle(dataSbuf);
    }
    return HRIL_ERR_SUCCESS;
}

从上面代码,可以看出通过HDF驱动发送HNOTI_NETWORK_CS_REG_STATUS_UPDATED消息给tel_ril层,下面就可以看tel_rel层与hril_network.cpp所对应的文件tel_ril_network.cpp的代码,有如下关键代码:

void TelRilNetwork::AddHandlerToMap()
{
    // indication
    memberFuncMap_[HNOTI_NETWORK_CS_REG_STATUS_UPDATED] = &TelRilNetwork::NetworkCsRegStatusUpdated;
    memberFuncMap_[HNOTI_NETWORK_SIGNAL_STRENGTH_UPDATED] = &TelRilNetwork::SignalStrengthUpdated;
    memberFuncMap_[HNOTI_NETWORK_TIME_UPDATED] = &TelRilNetwork::NetworkTimeUpdated;
   .....
}

这样,上报的cs域状态更新的业务就由NetworkCsRegStatusUpdated函数来处理。

4. 通过观察者模式监听RadioEvent,NetworkSearchHandler进行搜网业务处理

继续阅读上面提到的NetworkCsRegStatusUpdated的代码,如下:

int32_t TelRilNetwork::NetworkCsRegStatusUpdated(MessageParcel &data)
{
    std::shared_ptr<CsRegStatusInfo> regStatusInfo = std::make_shared<CsRegStatusInfo>();
    if (regStatusInfo == nullptr) {
        TELEPHONY_LOGE("regStatusInfo == nullptr");
        return TELEPHONY_ERR_LOCAL_PTR_NULL;
    }
    regStatusInfo->ReadFromParcel(data);
    int32_t flagType = data.ReadInt32();
    if (observerHandler_ == nullptr) {
        TELEPHONY_LOGE("observerHandler_ == nullptr");
        return TELEPHONY_ERR_LOCAL_PTR_NULL;
    } else {
        TELEPHONY_LOGI("TelRilNetwork::NetworkCsRegStatusUpdated indicationType:%{public}d", flagType);
        observerHandler_->NotifyObserver(RadioEvent::RADIO_NETWORK_STATE, regStatusInfo);
        return TELEPHONY_ERR_SUCCESS;
    }
}

关键代码observerHandler_->NotifyObserver(RadioEvent::RADIO_NETWORK_STATE, regStatusInfo);

这里会使用观察者模式,监听RadioEvent::RADIO_NETWORK_STATE事件。

而对该事件的观察者就是NetworkSearchHandler,下面追踪NetworkSearchHandler的代码,在network_search_handler.cpp中有如下代码

{RadioEvent::RADIO_NETWORK_STATE, &NetworkSearchHandler::GetNetworkStateInfo},

监听到RadioEvent::RADIO_NETWORK_STATE事件,会由GetNetworkStateInfo回调处理,继续追踪GetNetworkStateInfo代码,有如下关键代码:

        case CORE_SERVICE_POWER_OFF:
            RadioOffState();
            break;
        case CORE_SERVICE_POWER_ON:
            RadioOnState();
            break;

其中RadioOnState函数里面会调用如下代码:

 GetRilOperatorInfo(false);  //获取运营商信息
 GetRilPsRegistration(false);
 GetRilCsRegistration(false);

最终继续去追踪GetRilOperatorInfo这个函数,就会发现可以读取到RIL侧上报过来的PLMN数据,这里就不继续追了。

最后附上上述流程的时序图。

四、业务流程时序图

《OpenHarmony 3GPP协议开发深度剖析》之--PLMN业务源码解读-鸿蒙开发者社区

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
8
收藏 7
回复
举报
4条回复
按时间正序
/
按时间倒序
mb609898e2cfb86
mb609898e2cfb86

赞,跟着夏老师学源码

回复
2022-3-23 17:18:25
软通动力HOS
软通动力HOS

跟着夏老师学技术

回复
2022-5-7 09:00:23
软通田可辉
软通田可辉

夏老师加油

回复
2022-5-7 09:03:05
芒果爱学习
芒果爱学习

感谢夏老师分享

回复
2022-5-7 14:11:14
回复
    相关推荐