1、背景介绍
3.1版本标准系统增加了通话相关的联系人应用,来电应用等,在系统服务层面电话相关功能也比较完善,相关modem模块目前从代码中看到有美格智能的slm790和紫光展锐的模块,之前介绍过鸿蒙电话服务子系统功能划分介绍,只是对官方文档的介绍,可以通过这个文档先去了解一下各个功能模块。今天从上到下分析下代码调用流程,以拨打电话为例介绍鸿蒙电话子系统的各个部分。电话服务子系统的在/base/telephony目录下,大概有700多个文件,11万多行代码。
2、应用层js代码介绍
应用层拨打电话的应用是联系人,联系人应用的通话记录,联系人查看,拨号盘和收藏都有对拨电话的调用。
最终调用的是app.js中的call函数,此函数调用的是@ohos.telephony.call中的dial方法。
应用层只是调用电话框架层的api,这个api就是@ohos.telephony.call提供的。
3、拨打电话NAPI实现介绍
应用层调用的是js函数,而电话服务层是c语言实现的,二者之间的桥梁就是通过NAPI实现的,有关NAPI介绍请参照这几篇文章源码解析之JavaScript API框架(NAPI)。简单来说js的函数在c层都有对应的实现,它们之间是一一对应的关系,比如js的dial对应的是c++的DialCall,相关代码如下,这部分代码在通话管理模块。
NapiCallManager::DialCall函数中调用的函数是。
HandleAsyncWork(env, asyncContext, “DialCall”, NativeDialCall, NativeDialCallBack)。
之后跳转到了CallManagerClient中的DialCall,然后是CallManagerProxy::DialCall,如下:
4、如何从CallManagerServiceProxy到CallManagerServiceStub
到CallManagerProxy::DialCall中后代码突然不清晰了,callManagerServicePtr_是什么,它的DialCall在哪里,顺藤摸瓜,它的位置如下:
SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager()和managerPtr->GetSystemAbility(systemAbilityId_)是理解下一步调用位置的关键。
这里涉及到了另外一个知识点,就是进程通信,这里使用了ipc机制,可以查看官方文档的介绍。
在目录foundation/comminucotion下的ipc下有readme文档,贴一张原图:
大体意思是proxy通过samgr也就是SystemAbilityManager,去调用跨进程的Stub中的函数,代码层面可以通过systemAbilityId_和接口类去定位函数调用的对应关系,这里的abilityid是TELEPHONY_CALL_MANAGER_SYS_ABILITY_ID,接口类是ICallManagerService,通过查找定位到了CallManagerServiceProxy和CallManagerServiceStub。
函数调用就是从CallManagerServiceProxy::DialCall过渡到了CallManagerServiceStub::OnDialCall。
5、从CallControlManager::DialCall到 蜂窝通话(cellular_call)模块
CallManagerServiceStub::OnDialCall中调用到了CallControlManager::DialCall,然后又走到了。
CallRequestHandlerService::DialCall(),这里调用了一个发送事件函数,这是一种线程异步处理机智可以看这篇文章,只要有发就有收,我们找到收事件的地方。
对应关系在如下位置,由此找到了函数CallRequestHandler::DialCallEvent。
CallRequestHandler::DialCallEvent中有针对类型的判断:
我们走CallRequestProcess::DialRequest分支,然后走到了CellularCallIpcInterfaceProxy::Dial。
在这里我们又遇到了ipc,同样的方法此时id是TELEPHONY_CELLULAR_CALL_SYS_ABILITY_ID。
找到stub函数位置是CellularCallStub::Dial,此时已经到了蜂窝通话模块目录。
6、从蜂窝通话(cellular_call)到 核心服务(core_service)
CellularCallStub::Dial直接调用的是CSControl::Dial。
在这里又出现了类型判断,判断手机网络制式是gsm还是cdma,了解通信行业的都知道gsm和cdma是2G时代的两种制式标准,一个是欧洲主导的,一个是高通主导的,扯得有点远,继续看代码。
二者最终调用的是CSControl::EncapsulateDialCommon,然后CellularCallConnectionCS::DialRequest。
然后进入到了核心服务模块。
然后是TelRilManager::Dial和TelRilCall::Dial。
7、从核心服务(core_service)到 RIL适配层(RIL Adapter)
TelRilCall::Dial中调用了TelRilBase基类的SendBufferEvent(HREQ_CALL_DIAL, wData)函数代码如下:
cellularRadio_这个成员的初始化实在TelRilManager中。
这又涉及到了另外一个知识点驱动相关看这篇OpenHarmony HDF HDI基础能力分析与使用,对modem的操作在RIL适配层被当做了驱动服务来使用的,SendRequest就是对驱动的异步调用,在驱动中的函数RilAdapterDispatch负责对SendRequest的接收处理。
而这个处理函数是驱动加载时运行起来的。
RilAdapterDispatch中的函数DispatchRequest位于HRilManager中,然后调用DispatchModule。
继续调用HRilManager::Dispatch,进入HRilCall中的ProcessCallRequest,然后根据HREQ_CALL_DIAL在函数指针数组reqMemberFuncMap_找到了处理函数HRilCall::Dial,然后处理逻辑变到了callFuncs_->Dial中。
8、RIL适配层(RIL Adapter)中的处理流程
要知道callFuncs_->Dial的最终位置,就要查找callFuncs_的赋值流程, HRilCall::RegisterCallFuncs上层调用是
HRilManager::RegisterCallFuncs再上层是HRilRegOps。
HRilRegOps在LoadVendor中调用,这又涉及到了modem的加载机制,具体厂商modem处理的部分是通过so加载的方式引入的。
通过打开so文件并建立函数的对应关系,最终调用的at命令的具体实现部分。
so的代码目录在vendor目录下,通过成员为函数指针的结构体建立关联性。
也就是callFuncs_->Dial在so中的处理函数是ReqDial,在这里边组装了具体的at命令 ,拨打电话的命令是ATD,GenerateCommand(cmd, MAX_CMD_LENGTH, “ATD%s%s;”, pDial->address, clir)。
最终通过WriteATCommand函数写入到了at命令的通道,通道就是具体的modem模块与主处理器的硬件连接了。
也就是当前代码中的usb通道。