黄沙百战穿金甲,不破楼兰终不还。这篇文章主要讲述《OpenHarmony 3GPP协议开发深度剖析》之--PLMN业务源码解读相关的知识,希望能为你提供帮助。
接续上一篇<
<
搜网流程之PLMN选择>
>
搜网流程可以简单概括为PLMN选择、小区搜索、网络注册,而PLMN选择主要在cp侧完成,而PLMN在OpenHarmony源码中(即AP侧)主要涉及到的业务就是搜网模块中的运营商相关信息获取的业务,比如我们常见的手机状态栏上的运营商名称显示。
下面来针对AP侧中搜网相关的PLMN业务解读下源码流程。
Ril架构回顾
在进行代码解读之前,还是对电话子系统的架构再讲解下,如下图
文章图片
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:%publicp", 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: %publicd", 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);
/* Disabledauto-answer */
SendCommandLock("ATS0=0", NULL, 0, NULL);
/* Extended errors */
SendCommandLock("AT+CMEE=1", NULL, 0, NULL);
/* Set to signalreporting */
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 %publicd", 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 formatunexpected: %publics", 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:%publicd", 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数据,这里就不继续追了。
最后附上上述流程的时序图。
四、业务流程时序图
文章图片
想了解更多关于鸿蒙的内容,请访问:
51CTO和华为官方合作共建的鸿蒙技术社区
https://ost.51cto.com/#bkwz
::: hljs-center
文章图片
【《OpenHarmony 3GPP协议开发深度剖析》之--PLMN业务源码解读】:::
推荐阅读
- Django模版与vue.js渲染冲突问题
- [OpenCV实战]1 基于深度学习识别人脸性别和年龄
- kubernetes-部署helm3
- Yaml文件解析
- 科技向善,“以人为本”将掷地有声!
- 一文详尽 VMWare 多系统安装教程
- centos7 部署confluence7.13.4
- 春眠不觉晓,Redis数据类型知多少(String,List,Set,SortedSet,Hash,Bitmap,HyperLogLogs)
- 红黑树