#过年不停更#OpenHarmony源码解析之电话子系统(call) 原创 精华

深开鸿
发布于 2022-2-14 10:29
浏览
3收藏

春节不停更,此文正在参加「星光计划-春节更帖活动」
作者:王大鹏

1. 简介

OpenHarmony电话子系统为OS提供了基础的无线通信能力,支持TD-LTE/FDD-LTE/TD-SCDMA/WCDMA/EVDO/CDMA1X/GSM等等网络制式的通信模块,能够提供高速的无线数据传输、互联网接入等业务,具备语音、短信、彩信,SIM卡等功能。

以下行文如无特别说明,所述说的鸿蒙系统均指OpenHarmony系统(OpenHarmony 3.0 LTS版本)。

1.1OpenHarmony架构图

#过年不停更#OpenHarmony源码解析之电话子系统(call)-鸿蒙开发者社区

2. 基础知识

2.1电话子系统

电话子系统做为OpenHarmony很重要的组成部分,为系统提供基础的通信功能,包括CS域的服务,比如语音呼叫、短信、呼叫管理,也包括PS域的相关服务,比如MMS、数据业务等,另外SIM和RIL的业务也在该子系统内。

2.2 电话子系统架构图

OpenHarmony现有电话子系统蜂窝通话通话相关框架图:

#过年不停更#OpenHarmony源码解析之电话子系统(call)-鸿蒙开发者社区

应用层 :各种需要通话、SMS、数据业务、SIM卡功能的应用,例如call应用,SMS应用,launcher应用等等。

框架层 :

1.SDK :给应用提供标准接口,包括JS接口和C++接口。

2.Framework:向应用层提供对应模块稳定的基础能力,包括network、call、sms,sim,相关的功能,包括通话管理,短彩编辑以及发送接收,sim卡的识别驻网,数据业务的管理等。在目前的OpenHarmony版本中,call_manager、cellular_call、cellular_data、data_storage、sms_mms、state_registry、core_service等模块都属于框架层。

Hril层 :相当于安卓的ril,由于不同方案使用的Modem不一样,所以各种指令格式,初始化序列都不一样,为了消除这些差别,而Hril则提供了无线硬件设备与电话服务之间的抽象层。

Vendor lib层 :类似于安卓的RILJ,负责与modem模块交互,发送各模块对应的AT命令。

Modem层:现在的基带处理器,主要处理数字信号、语音信号的编码解码以及通信协议,而基带处理器、射频和其它外围芯片作为一个 Modem 模块,提供 AT 命令接口给上层用于交互。通信模块Modem 通过与通信网络进行沟通,传输语音及数据,完成呼叫、短信等相关电话功能。

2.3电话子系统代码结构

由于电话子系统包含较多模块,所以将各模块分开描述:

通话管理模块:主要管理CS(Circuit Switch,电路交换)、IMS(IP Multimedia Subsystem,IP多媒体子系统)和OTT(over the top,OTT解决方案)三种类型的通话,负责申请通话所需要的音视频资源,并处理多路通话时产生的各种冲突。

1.	/base/telephony/call_manager  
2.	├─ frameworks                              # napi接口存放目录  
3.	├─ interfaces                              # 对外部暴露的接口  
4.	│  ├─ innerkits                            # 部件间的内部接口  
5.	│  └─ kits                                 # js接口存放目录  
6.	├─ sa_profile                              # 启动配置文件  
7.	├─ services                                # 服务内部代码  
8.	│  ├─ audio                                # 音频管理相关代码  
9.	│  ├─ bluetooth                            # 蓝牙通话相关代码  
10.	│  ├─ call                                 # 通话业务相关代码  
11.	│  ├─ call_manager_service                 # 进程服务管理相关代码  
12.	│  ├─ call_setting                         # 通话设置相关代码  
13.	│  ├─ telephony_interaction                # 电话核心服务交互相关代码  
14.	│  └─ call_ui_interaction                  # UI交互相关代码  
15.	├─ test                                    # 单元测试相关代码  
16.	└─ utils                                   # 通用工具类  
17.	``` 

蜂窝通话模块:支持基于运营商网络的基础通话实现,包含基于2G/3G的CS(Circuit Switch,电路交换)通话和基于4G/5G的IMS(IP Multimedia Subsystem,IP多媒体子系统)通话,包含VoLTE/ VoWIFI/ VoNR语音、视频、会议,支持CS和IMS通话之间的域选控制和切换,支持紧急通话。支持主流modem芯片平台。

1.	/base/telephony/cellular_call     # 蜂窝通话子组件  
2.	├─ BUILD.gn                       # 编译gn脚本  
3.	├─ README.md                      # Readme文档  
4.	├─ services  
5.	│  ├─ common                      # 工具仓  
6.	│  ├─ connection                  # 连接层  
7.	│  ├─ control                     # 控制业务层  
8.	│  └─ manager                     # 管理层  
9.	├─ sa_profile                     # sa文件  
10.	├─ ohos.build                     # 编译build  
11.	└─ test                           # 测试相关  
12.	```  

蜂窝数据模块:作为电话子系统可裁剪部件,依赖于core_service核心服务、ril_adapter。 具有蜂窝数据激活、蜂窝数据异常检测与恢复、蜂窝数据状态管理、蜂窝数据开关管理、蜂窝数据漫游管理、APN管理、网络管理交互等功能。

1.	base/telephony/cellular_data  
2.	├── figures  
3.	├── frameworks  
4.	│   ├── js                              # js文件  
5.	│   │   └── napi  
6.	│   │       ├── include  
7.	│   │       └── src  
8.	│   └── native  
9.	│       └── src  
10.	├── interfaces  
11.	│   ├── innerkits                       # 对外提供的接口  
12.	│   └── kits  
13.	│       └── js                          # 对外提供的js接口  
14.	│           └── declaration  
15.	├── sa_profile                          # SA配置  
16.	├── services  
17.	│   ├── include                         # 头文件  
18.	│       ├── apn_manager  
19.	│   │   ├── common  
20.	│   │   ├── state_machine  
21.	│   │   └── utils  
22.	│   └── src                             # 源文件  
23.	│       ├── apn_manager  
24.	│       ├── state_machine  
25.	│       └── utils  
26.	└── test  
27.	    └── unit_test                       # 单元测试相关代码  
28.	```  

电话核心服务模块:主要功能是初始化RIL管理、SIM卡和搜网模块,以及获取RIL Adapter服务。通过注册回调服务,实现与RIL Adapter进行通信;通过发布订阅,来实现与各功能模块的通信。

1.	/base/telphony/core_service  
2.	├── interfaces             # 接口目录  
3.	│   ├── innerkits          # 部件间的内部接口  
4.	│   └── kits               # 对应用提供的接口(例如JS接口)  
5.	├── services               # 核心服务实现代码目录  
6.	│   ├── include  
7.	│   └── src  
8.	├── etc                    # 核心服务的驱动脚本目录  
9.	│   └── init  
10.	├── sa_profile             # 核心服务的启动文件目录  
11.	├── tel_ril                # 核心服务与RIL Adapter通信代码目录  
12.	│   ├── include          
13.	│   ├── src  
14.	│   └── test  
15.	├── network_search         # 搜网服务代码目录  
16.	│   ├── include  
17.	│   ├── src  
18.	│   └── test  
19.	├── sim                    # SIM卡服务代码目录  
20.	│   ├── include  
21.	│   ├── src  
22.	│   └── test  
23.	├── frameworks             # frameworks目录  
24.	│   ├── js  
25.	│   ├── nstive  
26.	├── common                 # 各个业务模块头文件目录  
27.	│   ├── api  
28.	│   ├── call_manager  
29.	│   ├── network_search  
30.	│   └── sim  
31.	├── utils  
32.	│   ├── log                # 核心服务日志打印目录  
33.	│   ├── preferences  
34.	│   ├── common  
35.	```

数据库及持久化模块:负责电话服务子系统中的SIM卡/短彩信等模块持久化数据存储,提供DataAbility访问接口。

1.	/base/telephony/data_storage     # 数据库及持久化  
2.	├─ BUILD.gn                         # 编译gn脚本  
3.	├─ README.md                        # Readme文档  
4.	├─ common                           # 公共、通用文件  
5.	│  ├─ include                         
6.	│  └─ src                             
7.	├─ pdp_profile                      # 网络运营商  
8.	│  ├─ include                         
9.	│  └─ src                             
10.	├─ sim                              # sim卡  
11.	│  ├─ include                         
12.	│  └─ src                             
13.	├─ sms_mms                          # 短彩信  
14.	│  ├─ include                        
15.	│  └─ src                             
16.	├─ ohos.build                       # 编译build  
17.	└─ test                             # 测试相关  
18.	```  

RIL Adapter模块:主要包括厂商库加载,业务接口实现以及事件调度管理。主要用于屏蔽不同modem厂商硬件差异,为上层提供统一的接口,通过注册HDF服务与上层接口通讯。

1.	base/telephony/ril_adapter  
2.	├─ hril                            # hri层的各个业务模块接口实现  
3.	├─ hril_hdf                        # HDF服务  
4.	├─ include                         # 头文件存放目录  
5.	├─ interfaces                      # 对应提供上层各业务内部接口  
6.	│  └─ innerkits  
7.	├─ test                            # 单元测试相关代码  
8.	│  ├─ mock  
9.	│  └─ unittest                     # 单元测试代码  
10.	└─ vendor                          # 厂商库代码  
11.	│  └─ include  
12.	``` 

短彩信模块:为移动数据用户提供短信收发和彩信编解码功能,主要功能有GSM/CDMA短信收发、短信PDU(Protocol data unit,协议数据单元)编解码、Wap Push接收处理 、小区广播接收、彩信通知、 彩信编解码和SIM卡短信记录增删改查等。

1.	/base/telephony/sms_mms  
2.	├─ interfaces               # 对外暴露的接口  
3.	│  └─ kits  
4.	├─ sa_profile               # 启动配置文件  
5.	├─ services                 # 服务内部代码  
6.	│  ├─ include               # 头文件目录  
7.	│  ├─ cdma                  # CDMA制式源文件  
8.	│  └─ gsm                   # GSM制式源文件  
9.	├─ test                     # 单元测试目录  
10.	└─ utils                    # 通用工具相关  
11.	``` 

状态注册模块:主要负责提供电话服务子系统各种消息事件的订阅以及取消订阅的API。事件类型包括网络状态变化、信号强度变化、小区信息变化、蜂窝数据连接状态变化、通话状态变化等等。

1.	/base/telephony/state_registry      # 状态注册转发服务  
2.	├─ BUILD.gn                         # 编译gn脚本  
3.	├─ README.md                        # Readme文档  
4.	├─ interfaces                       # API,js文件  
5.	├─ service  
6.	│  ├─ include                       # 头文件  
7.	│  └─ src                           # 源文件  
8.	├─ sa_profile                       # sa文件  
9.	├─ ohos.build                       # 编译build  
10.	└─ test                             # 测试相关  
11.	```  

2.4 相关仓

2.5电话子系统(call)核心类

由于电话子系统所包含的模块太多,如果全部进行介绍会要很大篇幅,本次我们只对蜂窝通话核心业务模块进行进一步介绍:

所属服务 类名 功能
call_manager CallAbilityReportIpcProxy 负责通话状态上报
CallManagerService 负责通话服务管理
CallControlManager 负责通话控制管理
CallStatusManager 负责呼叫状态管理
CallStatusCallback 呼叫状态回调
CSCall 普通的CS呼叫相关处理
IMSCall IMS呼叫相关处理
OTTCall OTT呼叫相关处理(目前基本不支持)
AudioControlManager 音频管理类
BluetoothCallManager 蓝牙呼叫管理
VideoControlManager 视频管理类
cellular_call CellularCallService 通话管理层实现类
CellularCallRegister 提供通话信息变化订阅功能
CellularCallProxy 提供蜂窝通话对外接口实现的代理
CellularCallHandler 处理RIL Adapter上报的消息
CellularCallStub 蜂窝通话业务层
CSControl 处理CS通话
IMSControl 处理IMS通话
CellularCallConfig 配置通话业务
CellularCallSupplement 通话补充业务
BaseConnection 会话连接
ConfigRequest 配置业务命令请求
SupplementRequest 补充业务命令请求
core_service Core Core的初始化和各模块Manager交互的集合。
RilManager Core核心服务与RIL Adapter通信管理代码
TelRilCall Call相关核心服务与RIL Adapter通信代码
ril_adapter HRilManager hril层管理类
HRilCall hril层Call模块管理类

3. 源码解析

做为电话子系统的核心业务,通话功能(Call)除了需要硬件支持,比如音频模块,基带模块等,还需要系统本身的许多服务相互配合才能实现该功能,比如:通话管理(call_manager)、蜂窝通话服务(cellular_call)、Telephony核心服务(core_service)、RIL适配(ril_adapter),状态注册服务(state_registry)等。

通话目前主要分成三种:CS Call(Circuit Switch,电路交换)、IMS Call(IP Multimedia Subsystem,IP多媒体子系统)和OTT Call(over the top,OTT解决方案)三种类型的通话。对上层Call应用暴露的接口包括:dial、answer、reject、hangup、holdCall、unHoldCall、switchCal、startDTMF、stopDTMF等。由于事件较多,而且各事件处理流程比较类似,所以我们此处以Call的Answer事件处理流程来阐述。

3.1通话上层调用代码分析

当有电话呼入时,Callui界面会显示电话呼入,用户点击answer按键,则会激活Call的Answer流程:

1.	<div class="bottom-btn-wrap">  
2.	       <div class="btn-item">  
3.	           <div class="btn-box" @click="onReject">  
4.	               <image src="assets/picture/hangUP.png"></image>  
5.	           </div>  
6.	       </div>  
7.	       <div class="btn-item">  
8.	           <div class="btn-box" @click="onAnswer">  
9.	               <image src="assets/picture/answer.png"></image>  
10.	           </div>  
11.	       </div>  
12.	   </div>  

此处会调用incomingCom.js的onAnswer函数:

1.	/** 
2.	 * Answer the phone interface 
3.	 */  
4.	onAnswer() {  
5.	    console.log('onAnswer function start');  
6.	    console.log('this.callData: ' + JSON.stringify(this.callData));  
7.	    acceptCall(this.callData.callId);  
8.	},  

由于之前已经import了callServiceProxy.js文件

1.	import {acceptCall, rejectCall, hangUpCall} from'../../../model/callServiceProxy.js';

所以我们来看下callServiceProxy的acceptCall 函数:

1.	/** 
2.	 * accept call 
3.	 * @param { number } callId - call id 
4.	 */  
5.	export const acceptCall = function (callId) {  
6.	    call.answer(callId).then((res) => {  
7.	        console.log(prefixLog + 'then:acceptCall' + JSON.stringify(res));  
8.	    }).catch((err) => {  
9.	        console.log(prefixLog + 'catch:acceptCall' + JSON.stringify(err));  
10.	    });  
11.	}; 

从代码中我们能看到此函数实际调用了call的answer函数,那么这个哪来的呢。实际这个call是由@ohos.telephony.call中引入的,从这里代码已经完成了从app到framework的调用。

1.	import call from '@ohos.telephony.call'; 

3.2通话框架层代码分析

3.2.1通话框架层代码调用时序图

#过年不停更#OpenHarmony源码解析之电话子系统(call)-鸿蒙开发者社区

3.2.2通话框架层代码分析

从上边的时序图可以看出,整个Answer的调用流程是比较长的,整个框架层处理跨越包括call_manager、cellular_call、core_service、IPC、ril_adapter等多个服务。由于处理流程过长,具体的调用情况可以参照时序图,此处我们将框架层的处理一些关键地方根据调用不同的服务来进行描述。

3.2.2.1Answer事件在call_manager中的处理

#过年不停更#OpenHarmony源码解析之电话子系统(call)-鸿蒙开发者社区

之前应用层调用的call.answer是通过@ohos.telephony.call引入的,而实际定义在call_manage服务中的interfaces内的napi接口中实现:

1.	static napi_module g_nativeCallManagerModule = {  
2.	    .nm_version = NATIVE_VERSION,  
3.	    .nm_flags = NATIVE_FLAGS,  
4.	    .nm_filename = nullptr,  
5.	    .nm_register_func = NapiCallManager::RegisterCallManagerFunc,  
6.	    .nm_modname = "telephony.call",  
7.	    .nm_priv = ((void *)0),  
8.	    .reserved = {0},  
9.	}; 

注册要用到接口以及一些枚举参数

napi_value NapiCallManager::RegisterCallManagerFunc(napi_env env, napi_value exports)

1.	napi_value NapiCallManager::RegisterCallManagerFunc(napi_env env, napi_value exports)  
2.	{  
3.	    DeclareCallBasisInterface(env, exports);  
4.	    DeclareCallConferenceInterface(env, exports);  
5.	    DeclareCallSupplementInterface(env, exports);  
6.	    DeclareCallExtendInterface(env, exports);  
7.	    DeclareCallMultimediaInterface(env, exports);  
8.	    DeclareCallMediaEnum(env, exports);  
9.	    DeclareCallDialEnum(env, exports);  
10.	    DeclareCallStateEnum(env, exports);  
11.	    DeclareCallEventEnum(env, exports);  
12.	    DeclareCallRestrictionEnum(env, exports);  
13.	    DeclareCallWaitingEnum(env, exports);  
14.	    DeclareCallTransferEnum(env, exports);  
15.	    std::u16string bundleName = GetBundleName(env);  
16.	    Init(bundleName);  
17.	    return exports;  
18.	}  

这里我们需要的是CallBasis接口,具体内容如下所述,这些就是之前应用层调用的一些事件对应的处理函数。

1.	napi_value NapiCallManager::DeclareCallBasisInterface(napi_env env, napi_value exports)  
2.	{  
3.	    napi_property_descriptor desc[] = {  
4.	        DECLARE_NAPI_FUNCTION("dial", DialCall),  
5.	        DECLARE_NAPI_FUNCTION("answer", AnswerCall),  
6.	        DECLARE_NAPI_FUNCTION("reject", RejectCall),  
7.	        DECLARE_NAPI_FUNCTION("hangup", HangUpCall),  
8.	        DECLARE_NAPI_FUNCTION("holdCall", HoldCall),  
9.	        DECLARE_NAPI_FUNCTION("unHoldCall", UnHoldCall),  
10.	        DECLARE_NAPI_FUNCTION("switchCall", SwitchCall),  
11.	        DECLARE_NAPI_FUNCTION("upgradeCall", UpgradeCall),  
12.	        DECLARE_NAPI_FUNCTION("downgradeCall", DowngradeCall),  
13.	        DECLARE_NAPI_FUNCTION("setCallPreferenceMode", SetCallPreferenceMode),  
14.	    };  
15.	    NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) /   sizeof(desc[0]), desc));  
16.	    return exports;  
17.	} 

因为我们对应的是answer事件,所以这里的对应函数是AnswerCall。

1.	napi_value NapiCallManager::AnswerCall(napi_env env, napi_callback_info info)  
2.	{  
3.	    GET_PARAMS(env, info, VALUE_MAXIMUM_LIMIT);  
4.	    NAPI_ASSERT(env, argc <= VALUE_MAXIMUM_LIMIT, "parameter error!");  
5.	    bool matchFlag = MatchValueType(env, argv[ARRAY_INDEX_FIRST], napi_number);  
6.	    NAPI_ASSERT(env, matchFlag, "Type error, should be number type");  
7.	    auto asyncContext = (std::make_unique<AnswerAsyncContext>()).release();  
8.	    napi_get_value_int32(env, argv[ARRAY_INDEX_FIRST], &asyncContext->callId);  
9.	    if (argc == TWO_VALUE_LIMIT) {  
10.	        if (MatchValueType(env, argv[ARRAY_INDEX_SECOND], napi_function)) {  
11.	            napi_create_reference(env, argv[ARRAY_INDEX_SECOND], DATA_LENGTH_ONE, &                       (asyncContext->callbackRef));  
12.	        } else if (MatchValueType(env, argv[ARRAY_INDEX_SECOND], napi_number)) {  
13.	            asyncContext->videoState = GetIntProperty(env, argv[ARRAY_INDEX_SECOND],                     "videoState");  
14.	        }  
15.	    } else if (argc == VALUE_MAXIMUM_LIMIT) {  
16.	        asyncContext->videoState = GetIntProperty(env, argv[ARRAY_INDEX_SECOND],                     "videoState");  
17.	        napi_create_reference(env, argv[ARRAY_INDEX_THIRD], DATA_LENGTH_ONE, &                       (asyncContext->callbackRef));  
18.	    }  
19.	    return HandleAsyncWork(env, asyncContext, "AnswerCall", NativeAnswerCall,                     NativeVoidCallBack);  
20.	}  

继续往下调用,从之前的函数看AnswerCall应该是异步处理的。

1.	void NapiCallManager::NativeAnswerCall(napi_env env, void *data)  
2.	{  
3.	    if (data == nullptr) {  
4.	        TELEPHONY_LOGE("data is nullptr");  
5.	        return;  
6.	    }  
7.	    auto asyncContext = (AnswerAsyncContext *)data;  
8.	    int32_t ret = DelayedSingleton<CallManagerProxy>::GetInstance()->AnswerCall(  
9.	        asyncContext->callId, asyncContext->videoState);  
10.	    asyncContext->result = ret;  
11.	}  

在一路调用的的过程中,会调用CallPolicy类的AnswerCallPolicy函数用来判断对应callId的CallObject是否存在,如有有就判断Call所处的状态。

1.	int32_t CallPolicy::AnswerCallPolicy(int32_t callId)  
2.	{  
3.	    if (!IsCallExist(callId)) {  
4.	        TELEPHONY_LOGE("callId is invalid, callId:%{public}d", callId);  
5.	        return CALL_ERR_CALLID_INVALID;  
6.	    }  
7.	    TelCallState state = GetCallState(callId);  
8.	    if (state != CALL_STATUS_INCOMING && state != CALL_STATUS_WAITING) {  
9.	        TELEPHONY_LOGE("current call state is:%{public}d, accept call not allowed",                   state);  
10.	        return CALL_ERR_ILLEGAL_CALL_OPERATION;  
11.	    }  
12.	    return TELEPHONY_SUCCESS;  
13.	}  

在后续调用到CallRequestHandlerService的AnswerCall进行处理时,如果有CallRequestHandler的handler_存在,则make个AnswerCallPara的unique_ptr,然后将callid和videostate传入该指针。调用SendEvent将HANDLER_ANSWER_CALL_REQUEST发出。

1.	int32_t CallRequestHandlerService::AnswerCall(int32_t callId, int32_t videoState)   
2.	{  
3.	    if (handler_.get() == nullptr) {  
4.	        TELEPHONY_LOGE("handler_ is nullptr");  
5.	        return TELEPHONY_ERR_FAIL;  
6.	    }  
7.	    std::unique_ptr<AnswerCallPara> para = std::make_unique<AnswerCallPara>();  
8.	    if (para.get() == nullptr) {  
9.	        TELEPHONY_LOGE("make_unique AnswerCallPara failed!");  
10.	        return TELEPHONY_ERR_FAIL;  
11.	    }  
12.	    para->callId = callId;  
13.	    para->videoState = videoState;  
14.	    if (!handler_->SendEvent(HANDLER_ANSWER_CALL_REQUEST, std::move(para))) {  
15.	        TELEPHONY_LOGE("send accept event failed!");  
16.	        return TELEPHONY_ERR_FAIL;  
17.	    }  
18.	    return TELEPHONY_SUCCESS;  
19.	} 

从代码的处理流程看,这里发出的event会被同一个文件中的CallRequestHandler类捕获,触发ProcessEvent处理。

1.	void CallRequestHandler::ProcessEvent(const AppExecFwk::InnerEvent::Pointer &event)  
2.	{  
3.	    if (event == nullptr) {  
4.	        TELEPHONY_LOGE("CallRequestHandler::ProcessEvent parameter error");  
5.	        return;  
6.	    }  
7.	    TELEPHONY_LOGD("CallRequestHandler inner event id obtained: %{public}u.", event-             >GetInnerEventId());  
8.	    auto itFunc = memberFuncMap_.find(event->GetInnerEventId());  
9.	    if (itFunc != memberFuncMap_.end()) {  
10.	        auto memberFunc = itFunc->second;  
11.	        if (memberFunc != nullptr) {  
12.	            return (this->*memberFunc)(event);  
13.	        }  
14.	    }  
15.	} 

memberFuncMap会根据event的id从memberFuncMap_拿到对应的memberFunc,然后进入对应的处理函数:

1.	CallRequestHandler::CallRequestHandler(const std::shared_ptr<AppExecFwk::EventRunner> &runner)  
2.	    : AppExecFwk::EventHandler(runner), callRequestProcessPtr_(nullptr)  
3.	{  
4.	    memberFuncMap_[CallRequestHandlerService::HANDLER_DIAL_CALL_REQUEST] = &CallRequestHandler::DialCallEvent;  
5.	    memberFuncMap_[CallRequestHandlerService::HANDLER_ANSWER_CALL_REQUEST] = &CallRequestHandler::AcceptCallEvent;  
6.	    memberFuncMap_[CallRequestHandlerService::HANDLER_REJECT_CALL_REQUEST] = &CallRequestHandler::RejectCallEvent;  
7.	    memberFuncMap_[CallRequestHandlerService::HANDLER_HANGUP_CALL_REQUEST] = &CallRequestHandler::HangUpCallEvent;  
8.	    memberFuncMap_[CallRequestHandlerService::HANDLER_HOLD_CALL_REQUEST] = &CallRequestHandler::HoldCallEvent;  
9.	    memberFuncMap_[CallRequestHandlerService::HANDLER_UNHOLD_CALL_REQUEST] = &CallRequestHandler::UnHoldCallEvent;  
10.	    memberFuncMap_[CallRequestHandlerService::HANDLER_SWAP_CALL_REQUEST] = &CallRequestHandler::SwitchCallEvent;  
11.	    memberFuncMap_[CallRequestHandlerService::HANDLER_COMBINE_CONFERENCE_REQUEST] =  
12.	        &CallRequestHandler::CombineConferenceEvent;  
13.	    memberFuncMap_[CallRequestHandlerService::HANDLER_SEPARATE_CONFERENCE_REQUEST] =  
14.	        &CallRequestHandler::SeparateConferenceEvent;  
15.	    memberFuncMap_[CallRequestHandlerService::HANDLER_UPGRADE_CALL_REQUEST] = &CallRequestHandler::UpgradeCallEvent;  
16.	    memberFuncMap_[CallRequestHandlerService::HANDLER_DOWNGRADE_CALL_REQUEST] =  
17.	        &CallRequestHandler::DowngradeCallEvent;  
18.	}  

这里对应的处理函数是AcceptCallEvent(),这部分函数无返回值,又从event中取出callId和videoState。

1.	void CallRequestHandler::AcceptCallEvent(const AppExecFwk::InnerEvent::Pointer &event)  
2.	{  
3.	    if (event == nullptr) {  
4.	        TELEPHONY_LOGE("CallRequestHandler::ProcessEvent parameter error");  
5.	        return;  
6.	    }  
7.	    auto object = event->GetUniqueObject<AnswerCallPara>();  
8.	    if (object == nullptr) {  
9.	        TELEPHONY_LOGE("object is nullptr!");  
10.	        return;  
11.	    }  
12.	    AnswerCallPara acceptPara = *object;  
13.	    if (callRequestProcessPtr_ == nullptr) {  
14.	        TELEPHONY_LOGE("callRequestProcessPtr_ is nullptr");  
15.	        return;  
16.	    }  
17.	    callRequestProcessPtr_->AnswerRequest(acceptPara.callId, acceptPara.videoState);  
18.	} 

继续调用CallRequestProcessl类中的AnswerReques函数时,会通过GetOneCallObject拿到不同的CallObject。

1.	void CallRequestProcess::AnswerRequest(int32_t callId, int32_t videoState)  
2.	{  
3.	    sptr<CallBase> call = GetOneCallObject(callId);  
4.	    if (call == nullptr) {  
5.	        TELEPHONY_LOGE("the call object is nullptr, callId:%{public}d", callId);  
6.	        return;  
7.	    }  
8.	    int32_t ret = call->AnswerCall(videoState);  
9.	    if (ret != TELEPHONY_SUCCESS) {  
10.	        TELEPHONY_LOGE("AnswerCall failed!");  
11.	        return;  
12.	    }  
13.	    DelayedSingleton<CallControlManager>::GetInstance()->NotifyIncomingCallAnswered(call);  
14.	} 

拿到了对应的call之后,就可以调用对应的call的AnswerCall函数,这个函数会覆盖BaseCall的虚函数。由于CSCall,IMSCall,OTTCall都有对应的AnswerCall函数,所以实际使用那个是由BaseCall类的虚函数AnswerCall被那个子类重写,从而调用 不同的AnswerCall函数。这里我们假设为CsCall 。

1.	int32_t CSCall::AnswerCall(int32_t videoState)  
2.	{  
3.	    return CarrierAcceptCall(videoState);  
4.	}  

调用到CarrierCall的CarrierAcceptCall进行后续处理。其中AcceptCallBase()函数会判断Call状态,如果是CALL_RUNNING_STATE_RINGING状态,则会调用AudioControlManager的SetVolumeAudible来设置audio的相关内容。

1.	int32_t CarrierCall::CarrierAcceptCall(int32_t videoState)  
2.	{  
3.	    CellularCallInfo callInfo;  
4.	    AcceptCallBase();     
5.	    PackCellularCallInfo(callInfo);  
6.	    int32_t ret = DelayedSingleton<CellularCallIpcInterfaceProxy>::GetInstance()->Answer(callInfo);  
7.	    if (ret != TELEPHONY_SUCCESS) {  
8.	        TELEPHONY_LOGE("Accept failed!");  
9.	        return CALL_ERR_ACCEPT_FAILED;  
10.	    }  
11.	    return TELEPHONY_SUCCESS;  
12.	}  

处理完AcceptCallBase()后,需要用PackCellularCallInfo将call的信息打包进给callinfo中,后续直接该callInfo传递出去。

1.	void CarrierCall::PackCellularCallInfo(CellularCallInfo &callInfo)  
2.	{  
3.	    callInfo.callId = callId_;  
4.	    callInfo.callType = callType_;  
5.	    callInfo.videoState = (int32_t)videoState_;  
6.	    callInfo.index = index_;  
7.	    callInfo.slotId = slotId_;  
8.	    (void)memset_s(callInfo.phoneNum, kMaxNumberLen, 0, kMaxNumberLen);  
9.	    if (memcpy_s(callInfo.phoneNum, kMaxNumberLen, accountNumber_.c_str(), accountNumber_.length()) != 0) {  
10.	        TELEPHONY_LOGW("memcpy_s failed!");  
11.	        return;  
12.	    }  
13.	}  

下一步会调用CellularCallIpcInterfaceProxy类的来Answer函数来继续处理。首先要判断是否有ReConnectService的必要,这样操作是为了保证有可用的cellularCallInterfacePtr_。cellularCallInterfacePtr_是一个IRemoteBroker类,该类是一个IPC基类接口用于IPC通信。

1.	int CellularCallIpcInterfaceProxy::Answer(const CellularCallInfo &callInfo)  
2.	{  
3.	    if (ReConnectService() != TELEPHONY_SUCCESS) {  
4.	        return TELEPHONY_ERR_IPC_CONNECT_STUB_FAIL;  
5.	    }  
6.	    std::lock_guard<std::mutex> lock(mutex_);  
7.	    int errCode = cellularCallInterfacePtr_->Answer(callInfo);    
8.	    if (errCode != TELEPHONY_SUCCESS) {  
9.	        TELEPHONY_LOGE("answering call failed, errcode:%{public}d", errCode);  
10.	        return TELEPHONY_ERR_FAIL;  
11.	    }  
12.	    return TELEPHONY_SUCCESS;  
13.	}  

在此之前call_manager已经在CellularCallIpcInterfaceProxy的初始化中将systemAbilityId(TELEPHONY_CELLULAR_CALL_SYS_ABILITY_ID)通过ConnectService()完成对应SA代理IRemoteObject的获取,然后构造对应的Proxy类,这样就能保证IPC通信的proxy和stub的对应。下节我们会看到,对应的stub其实是在cellular_call中注册的。

1.	void CellularCallIpcInterfaceProxy::Init(int32_t systemAbilityId)  
2.	{  
3.	    systemAbilityId_ = systemAbilityId;  
4.	  
5.	    int32_t result = ConnectService();  
6.	    if (result != TELEPHONY_SUCCESS) {  
7.	        TELEPHONY_LOGE("connect service failed,errCode: %{public}X", result);  
8.	        Timer::start(CONNECT_SERVICE_WAIT_TIME, CellularCallIpcInterfaceProxy::task);  
9.	        return;  
10.	    }  
11.	    TELEPHONY_LOGI("connected to cellular call service successfully!");  
12.	}  

当调用到cellularCallInterfacePtr_->Answer时,CellularCallInterface的Answer虚函数,会被CellularCallProxy的Answer重写,所以实际执行CellularCallProxy的Answer函数,它是一个IRemoteProxy类。而callInfo信息则转换成MessageParcel 形式通过IPC的SendRequest发出。

class CellularCallProxy : public IRemoteProxy<CellularCallInterface> { 
1.	int32_t CellularCallProxy::Answer(const CellularCallInfo &callInfo)  
2.	{  
3.	    MessageOption option;  
4.	    MessageParcel in;  
5.	    MessageParcel out;  
6.	  
7.	    if (!in.WriteInterfaceToken(CellularCallProxy::GetDescriptor())) {  
8.	        return TELEPHONY_ERR_WRITE_DESCRIPTOR_TOKEN_FAIL;  
9.	    }  
10.	    if (!in.WriteInt32(MAX_SIZE)) {  
11.	        return TELEPHONY_ERR_WRITE_DATA_FAIL;  
12.	    }  
13.	    if (!in.WriteRawData((const void *)&callInfo, sizeof(CellularCallInfo))) {  
14.	        return TELEPHONY_ERR_WRITE_DATA_FAIL;  
15.	    }  
16.	    int32_t error = Remote()->SendRequest(ANSWER, in, out, option);  
17.	    if (error == ERR_NONE) {  
18.	        return out.ReadInt32();  
19.	    }  
20.	    return error;  
21.	}  

从这里开始,Answer流程在call_manager的处理已经完成。

3.2.2.2 Answer事件在cellular_call中的处理

#过年不停更#OpenHarmony源码解析之电话子系统(call)-鸿蒙开发者社区

IPC(Inter-Process Communication)与RPC(Remote Procedure Call)机制用于实现跨进程通信,不同的是前者使用Binder驱动,用于设备内的跨进程通信,而后者使用软总线驱动,用于跨设备跨进程通信。IPC和RPC通常采用客户端-服务器(Client-Server)模型,服务请求方(Client)可获取提供服务提供方(Server)的代理 (Proxy),并通过此代理读写数据来实现进程间的数据通信。通常,Server会先注册系统能力(System Ability)到系统能力管理者(System Ability Manager,缩写SAMgr)中,SAMgr负责管理这些SA并向Client提供相关的接口。Client要和某个具体的SA通信,必须先从SAMgr中获取该SA的代理,然后使用代理和SA通信。下文使用Proxy表示服务请求方,Stub表示服务提供方。实现代码在/foundation/communication/ipc目录下。

SA注册与启动:SA需要将自己的AbilityStub实例通过AddSystemAbility接口注册到SystemAbilityManager。

SA获取与调用:通过SystemAbilityManager的GetSystemAbility方法获取到对应SA的代理IRemoteObject,然后构造AbilityProxy。这样就能保证proxy和stub的对应。

上一节我们在call_manager中看到了SA的获取过程,那这里我们来看下该SA的注册过程,还记得TELEPHONY_CELLULAR_CALL_SYS_ABILITY_ID这个systemAbilityId吗?它就是我们注册SA的关键。

1.	bool g_registerResult =  
2.	    SystemAbility::MakeAndRegisterAbility(DelayedSingleton<CellularCallService>::GetInstance().get());  
3.	  
4.	CellularCallService::CellularCallService() : SystemAbility(TELEPHONY_CELLULAR_CALL_SYS_ABILITY_ID, true)  
5.	{  
6.	    state_ = ServiceRunningState::STATE_STOPPED;  
7.	}  

这样我们就完成stub的注册,之前通过proxy发出的消息就会通过IPC传递到这里的stub中。IPC过程比较复杂,这里就不深入讨论了,有兴趣的同学可以自学一下。

由于IPCObjectStub的OnRemoteRequest是虚函数会被继承IPCObjectStub类的CellularCallStub 的OnRemoteRequest函数重写:

1.	int32_t CellularCallStub::OnRemoteRequest(  
2.	    uint32_t code, MessageParcel &data, MessageParcel &reply, MessageOption &option)  
3.	{  
4.	    std::u16string myDescriptor = CellularCallStub::GetDescriptor();  
5.	    std::u16string remoteDescriptor = data.ReadInterfaceToken();  
6.	    if (myDescriptor != remoteDescriptor) {  
7.	        TELEPHONY_LOGE("descriptor checked fail");  
8.	        return TELEPHONY_ERR_DESCRIPTOR_MISMATCH;  
9.	    }  
10.	  
11.	    auto itFunc = requestFuncMap_.find(code);  
12.	    if (itFunc != requestFuncMap_.end()) {  
13.	        auto requestFunc = itFunc->second;  
14.	        if (requestFunc != nullptr) {  
15.	            return (this->*requestFunc)(data, reply);  
16.	        }  
17.	    }  
18.	    TELEPHONY_LOGD("CellularCallStub::OnRemoteRequest, default case, need check.");  
19.	    return IPCObjectStub::OnRemoteRequest(code, data, reply, option);  
20.	}  

此处会根据map表来找到对应的处理函数,之前我们的code = 4是ANSWER,此处应该会进行对应的处理:

1.	CellularCallStub::CellularCallStub()  
2.	{  
3.	    Init();  
4.	    TELEPHONY_LOGD("CellularCallStub::CellularCallStub");  
5.	    requestFuncMap_[DIAL] = &CellularCallStub::DialInner;  
6.	    requestFuncMap_[HANG_UP] = &CellularCallStub::HangUpInner;  
7.	    requestFuncMap_[REJECT] = &CellularCallStub::RejectInner;  
8.	    requestFuncMap_[ANSWER] = &CellularCallStub::AnswerInner;  
9.	    requestFuncMap_[EMERGENCY_CALL] = &CellularCallStub::IsEmergencyPhoneNumberInner;  
10.	    requestFuncMap_[HOLD_CALL] = &CellularCallStub::HoldCallInner;  
11.	    requestFuncMap_[UN_HOLD_CALL] = &CellularCallStub::UnHoldCallInner;  
12.	    requestFuncMap_[SWITCH_CALL] = &CellularCallStub::SwitchCallInner;  
13.	    requestFuncMap_[COMBINE_CONFERENCE] = &CellularCallStub::CombineConferenceInner;  
14.	    requestFuncMap_[SEPARATE_CONFERENCE] = &CellularCallStub::SeparateConferenceInner;  
15.	    requestFuncMap_[CALL_SUPPLEMENT] = &CellularCallStub::CallSupplementInner;  
16.	    requestFuncMap_[REGISTER_CALLBACK] = &CellularCallStub::RegisterCallBackInner;  
17.	    requestFuncMap_[UNREGISTER_CALLBACK] = &CellularCallStub::UnRegisterCallBackInner;  
18.	    requestFuncMap_[START_DTMF] = &CellularCallStub::StartDtmfInner;  
19.	    requestFuncMap_[STOP_DTMF] = &CellularCallStub::StopDtmfInner;  
20.	    requestFuncMap_[SEND_DTMF] = &CellularCallStub::SendDtmfInner;  
21.	    requestFuncMap_[SEND_DTMF_STRING] = &CellularCallStub::SendDtmfStringInner;  
22.	    requestFuncMap_[SET_CALL_TRANSFER] = &CellularCallStub::SetCallTransferInner;  
23.	    requestFuncMap_[GET_CALL_TRANSFER] = &CellularCallStub::GetCallTransferInner;  
24.	    requestFuncMap_[SET_CALL_WAITING] = &CellularCallStub::SetCallWaitingInner;  
25.	    requestFuncMap_[GET_CALL_WAITING] = &CellularCallStub::GetCallWaitingInner;  
26.	    requestFuncMap_[SET_CALL_RESTRICTION] = &CellularCallStub::SetCallRestrictionInner;  
27.	    requestFuncMap_[GET_CALL_RESTRICTION] = &CellularCallStub::GetCallRestrictionInner;  
28.	    requestFuncMap_[SET_CALL_PREFERENCE_MODE] = &CellularCallStub::SetCallPreferenceModeInner;  
29.	    requestFuncMap_[GET_CALL_PREFERENCE_MODE] = &CellularCallStub::GetCallPreferenceModeInner;  
30.	    requestFuncMap_[SET_LTE_IMS_SWITCH_STATUS] = &CellularCallStub::SetLteImsSwitchStatusInner;  
31.	    requestFuncMap_[GET_LTE_IMS_SWITCH_STATUS] = &CellularCallStub::GetLteImsSwitchStatusInner;  
32.	    requestFuncMap_[CTRL_CAMERA] = &CellularCallStub::CtrlCameraInner;  
33.	    requestFuncMap_[SET_PREVIEW_WINDOW] = &CellularCallStub::SetPreviewWindowInner;  
34.	    requestFuncMap_[SET_DISPLAY_WINDOW] = &CellularCallStub::SetDisplayWindowInner;  
35.	    requestFuncMap_[SET_CAMERA_ZOOM] = &CellularCallStub::SetCameraZoomInner;  
36.	    requestFuncMap_[SET_PAUSE_IMAGE] = &CellularCallStub::SetPauseImageInner;  
37.	    requestFuncMap_[SET_DEVICE_DIRECTION] = &CellularCallStub::SetDeviceDirectionInner;  
38.	} 

AnswerInner会进行后续的处理,这里会从data中解析出对应的callinfo信息,在函数末尾出调用本文件的Answer(),并将返回的结果写入reply中,这个返回值是函数执行的结果,为Int32整形值。

1.	int32_t CellularCallStub::AnswerInner(MessageParcel &data, MessageParcel &reply)  
2.	{  
3.	    TELEPHONY_LOGD("CellularCallStub::AnswerInner ANSWER");  
4.	    int32_t size = data.ReadInt32();  
5.	    TELEPHONY_LOGD("CellularCallStub::OnRemoteRequest:size=%{public}u, MAX_SIZE=%{public}u\n", size, MAX_SIZE);  
6.	    size = ((size > MAX_SIZE) ? 0 : size);  
7.	    if (size <= 0) {  
8.	        TELEPHONY_LOGE("CellularCallStub::OnRemoteRequest data size error");  
9.	        return TELEPHONY_ERR_FAIL;  
10.	    }  
11.	    auto pCallInfo = (CellularCallInfo *)data.ReadRawData(sizeof(CellularCallInfo));  
12.	    if (pCallInfo == nullptr) {  
13.	        TELEPHONY_LOGE("AnswerInner return, pCallInfo is nullptr.");  
14.	        return TELEPHONY_ERR_ARGUMENT_INVALID;  
15.	    }  
16.	    reply.WriteInt32(Answer(*pCallInfo));  
17.	    return TELEPHONY_SUCCESS;  
18.	}  

Answer函数是来完成跟ril交互的关键。首先根据callInfo的信息获得calltype,目前支持三种calltype,分别为cs_call、ims_call、ott_call。在确定calltype后,通过slotId_在GetCsControl()获得对应的CSControl对象。

1.	int32_t CellularCallStub::Answer(const CellularCallInfo &callInfo)  
2.	{  
3.	    if (!IsValidSlotId(callInfo.slotId)) {  
4.	        TELEPHONY_LOGE("CellularCallStub::Answer return, invalid slot id");  
5.	        return CALL_ERR_INVALID_SLOT_ID;  
6.	    }  
7.	    if (CallType::TYPE_CS == callInfo.callType) {  
8.	        auto control = GetCsControl(slotId_);  
9.	        if (control == nullptr) {  
10.	            TELEPHONY_LOGE("CellularCallStub::Answer return, control is nullptr");  
11.	            return TELEPHONY_ERR_LOCAL_PTR_NULL;  
12.	        }  
13.	        return control->Answer(callInfo);  
14.	    } else if (CallType::TYPE_IMS == callInfo.callType) {  
15.	        auto imsControl = GetImsControl(slotId_);  
16.	        if (imsControl == nullptr) {  
17.	            TELEPHONY_LOGE("CellularCallStub::Answer return, imsControl is nullptr");  
18.	            return TELEPHONY_ERR_LOCAL_PTR_NULL;  
19.	        }  
20.	        return imsControl->Answer(callInfo);  
21.	    }  
22.	    TELEPHONY_LOGE("CellularCallStub::Answer return, call type error.");  
23.	    return TELEPHONY_ERR_ARGUMENT_INVALID;  
24.	}  

这个调用到CSControl::Answer函数,会根据callInfo拿到对应的CellularCallConnectionCS 和CALL_STATUS,状态为来电、响铃、等待状态,则会调用CellularCallConnectionCS 对应的函数。

1.	int32_t CSControl::Answer(const CellularCallInfo &callInfo)  
2.	{  
3.	    auto pConnection =  
4.	        GetConnectionData<CsConnectionMap &, CellularCallConnectionCS *>(connectionMap_, callInfo.phoneNum);  
5.	    if (pConnection == nullptr) {  
6.	        TELEPHONY_LOGI("Answer: connection cannot be matched, use index directly");  
7.	        pConnection =  
8.	            FindConnectionByIndex<CsConnectionMap &, CellularCallConnectionCS *>(connectionMap_, callInfo.index);  
9.	    }  
10.	    if (pConnection == nullptr) {  
11.	        TELEPHONY_LOGE("Answer return, error type: connection is null");  
12.	        return CALL_ERR_CALL_CONNECTION_NOT_EXIST;  
13.	    }  
14.	  
15.	    /** 
16.	     * <stat> (state of the call): 
17.	     * 0 active 
18.	     * 1 held 
19.	     * 2 dialing (MO call) 
20.	     * 3 alerting (MO call) 
21.	     * 4 incoming (MT call) 
22.	     * 5 waiting (MT call) 
23.	     */  
24.	    // There is an active call when you call, or third party call waiting  
25.	    if (IsInState(connectionMap_, CALL_STATUS_ACTIVE) || pConnection->GetStatus() == CALL_STATUS_WAITING) {  
26.	        TELEPHONY_LOGD("Answer there is an active call when you call, or third party call waiting");  
27.	        auto con =  
28.	            FindConnectionByState<CsConnectionMap &, CellularCallConnectionCS *>(connectionMap_, CALL_STATUS_ACTIVE);  
29.	        if (con != nullptr) {  
30.	            /** 
31.	             * shows commands to start the call, to switch from voice to data (In Call Modification) and to hang up 
32.	             * the call. +CMOD and +FCLASS commands indicate the current settings before dialling or answering 
33.	             * command, not that they shall be given just before D or A command. 
34.	             */  
35.	            TELEPHONY_LOGD("Answer: There is an active session currently, and it needs to hold");  
36.	            con->SwitchCallRequest(GetSlotId());  
37.	        } else {  
38.	            TELEPHONY_LOGE("Answer return, error type: con is null, there are no active calls");  
39.	        }  
40.	    }  
41.	    if (pConnection->GetStatus() == CALL_STATUS_INCOMING || pConnection->GetStatus() == CALL_STATUS_ALERTING ||  
42.	        pConnection->GetStatus() == CALL_STATUS_WAITING) {  
43.	        return pConnection->AnswerRequest(GetSlotId());  
44.	    }  
45.	  
46.	    TELEPHONY_LOGE("CSControl::Answer return, error type: call state error, phone not ringing.");  
47.	    return CALL_ERR_CALL_STATE;  
48.	}  

后续会调用CellularCallConnectionCS::AnswerRequest和core_service进行交互了。GetCore会根据slotId得到对应的Core,然后用Get函数得到对应的的event,设置Owner为CellularCallHandler,等待返回时进行回调对应的handler。

1.	int32_t CellularCallConnectionCS::AnswerRequest(int32_t slotId)  
2.	{  
3.	    auto core = GetCore(slotId);  
4.	    if (core == nullptr) {  
5.	        TELEPHONY_LOGE("AnswerRequest return, error type: core is nullptr.");  
6.	        return CALL_ERR_RESOURCE_UNAVAILABLE;  
7.	    }  
8.	    auto event = AppExecFwk::InnerEvent::Get(ObserverHandler::RADIO_ACCEPT_CALL);  
9.	    if (event == nullptr) {  
10.	        TELEPHONY_LOGE("AnswerRequest return, error type: event is nullptr.");  
11.	        return CALL_ERR_RESOURCE_UNAVAILABLE;  
12.	    }  
13.	    if (DelayedSingleton<CellularCallService>::GetInstance() == nullptr) {  
14.	        TELEPHONY_LOGE("AnswerRequest return, error type: GetInstance() is nullptr.");  
15.	        return CALL_ERR_RESOURCE_UNAVAILABLE;  
16.	    }  
17.	    event->SetOwner(DelayedSingleton<CellularCallService>::GetInstance()->GetHandler(slotId));  
18.	    core->Answer(event);  
19.	    return TELEPHONY_SUCCESS;  
20.	}  

这里就完成了Answer流程在cellular_call的调用。

3.2.2.3 Answer事件在core_servicel中的处理

#过年不停更#OpenHarmony源码解析之电话子系统(call)-鸿蒙开发者社区

这部分的代码会调用core对应的Answer函数,消息就会传递到负责核心服务与RIL Adapter通信交互的tel_ril代码中。

1.	void Core::Answer(const AppExecFwk::InnerEvent::Pointer &result)  
2.	{  
3.	    if (telRilManager_ == nullptr) {  
4.	        TELEPHONY_LOGE("telRilManager is null!");  
5.	        return;  
6.	    }  
7.	    telRilManager_->Answer(result);  
8.	}  

进一步到负责tel_ril的管理类TelRilManager中,判断下对应的telRilCall_是否为空,如果不为空就可以继续调用了。

1.	void TelRilManager::Answer(const AppExecFwk::InnerEvent::Pointer &result)  
2.	{  
3.	    if (telRilCall_ != nullptr) {  
4.	        telRilCall_->Answer(result);  
5.	    } else {  
6.	        TELEPHONY_LOGE("telRilCall_ is null");  
7.	    }  
8.	}  

telRilCall_是在TelRilManager的初始化中进行的。其中InitCellularRadio首先通过ServiceManager拿到cellular_radio1对应的service。然后在InitTelInfo中用cellularRadio_和observerHandler_构造telRilCall_。

1.	bool TelRilManager::OnInit()  
2.	{  
3.	    bool res = false;  
4.	    int i = 0;  
5.	  
6.	    do {  
7.	        res = InitCellularRadio(true);  
8.	        if (!res) {  
9.	            i++;  
10.	            sleep(1);  
11.	            TELEPHONY_LOGD("Initialization cellular radio failed. Try initialization again!");  
12.	        } else {  
13.	            InitTelInfo();  
14.	        }  
15.	    } while (!res && (i < RIL_INIT_COUNT_MAX));  
16.	    return res;  
17.	}  

调用到teRilCall的Answer函数进行处理,用CreateTelRilRequest构造telRilRequest ,通过TelRilBase的SendInt32Event发出。

1.	void TelRilCall::Answer(const AppExecFwk::InnerEvent::Pointer &result)  
2.	{  
3.	    std::shared_ptr<TelRilRequest> telRilRequest = CreateTelRilRequest(HREQ_CALL_ANSWER, result);  
4.	    if (telRilRequest == nullptr) {  
5.	        TELEPHONY_LOGE("telRilRequest is nullptr");  
6.	        return;  
7.	    }  
8.	    if (cellularRadio_ == nullptr) {  
9.	        TELEPHONY_LOGE("%{public}s  cellularRadio_ == nullptr", __func__);  
10.	        ErrorResponse(telRilRequest->serialId_, HRilErrType::HRIL_ERR_GENERIC_FAILURE);  
11.	        return;  
12.	    }  
13.	  
14.	    int ret = SendInt32Event(HREQ_CALL_ANSWER, telRilRequest->serialId_);  
15.	    TELEPHONY_LOGD("HREQ_CALL_ANSWER ret %{public}d", ret);  
16.	}  

从调用的情况来看,则又通过IPC的SendRequest请求将dispatchId和data发给cellular_radio1。

1.	int32_t TelRilBase::SendInt32Event(int32_t dispatchId, int32_t value)  
2.	{  
3.	    int status = 0;  
4.	    if (cellularRadio_ != nullptr) {  
5.	        MessageParcel data;  
6.	        MessageParcel reply;  
7.	        data.WriteInt32(value);  
8.	        OHOS::MessageOption option = {OHOS::MessageOption::TF_ASYNC};  
9.	        status = cellularRadio_->SendRequest(dispatchId, data, reply, option);  
10.	        TELEPHONY_LOGD("TelRilBase SendInt32Event, dispatchId:%{public}d, status:%{public}d", dispatchId, status);  
11.	    } else {  
12.	        TELEPHONY_LOGE("cellularRadio_ is nullptr!!!");  
13.	    }  
14.	    return status;  
15.	}  

到这里Answer流程在core_service服务中的处理就完毕了。

3.2.2.4 Answer事件在ril_adapter中的处理

#过年不停更#OpenHarmony源码解析之电话子系统(call)-鸿蒙开发者社区

从目前的代码调用来看有可能是从vendor调用hril_hdf的dispatch,然后传递消息到ril_adapter中

下边为hril_hdf的初始化过程:

1.	struct HdfDriverEntry g_rilAdapterDevEntry = {  
2.	    .moduleVersion = 1,  
3.	    .moduleName = MODULE_NAME,  
4.	    .Bind = RilAdapterBind,  
5.	    .Init = RilAdapterInit,  
6.	    .Release = RilAdapterRelease,  
7.	};  
8.	HDF_INIT(g_rilAdapterDevEntry);  

可以看到hdf的初始化包括MODULE_ NAME和RilAdapterBind,RilAdapterInit。

Bind过程如下:

1.	static int32_t RilAdapterBind(struct HdfDeviceObject *device)  
2.	{  
3.	    if (device == NULL) {  
4.	        return HDF_ERR_INVALID_OBJECT;  
5.	    }  
6.	    device->service = &g_rilAdapterService;  
7.	    return HDF_SUCCESS;  
8.	}  

可以看看到service的dispatch对应于RilAdapterDispatch:

1.	static struct IDeviceIoService g_rilAdapterService = {  
2.	    .Dispatch = RilAdapterDispatch,  
3.	    .Open = NULL,  
4.	    .Release = NULL,  
5.	};  

Init过程如下:

1.	static int32_t RilAdapterInit(struct HdfDeviceObject *device)  
2.	{  
3.	    if (device == NULL) {  
4.	        return HDF_ERR_INVALID_OBJECT;  
5.	    }  
6.	    DFX_InstallSignalHandler();  
7.	    TELEPHONY_LOGD("Start %{public}s hdf service!", HdfDeviceGetServiceName(device));  
8.	    struct HdfSBuf *sbuf = HdfSBufTypedObtain(SBUF_IPC);  
9.	    if (sbuf == NULL) {  
10.	        TELEPHONY_LOGE("HdfSampleDriverBind, failed to obtain ipc sbuf");  
11.	        return HDF_ERR_INVALID_OBJECT;  
12.	    }  
13.	    if (!HdfSbufWriteString(sbuf, "string")) {  
14.	        TELEPHONY_LOGE("HdfSampleDriverBind, failed to write string to ipc sbuf");  
15.	        HdfSBufRecycle(sbuf);  
16.	        return HDF_FAILURE;  
17.	    }  
18.	    if (sbuf != NULL) {  
19.	        HdfSBufRecycle(sbuf);  
20.	    }  
21.	    TELEPHONY_LOGD("sbuf IPC obtain test success!");  
22.	    if (!IsLoadedVendorLib()) {  
23.	        LoadVendor();  
24.	    } else {  
25.	        TELEPHONY_LOGI("The vendor library has been loaded!");  
26.	    }  
27.	    return HDF_SUCCESS;  
28.	}  

其中LoadVendor会加载对应vendor的rilLib

1.	static void LoadVendor(void)  
2.	{  
3.	    const char *rilLibPath = NULL;  
4.	    char vendorLibPath[PARAMETER_ZISE] = {0};  
5.	    // Pointer to ril init function in vendor ril  
6.	    const HRilOps *(*rilInitOps)(const struct HRilReport *) = NULL;  
7.	    // functions returned by ril init function in vendor ril  
8.	    const HRilOps *ops = NULL;  
9.	  
10.	    if (GetVendorLibPath(vendorLibPath) == HDF_SUCCESS) {  
11.	        rilLibPath = vendorLibPath;  
12.	    } else {  
13.	        rilLibPath = g_modem_list[MODEM_INDEX].path;  
14.	    }  
15.	    if (rilLibPath == NULL) {  
16.	        TELEPHONY_LOGE("dynamic library path is empty");  
17.	        return;  
18.	    }  
19.	    TELEPHONY_LOGD("RilInit LoadVendor start with rilLibPath:%{public}s", rilLibPath);  
20.	    g_dlHandle = dlopen(rilLibPath, RTLD_NOW);  
21.	    if (g_dlHandle == NULL) {  
22.	        TELEPHONY_LOGE("dlopen %{public}s is fail. %{public}s", rilLibPath, dlerror());  
23.	        return;  
24.	    }  
25.	    rilInitOps = (const HRilOps *(*)(const struct HRilReport *))dlsym(g_dlHandle, "RilInitOps");  
26.	    if (rilInitOps == NULL) {  
27.	        dlclose(g_dlHandle);  
28.	        TELEPHONY_LOGE("RilInit not defined or exported");  
29.	        return;  
30.	    }  
31.	    ops = rilInitOps(&g_reportOps);  
32.	    TELEPHONY_LOGD("RilInit completed");  
33.	    HRilRegOps(ops);  
34.	}  

之前已经说到有可能是vendor调用了dispatch,现在已经绑定了对应的RilAdapterDispatch,后续处理如下:

C++写的IPC和C语言写的IPC是可以相互通信的,格式并不同。HdfSBuf也可以和MessageParcel互转。

1.	static int32_t RilAdapterDispatch(  
2.	    struct HdfDeviceIoClient *client, int32_t cmd, struct HdfSBuf *data, struct HdfSBuf *reply)  
3.	{  
4.	    int32_t ret;  
5.	    static pthread_mutex_t dispatchMutex = PTHREAD_MUTEX_INITIALIZER;  
6.	    pthread_mutex_lock(&dispatchMutex);  
7.	    TELEPHONY_LOGD("RilAdapterDispatch cmd:%{public}d", cmd);  
8.	    ret = DispatchRequest(SLOTID, cmd, data);  
9.	    pthread_mutex_unlock(&dispatchMutex);  
10.	    return ret;  
11.	}  

Request消息通过IPC传到RilAdapter后会通过DispatchRequest分发出去:

1.	int32_t DispatchRequest(int32_t slotId, int32_t cmd, struct HdfSBuf *data)  
2.	{  
3.	    if (data == nullptr) {  
4.	        TELEPHONY_LOGE("miss callback parameter");  
5.	        return HDF_ERR_INVALID_PARAM;  
6.	    }  
7.	    switch (cmd) {  
8.	        case HRIL_ADAPTER_RADIO_INDICATION: {  
9.	            HdfRemoteService *serviceCallbackInd = HdfSBufReadRemoteService(data);  
10.	            if (serviceCallbackInd == nullptr) {  
11.	                TELEPHONY_LOGE("miss callback parameter");  
12.	                return HDF_ERR_INVALID_PARAM;  
13.	            }  
14.	            RegisterManagerNotifyCallback(slotId, serviceCallbackInd);  
15.	            break;  
16.	        }  
17.	        case HRIL_ADAPTER_RADIO_RESPONSE: {  
18.	            HdfRemoteService *serviceCallback = HdfSBufReadRemoteService(data);  
19.	            if (serviceCallback == nullptr) {  
20.	                TELEPHONY_LOGE("miss callback parameter");  
21.	                return HDF_ERR_INVALID_PARAM;  
22.	            }  
23.	            RegisterManagerResponseCallback(slotId, serviceCallback);  
24.	            break;  
25.	        }  
26.	        default:  
27.	            DispatchModule(slotId, cmd, data);  
28.	    }  
29.	    return HDF_SUCCESS;  
30.	}  

在后续调用中我们知道code为HREQ_CALL_ANSWER,所以会调用DispatchModule:

1.	static void DispatchModule(int32_t slotId, int32_t cmd, struct HdfSBuf *data)  
2.	{  
3.	    auto itFunc = g_manager.find(slotId);  
4.	    if (itFunc != g_manager.end()) {  
5.	        auto &manager = itFunc->second;  
6.	        if (manager != nullptr) {  
7.	            int32_t ret = manager->Dispatch(slotId, cmd, data);  
8.	            if (ret != HDF_SUCCESS) {  
9.	                TELEPHONY_LOGE("HRilManager::Dispatch is failed!");  
10.	            }  
11.	        } else {  
12.	            TELEPHONY_LOGE("Manager is nullptr, id:%{public}d, addr:%{public}p!", slotId, &manager);  
13.	        }  
14.	    } else {  
15.	        TELEPHONY_LOGE("Can not find slotId in g_manager: %{public}d!", slotId);  
16.	    }  
17.	}  

根据slotId拿到对应g_manager的Dispatch函数:

1.	int32_t HRilManager::Dispatch(int32_t slotId, int32_t code, struct HdfSBuf *data)  
2.	{  
3.	    if (hrilCall_ != nullptr && hrilCall_->IsCallRespOrNotify(code)) {  
4.	        hrilCall_->ProcessCallRequest(slotId, code, data);  
5.	        return HDF_SUCCESS;  
6.	    }  
7.	    if (hrilSms_ != nullptr && hrilSms_->IsSmsRespOrNotify(code)) {  
8.	        hrilSms_->ProcessSmsRequest(slotId, code, data);  
9.	        return HDF_SUCCESS;  
10.	    }  
11.	    if (hrilSim_ != nullptr && hrilSim_->IsSimRespOrNotify(code)) {  
12.	        hrilSim_->ProcessSimRequest(slotId, code, data);  
13.	        return HDF_SUCCESS;  
14.	    }  
15.	    if (hrilNetwork_ != nullptr && hrilNetwork_->IsNetworkRespOrNotify(code)) {  
16.	        hrilNetwork_->ProcessNetworkRequest(slotId, code, data);  
17.	        return HDF_SUCCESS;  
18.	    }  
19.	    if (hrilModem_ != nullptr && hrilModem_->IsModemRespOrNotify(code)) {  
20.	        hrilModem_->ProcessCommonRequest(slotId, code, data);  
21.	        return HDF_SUCCESS;  
22.	    }  
23.	    if (hrilData_ != nullptr && hrilData_->IsDataRespOrNotify(code)) {  
24.	        hrilData_->ProcessDataRequest(slotId, code, data);  
25.	        return HDF_SUCCESS;  
26.	    }  
27.	    return HDF_FAILURE;  
28.	}  

由于我们的code = 4,在0到100之间,所以会被判定为callrequest

所以调用如下函数:

1.	void HRilCall::ProcessCallRequest(int32_t slotId, int32_t code, struct HdfSBuf *data)  
2.	{  
3.	    auto itFunc = reqMemberFuncMap_.find(code);  
4.	    if (itFunc != reqMemberFuncMap_.end()) {  
5.	        auto memberFunc = itFunc->second;  
6.	        if (memberFunc != nullptr) {  
7.	            (this->*memberFunc)(slotId, data);  
8.	        }  
9.	    } else {  
10.	        TELEPHONY_LOGE("Can not find CallRequest code in reqMemberFuncMap_!");  
11.	    }  
12.	}  

Request如下:

1.	// request  
2.	    reqMemberFuncMap_[HREQ_CALL_GET_CALL_LIST] = &HRilCall::GetCallList;  
3.	    reqMemberFuncMap_[HREQ_CALL_DIAL] = &HRilCall::Dial;  
4.	    reqMemberFuncMap_[HREQ_CALL_HANGUP] = &HRilCall::Hangup;  
5.	    reqMemberFuncMap_[HREQ_CALL_REJECT] = &HRilCall::Reject;  
6.	    reqMemberFuncMap_[HREQ_CALL_ANSWER] = &HRilCall::Answer;  
7.	    reqMemberFuncMap_[HREQ_CALL_HOLD_CALL] = &HRilCall::HoldCall;  
8.	    reqMemberFuncMap_[HREQ_CALL_UNHOLD_CALL] = &HRilCall::UnHoldCall;  
9.	    reqMemberFuncMap_[HREQ_CALL_SWITCH_CALL] = &HRilCall::SwitchCall;  
10.	    reqMemberFuncMap_[HREQ_CALL_GET_CLIP] = &HRilCall::GetClip;  
11.	    reqMemberFuncMap_[HREQ_CALL_SET_CLIP] = &HRilCall::SetClip;  
12.	    reqMemberFuncMap_[HREQ_CALL_COMBINE_CONFERENCE] = &HRilCall::CombineConference;  
13.	    reqMemberFuncMap_[HREQ_CALL_SEPARATE_CONFERENCE] = &HRilCall::SeparateConference;  
14.	    reqMemberFuncMap_[HREQ_CALL_CALL_SUPPLEMENT] = &HRilCall::CallSupplement;  
15.	    reqMemberFuncMap_[HREQ_CALL_GET_CALL_WAITING] = &HRilCall::GetCallWaiting;  
16.	    reqMemberFuncMap_[HREQ_CALL_SET_CALL_WAITING] = &HRilCall::SetCallWaiting;  
17.	    reqMemberFuncMap_[HREQ_CALL_GET_CALL_TRANSFER_INFO] = &HRilCall::GetCallTransferInfo;  
18.	    reqMemberFuncMap_[HREQ_CALL_SET_CALL_TRANSFER_INFO] = &HRilCall::SetCallTransferInfo;  
19.	    reqMemberFuncMap_[HREQ_CALL_GET_CALL_RESTRICTION] = &HRilCall::GetCallRestriction;  
20.	    reqMemberFuncMap_[HREQ_CALL_SET_CALL_RESTRICTION] = &HRilCall::SetCallRestriction;  
21.	    reqMemberFuncMap_[HREQ_CALL_GET_CLIR] = &HRilCall::GetClir;  
22.	    reqMemberFuncMap_[HREQ_CALL_SET_CLIR] = &HRilCall::SetClir;  
23.	    reqMemberFuncMap_[HREQ_CALL_START_DTMF] = &HRilCall::StartDtmf;  
24.	    reqMemberFuncMap_[HREQ_CALL_SEND_DTMF] = &HRilCall::SendDtmf;  
25.	    reqMemberFuncMap_[HREQ_CALL_STOP_DTMF] = &HRilCall::StopDtmf;  
26.	    reqMemberFuncMap_[HREQ_CALL_GET_IMS_CALL_LIST] = &HRilCall::GetImsCallList;  
27.	    reqMemberFuncMap_[HREQ_CALL_GET_CALL_PREFERENCE] = &HRilCall::GetCallPreferenceMode;  
28.	    reqMemberFuncMap_[HREQ_CALL_SET_CALL_PREFERENCE] = &HRilCall::SetCallPreferenceMode;  
29.	    reqMemberFuncMap_[HREQ_CALL_GET_LTEIMSSWITCH_STATUS] = &HRilCall::GetLteImsSwitchStatus;  
30.	    reqMemberFuncMap_[HREQ_CALL_SET_LTEIMSSWITCH_STATUS] = &HRilCall::SetLteImsSwitchStatus;  

调用request对应处理函数为Answer:

1.	void HRilCall::Answer(int32_t slotId, struct HdfSBuf *data)  
2.	{  
3.	    int32_t serial = 0;  
4.	    if (!HdfSbufReadInt32(data, &serial)) {  
5.	        TELEPHONY_LOGE("miss serial parameter");  
6.	        return;  
7.	    }  
8.	    ReqDataInfo *requestInfo = CreateHRilRequest(serial, slotId, HREQ_CALL_ANSWER);  
9.	    if (requestInfo == nullptr) {  
10.	        TELEPHONY_LOGE("RilAdapter failed to do Create Answer HRilRequest!");  
11.	        return;  
12.	    }  
13.	    if (callFuncs_ == nullptr) {  
14.	        TELEPHONY_LOGE("RilAdapter HRilCall::Dial  callFuncs_ is nullptr!");  
15.	        SafeFrees(requestInfo);  
16.	        return;  
17.	    }  
18.	    callFuncs_->Answer(requestInfo);  
19.	    SafeFrees(requestInfo);  
20.	}  

首先要创建HRilRequest,将信息封装进requestInfo中。

1.	ReqDataInfo *CreateHRilRequest(int32_t serial, int32_t slotId, int32_t request)  
2.	{  
3.	    ReqDataInfo *requestInfo = nullptr;  
4.	    HRilSimSlotId simSlotId = (HRilSimSlotId)slotId;  
5.	    requestInfo = (ReqDataInfo *)calloc(1, sizeof(ReqDataInfo));  
6.	    if (requestInfo == nullptr) {  
7.	        return nullptr;  
8.	    }  
9.	    requestInfo->slotId = simSlotId;  
10.	    requestInfo->request = request;  
11.	    requestInfo->serial = serial;  
12.	    return requestInfo;  
13.	}  

在LoadVendor时调用HRilRegOps(ops)时会进行register

1.	void HRilRegOps(const HRilOps *hrilOps)  
2.	{  
3.	    int i;  
4.	    if (hrilOps == nullptr) {  
5.	        TELEPHONY_LOGE("HRilRegOps: HRilRegOps * nullptr");  
6.	        return;  
7.	    }  
8.	    if (rilRegisterStatus > RIL_REGISTER_IS_NONE) {  
9.	        TELEPHONY_LOGE("HRilRegOps is running!!!!");  
10.	        return;  
11.	    }  
12.	    rilRegisterStatus = RIL_REGISTER_IS_RUNNING;  
13.	    vendorLibLoadStatus = RIL_REGISTER_IS_RUNNING;  
14.	    (void)memcpy_s(&g_callBacks, sizeof(HRilOps), hrilOps, sizeof(HRilOps));  
15.	  
16.	    for (i = HRIL_SIM_SLOT_1; i < HRIL_SIM_SLOT_NUM; i++) {  
17.	        g_manager[i] = std::make_unique<HRilManager>();  
18.	        if (g_callBacks.smsOps != nullptr) {  
19.	            g_manager[i]->RegisterSmsFuncs(g_callBacks.smsOps);  
20.	        }  
21.	        if (g_callBacks.callOps != nullptr) {  
22.	            g_manager[i]->RegisterCallFuncs(g_callBacks.callOps);  
23.	        }  
24.	        if (g_callBacks.dataOps != nullptr) {  
25.	            g_manager[i]->RegisterDataFuncs(g_callBacks.dataOps);  
26.	        }  
27.	        if (g_callBacks.modemOps != nullptr) {  
28.	            g_manager[i]->RegisterModemFuncs(g_callBacks.modemOps);  
29.	        }  
30.	        if (g_callBacks.networkOps != nullptr) {  
31.	            g_manager[i]->RegisterNetworkFuncs(g_callBacks.networkOps);  
32.	        }  
33.	        if (g_callBacks.simOps != nullptr) {  
34.	            g_manager[i]->RegisterSimFuncs(g_callBacks.simOps);  
35.	        }  
36.	    }  
37.	}  

这里会将callOps注册到callFuncs_中,而g_callBacks实际是对应hrilOps:

1.	HRilOps g_hrilOps = {  
2.	    .callOps = &g_callReqOps,  
3.	    .simOps = &g_simReqOps,  
4.	    .smsOps = &g_smsReqOps,  
5.	    .networkOps = &g_networkReqOps,  
6.	    .dataOps = &g_dataReqOps,  
7.	    .modemOps = &g_modemReqOps,  
8.	};  

.callOps 对应于 &g_callReqOps,对应的函数对照表如下

1.	static const HRilCallReq g_callReqOps = {  
2.	    .GetCallList = ReqGetCallList,  
3.	    .Dial = ReqDial,  
4.	    .Hangup = ReqHangup,  
5.	    .Reject = ReqReject,  
6.	    .Answer = ReqAnswer,  
7.	    .GetClip = ReqGetClip,  
8.	    .SetClip = ReqSetClip,  
9.	    .HoldCall = ReqHoldCall,  
10.	    .UnHoldCall = ReqUnHoldCall,  
11.	    .SwitchCall = ReqSwitchCall,  
12.	    .CombineConference = ReqCombineConference,  
13.	    .SeparateConference = ReqSeparateConference,  
14.	    .CallSupplement = ReqCallSupplement,  
15.	    .GetCallWaiting = ReqGetCallWaiting,  
16.	    .SetCallWaiting = ReqSetCallWaiting,  
17.	    .GetCallTransferInfo = ReqGetCallTransferInfo,  
18.	    .SetCallTransferInfo = ReqSetCallTransferInfo,  
19.	    .GetCallRestriction = ReqGetCallRestriction,  
20.	    .SetCallRestriction = ReqSetCallRestriction,  
21.	    .GetClir = ReqGetClir,  
22.	    .SetClir = ReqSetClir,  
23.	    .StartDtmf = ReqStartDtmf,  
24.	    .SendDtmf = ReqSendDtmf,  
25.	    .StopDtmf = ReqStopDtmf,  
26.	    .GetImsCallList = ReqGetImsCallList,  
27.	    .GetCallPreferenceMode = ReqGetCallPreferenceMode,  
28.	    .SetCallPreferenceMode = ReqSetCallPreferenceMode,  
29.	    .GetLteImsSwitchStatus = ReqGetLteImsSwitchStatus,  
30.	    .SetLteImsSwitchStatus = ReqSetLteImsSwitchStatus,  
31.	}; 

找对对应的ReqAnswer进行后续处理,这里其实已经走到AT命令的处理层:

1.	void ReqAnswer(const ReqDataInfo *requestInfo)  
2.	{  
3.	    int32_t ret;  
4.	    int32_t err = HRIL_ERR_SUCCESS;  
5.	    struct ReportInfo reportInfo = {0};  
6.	    ResponseInfo *pResponse = NULL;  
7.	  
8.	    ret = SendCommandLock("ATA", NULL, 0, &pResponse);  
9.	    if (ret != HRIL_ERR_SUCCESS || !pResponse->success) {  
10.	        err = HRIL_ERR_GENERIC_FAILURE;  
11.	    }  
12.	    reportInfo = CreateReportInfo(requestInfo, err, HRIL_RESPONSE, 0);  
13.	    OnCallReport(HRIL_SIM_SLOT_1, reportInfo, NULL, 0);  
14.	    FreeResponseInfo(pResponse);  
15.	}  

后续调用SendCommandLock函数,这里就会将requestInfo处理成AT命令:

1.	int SendCommandLock(const char *command, const char *prefix, long long timeout, ResponseInfo **outResponse)  
2.	{  
3.	    const char *atCmd = "AT";  
4.	    int err;  
5.	    if (pthread_equal(g_reader, pthread_self()) != 0) {  
6.	        TELEPHONY_LOGE("The read thread prohibits sending commands.");  
7.	        return AT_ERR_INVALID_THREAD;  
8.	    }  
9.	  
10.	    TELEPHONY_LOGD("command %{public}s, NeedATPause:%{public}d, atCmd:%{public}s", command, g_isNeedATPause, atCmd);  
11.	    pthread_mutex_lock(&g_commandmutex);  
12.	    if (g_isNeedATPause) {  
13.	        pthread_cond_signal(&g_commandcond);  
14.	        err = SendCommandNoLock(atCmd, timeout, outResponse);  
15.	        if (err != 0) {  
16.	            TELEPHONY_LOGD("NeedATPause err = %{public}d cmd:%{public}s", err, command);  
17.	        }  
18.	        if (g_atWatch != NULL) {  
19.	            g_atWatch();  
20.	        }  
21.	        g_isNeedATPause = false;  
22.	        alarm(0);  
23.	    }  
24.	    g_prefix = prefix;  
25.	    err = SendCommandNoLock(command, timeout, outResponse);  
26.	    pthread_mutex_unlock(&g_commandmutex);  
27.	    TELEPHONY_LOGD("err = %{public}d, cmd:%{public}s", err, command);  
28.	    // when timeout to process  
29.	    if (err == AT_ERR_TIMEOUT && g_onTimeout != NULL) {  
30.	        g_onTimeout();  
31.	    } else if (err == AT_ERR_GENERIC) {  
32.	        TELEPHONY_LOGD("OnReaderClosed() err = %{public}d", err);  
33.	        OnReaderClosed();  
34.	    }  
35.	    return err;  
36.	}  

实际的处理在这个函数SendCommandNoLock:

1.	int SendCommandNoLock(const char *command, long long timeout, ResponseInfo **outResponse)  
2.	{  
3.	    long long defaultTimeout = 50000;  
4.	    int err = 0;  
5.	    struct timespec time;  
6.	    if (g_response != NULL) {  
7.	        err = AT_ERR_COMMAND_PENDING;  
8.	        TELEPHONY_LOGE("g_response is not null, so the command cannot be sent.");  
9.	        ClearCurCommand();  
10.	        return err;  
11.	    }  
12.	    g_response = (ResponseInfo *)calloc(1, sizeof(ResponseInfo));  
13.	    if (g_response == NULL) {  
14.	        err = AT_ERR_GENERIC;  
15.	        TELEPHONY_LOGE("g_response calloc is fail, err:%{public}d.", err);  
16.	        ClearCurCommand();  
17.	        return err;  
18.	    }  
19.	    err = WriteATCommand(command, 0, g_atFd);  
20.	    if (err != VENDOR_SUCCESS) {  
21.	        TELEPHONY_LOGE("send AT cmd is fail, err:%{public}d.", err);  
22.	        ClearCurCommand();  
23.	        return err;  
24.	    }  
25.	    SetWaitTimeout(&time, (timeout != 0) ? timeout : defaultTimeout);  
26.	    while (g_response->result == NULL && g_readerClosed == 0) {  
27.	        err = pthread_cond_timedwait(&g_commandcond, &g_commandmutex, &time);  
28.	        if (err == ETIMEDOUT) {  
29.	            err = AT_ERR_TIMEOUT;  
30.	            TELEPHONY_LOGE("pthread cond timedwait is timeout, err:%{public}d.", err);  
31.	            ClearCurCommand();  
32.	            return err;  
33.	        }  
34.	    }  
35.	    if (outResponse == NULL) {  
36.	        FreeResponseInfo((ResponseInfo *)g_response);  
37.	    } else {  
38.	        *outResponse = (ResponseInfo *)g_response;  
39.	    }  
40.	    g_response = NULL;  
41.	    if (g_readerClosed > 0) {  
42.	        err = AT_ERR_CHANNEL_CLOSED;  
43.	        TELEPHONY_LOGE("g_readerClosed is closed, err:%{public}d.", err);  
44.	        ClearCurCommand();  
45.	        return err;  
46.	    }  
47.	    err = 0;  
48.	    return err;  
49.	} 

通过WriteATCommand将AT命令发给modem,然后等待modem回复处理的结果:

1.	int WriteATCommand(const char *s, int isPdu, int atFd)  
2.	{  
3.	    TELEPHONY_LOGD("cmd:%{public}s", s);  
4.	    ssize_t ret;  
5.	    size_t i = 0;  
6.	    size_t len = strlen(s);  
7.	    if (atFd < 0) {  
8.	        return AT_ERR_CHANNEL_CLOSED;  
9.	    }  
10.	  
11.	    while (i < len) {  
12.	        do {  
13.	            ret = write(atFd, s + i, len - i);  
14.	        } while (ret < 0 && errno == EINTR);  
15.	        if (ret < 0) {  
16.	            return AT_ERR_GENERIC;  
17.	        }  
18.	        i += ret;  
19.	    }  
20.	    if (isPdu != 0) {  
21.	        do {  
22.	            ret = write(atFd, "\x1A", 1);  
23.	        } while ((ret < 0 && errno == EINTR) || (ret == 0));  
24.	    } else {  
25.	        do {  
26.	            ret = write(atFd, "\r", 1);  
27.	        } while ((ret < 0 && errno == EINTR) || (ret == 0));  
28.	    }  
29.	    if (ret < 0) {  
30.	        return AT_ERR_GENERIC;  
31.	    }  
32.	    return VENDOR_SUCCESS;  
33.	} 

到这里为止,Answer流程已经完成了从app到modem的信息传递。

总结:

从第三章的分析过程,我们已经完成了从Call ui响应来电提示,然后一步一步完成了整个框架层的调用过程,其实在结尾的时候我们只是完成了消息的下传到modem,这个过程后modem也进行回复,这个过程也比较长此处就不再赘述了。有机会的话我们可以在后续的文章中进行分析。

整个文档由于本人能力和时间的限制,后续尽可能将遗漏地方补全。从core_service到ril_adapter的调用,是在vendor目录中将cellular_radio1的libhril_hdf.z.so加载。由于目前的代码还在完善中,有可能后续最新版的代码会有所改变。

更多原创内容请关注:深开鸿技术团队

入门到精通、技巧到案例,系统化分享HarmonyOS开发技术,欢迎投稿和订阅,让我们一起携手前行共建鸿蒙生态。

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
img.rar 666.64K 83次下载
已于2022-2-14 15:38:09修改
6
收藏 3
回复
举报
5条回复
按时间正序
/
按时间倒序
红叶亦知秋
红叶亦知秋

这个模块讲的太详细了!期待老师其他文章。另外文中图片方便放附件一份吗,文章中压得有点看不清了。

已于2022-2-14 14:07:08修改
回复
2022-2-14 14:06:34
深开鸿
深开鸿 回复了 红叶亦知秋
这个模块讲的太详细了!期待老师其他文章。另外文中图片方便放附件一份吗,文章中压得有点看不清了。

已添加附件

1
回复
2022-2-14 15:38:53
红叶亦知秋
红叶亦知秋 回复了 深开鸿
已添加附件

谢谢老师

回复
2022-2-14 16:03:20
胡泉河_2021
胡泉河_2021

讲得非常详细,非常清晰,点赞

回复
2022-4-15 15:41:08
wx61d406a8b8f18
wx61d406a8b8f18

老师,

     “从core_service到ril_adapter的调用,是在vendor目录中将cellular_radio1的libhril_hdf.z.so加载” 这部分代码现在完善了么?调用流程有介绍么?非常感谢!

回复
2022-4-20 20:38:41
回复
    相关推荐