OpenHarmony源码解析之电话子系统——通话流程

news2024/11/17 7:16:59

一、简介

OpenAtom OpenHarmony(以下简称“OpenHarmony”)电话子系统为 OS 提供了基础的无线通信能力。

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

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

二、OpenHarmony架构图

三、基础知识

1. 电话子系统

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

2. 电话子系统架构图

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

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

框架层 :

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

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

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

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

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

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\.  ```

4. 相关仓

核心服务 :https://gitee.com/openharmony/telephony_core_service

蜂窝通话 :https://gitee.com/openharmony/telephony_cellular_call

通话管理 :https://gitee.com/openharmony/telephony_call_manager

注册服务 :https://gitee.com/openharmony/telephony_state_registry

短彩信 :https://gitee.com/openharmony/telephony_sms_mms

riladapter:https://gitee.com/openharmony/telephony_ril_adapter

数据业务 :https://gitee.com/openharmony/telephony_cellular_data

数据存储 :https://gitee.com/openharmony/telephony_data_storage

网络管理 :https://gitee.com/openharmony/communication_netmanager_standard

5. 电话子系统(call)核心类

四、源码解析

作为电话子系统的核心业务,通话功能(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 事件处理流程来阐述。

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 是电话子系统框架层通过 napi 对外封装的接口实现的,call_manager 中的 @ohos.telephony.call.d.ts 中有对应的接口说明,从这里代码已经完成了从 app 到 framework 的调用。

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

2. 通话框架层代码分析

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

3.2.2通话框架层代码分析

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

3.2.2.1Answer事件在call_manager中的处理

之前应用层调用的 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_valueNapiCallManager::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 {

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中的处理

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中的处理

这部分的代码会调用 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中的处理

从目前的代码调用来看有可能是从 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 Next)全栈开发技术的学习路线,包含了鸿蒙开发必掌握的核心知识要点,内容有(ArkTS、ArkUI开发组件、Stage模型、多端部署、分布式应用开发、WebGL、元服务、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、OpenHarmony驱动开发、系统定制移植等等)鸿蒙(HarmonyOS NEXT)技术知识点。

在这里插入图片描述

《鸿蒙 (Harmony OS)开发学习手册》(共计892页):https://gitcode.com/HarmonyOS_MN

如何快速入门?

1.基本概念
2.构建第一个ArkTS应用
3.……

开发基础知识:

1.应用基础知识
2.配置文件
3.应用数据管理
4.应用安全管理
5.应用隐私保护
6.三方应用调用管控机制
7.资源分类与访问
8.学习ArkTS语言
9.……

在这里插入图片描述

基于ArkTS 开发

1.Ability开发
2.UI开发
3.公共事件与通知
4.窗口管理
5.媒体
6.安全
7.网络与链接
8.电话服务
9.数据管理
10.后台任务(Background Task)管理
11.设备管理
12.设备使用信息统计
13.DFX
14.国际化开发
15.折叠屏系列
16.……

在这里插入图片描述

鸿蒙开发面试真题(含参考答案):https://gitcode.com/HarmonyOS_MN

在这里插入图片描述

OpenHarmony 开发环境搭建

图片

《OpenHarmony源码解析》:https://gitcode.com/HarmonyOS_MN

  • 搭建开发环境
  • Windows 开发环境的搭建
  • Ubuntu 开发环境搭建
  • Linux 与 Windows 之间的文件共享
  • ……
  • 系统架构分析
  • 构建子系统
  • 启动流程
  • 子系统
  • 分布式任务调度子系统
  • 分布式通信子系统
  • 驱动子系统
  • ……

图片

OpenHarmony 设备开发学习手册:https://gitcode.com/HarmonyOS_MN

图片

写在最后

如果你觉得这篇内容对你还蛮有帮助,我想邀请你帮我三个小忙

  • 点赞,转发,有你们的 『点赞和评论』,才是我创造的动力。
  • 关注小编,同时可以期待后续文章ing🚀,不定期分享原创知识。
  • 想要获取更多完整鸿蒙最新学习资源,请移步前往在这里插入图片描述

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

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

相关文章

c++栈和队列(stack和queue)

前言 栈和队列是两个极其相似的数据结构&#xff0c;栈具有先进后出的特性&#xff0c;队列具有先进先出的特性。今天我们就来简单的介绍一下栈和队列这两数据结构&#xff0c;其中队列我们介绍普通队列、双端队列&#xff08;了解&#xff09;和优先级队列&#xff08;其实这…

C++从入门到起飞之——vector模拟实现 全方位剖析!

​ &#x1f308;个人主页&#xff1a;秋风起&#xff0c;再归来~&#x1f525;系列专栏&#xff1a;C从入门到起飞 &#x1f516;克心守己&#xff0c;律己则安 目录 1、vector的成员变量 2、迭代器 3、size与capacity 4、[]运算符重载 5、reserve 6、push_bac…

LSTM结合时序异常检测直接写!小论文闭着眼睛发!

还在愁小论文&#xff1f;不如考虑考虑这个方向&#xff1a;LSTM时间序列异常检测。 这是个比较活跃且热门的研究方向&#xff0c;因为LSTM具有非常优秀的时序数据深度处理能力&#xff0c;能够灵活适应不同复杂度的数据&#xff0c;给我们提供高精度的预测结果&#xff0c;在…

时间继电器和定时器

一、概述 1.时间继电器是可以在设定的定时周期内或周期后闭合或断开触点的元器件。 2.时间继电器上可设定的定时周期数量有限&#xff0c;多为一个或两个。定时时长从0.02s至300h(根据产品型号范围不同)。 3.定时器可以理解为一台钟表&#xff0c;它在某个时间点上闭合(断开…

PostgreSQL如何设置主键自增(序列、SERIAL)

文章目录 PostgreSQL如何设置主键自增背景什么是序列Postgresql的自增机制基本使用使用SERIAL或BIGSERIAL数据类型手动创建序列和设置默认值实战demo&#xff1a;PostgreSQL 手动序列管理设置序列的当前值 工作常用总结创建表时候自定义序列&#xff1a;id SERIAL PRIMARY KEY …

调用具体接口的所有实现类

Java获取接口的所有实现类方法-CSDN博客https://blog.csdn.net/feeltouch/article/details/135399078

最实用接地气的 .NET 微服务框架

目录 前言 项目介绍 快速入门 1、服务注册 2、启动UI 3、服务发现与调用 4、启动服务网关 项目地址 最后 前言 微服务架构已经成为搭建高效、可扩展系统的关键技术之一&#xff0c;然而&#xff0c;现有许多微服务框架往往过于复杂&#xff0c;使得我们普通开发者难以…

基于生成对抗模型GAN蒸馏的方法FAKD及其在EdgesSRGAN中的应用

文章目录 FAKD系列论文paper1: FAKD&#xff1a;用于高效图像超分辨率的特征亲和知识蒸馏&#xff08;2020&#xff09;ABSTRACT1. INTRODUCTION2. PROPOSED METHOD2.1. Feature Affinity-based Distillation (FAKD) 2.2. Overall Loss Function3. EXPERIMENTAL RESULTS3.1. Ex…

TypeSript9 命名空间namesapce

我们在工作中无法避免全局变量造成的污染&#xff0c;TypeScript提供了namespace 避免这个问题出现 内部模块&#xff0c;主要用于组织代码&#xff0c;避免命名冲突。命名空间内的类默认私有通过 export 暴露通过 namespace 关键字定义 TypeScript与ECMAScript 2015一样&…

React学习day02-React事件绑定、组件、useState、React组件样式处理方式

3、React事件绑定&#xff08;以点击事件为例&#xff09; &#xff08;1&#xff09;语法&#xff08;整体遵循驼峰命名法&#xff09;&#xff1a;on事件名称{事件处理程序} 比如&#xff1a;点击事件onClick&#xff08;类似于vue中的click&#xff09; &#xff08;2&…

成为Python高手,我能给出的最好建议

今天笔者将向大家分享5个良好的Python编程习惯&#xff0c;大牛认证&#xff0c;通过不断实践&#xff0c;助你写出更Pythonic的代码&#xff0c;让你向Python大师之路更进一步。 今天笔者将向大家分享5个良好的Python编程习惯&#xff0c;大牛认证&#xff0c;通过不断实践&a…

Java面试题精选:消息队列(二)

一、Kafka的特性 1.消息持久化&#xff1a;消息存储在磁盘&#xff0c;所以消息不会丢失 2.高吞吐量&#xff1a;可以轻松实现单机百万级别的并发 3.扩展性&#xff1a;扩展性强&#xff0c;还是动态扩展 4.多客户端支持&#xff1a;支持多种语言&#xff08;Java、C、C、GO、…

WPF中如何根据数据类型使用不同的数据模板

我们在将一个数据集合绑定到列表控件时&#xff0c;有时候想根据不同的数据类型&#xff0c;显示为不同的效果。 例如将一个文件夹集合绑定到ListBox时&#xff0c;系统文件夹和普通文件夹分别显示为不同的效果&#xff0c;就可以使用模板选择器功能。 WPF提供了一个模板选择…

查找4(散列表)

1&#xff09;基本概念 、 2)散列函数的构造 3&#xff09;解决冲突 I&#xff09;开放地址发 II&#xff09;链地址法 4&#xff09;散列表的查找

vs2022 C++ 使用MySQL Connector/C++访问mysql数据库

1、下载MySQL Connector/C&#xff0c;我这里下载的是debug版本&#xff0c;下载链接MySQL :: Download MySQL Connector/C (Archived Versions) 2、解压并且放到MySQL文件夹中&#xff0c;便于使用 3、打开vs2022&#xff0c;右键项目&#xff0c;点击属性 4、在 “C/C” ->…

el-input中show-password密码提示功能去掉

el-input中show-password密码提示功能去掉 一、效果图二、封装个组件三、如何使用 一、效果图 二、封装个组件 <template><divclass"el-password el-input":class"[size ? el-input-- size : , { is-disabled: disabled }]"><inputclass…

Java线上监控诊断产品Arthas(续集)

Java线上监控诊断产品Arthas&#xff08;续集&#xff09; 前言1.auth指令2.monitor指令解读 3.classloader指令场景 4.dump指令场景 5.getstatic指令场景 6.heapdump指令场景 7.profiler指令场景 8.sc指令场景 9.trace指令场景 前言 在去年&#xff0c;我发表了一片文章&…

心血管内科常用评估量表汇总,附操作步骤与评定标准

心血管内科常用量表来评估患者病情、预测风险&#xff0c;量表在制定治疗方案和预测疾病进展等方面发挥着重要作用。常笑医学整理了6个心血管内科常用的评估量表&#xff0c;支持下载和在线使用&#xff0c;供临床医护人员参考。 01 GRACE缺血风险评估 &#xff08;完整量表请点…

Qt 学习第7天:Qt核心特性

元对象系统Meta-object system 来自AI生成&#xff1a; Qt中的元对象系统&#xff08;Meta-Object System&#xff09;是Qt框架的一个核心特性&#xff0c;它为Qt提供了一种在运行时处理对象和类型信息的能力。元对象系统主要基于以下几个关键概念&#xff1a; 1. QObject&a…

【91-136】行为型模式

目录 一.模板方法模式 1.1 概述 1.2 结构 1.3 案例 1.4 优缺点 1.5 使用场景 二.策略模式 2.1 概述 2.2 结构 2.3 案例 2.4 优缺点 2.5 使用场景 2.6 JDK 源码解析 三.命令模式 3.1 概述 3.2 结构 3.3 案例 3.4 优缺点 3.5 使用场景 四.责任链模式 4.1 概…