写点什么

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

  • 2022 年 5 月 16 日
  • 本文字数:32603 字

    阅读完需:约 107 分钟

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

(以下内容来自开发者分享,不代表 OpenHarmony 项目群工作委员会观点)

本文转载自:https://harmonyos.51cto.com/posts/10384

王大鹏

深圳开鸿数字产业发展有限公司

一、简介

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

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

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

二、OpenHarmony 架构图

三、基础知识

电话子系统

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

电话子系统架构图

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

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

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

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

电话子系统代码结构

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

通话管理模块:主要管理 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.  ```  
复制代码

相关仓

电话子系统(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 事件处理流程来阐述。

通话上层调用代码分析

当有电话呼入时,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 文件。

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'; 
复制代码

通话框架层代码分析

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

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 加载。由于目前的代码还在完善中,有可能后续最新版的代码会有所改变。

高清流程图下载:https://ost.51cto.com/resource/1716



发布于: 5 小时前阅读数: 6
用户头像

OpenHarmony开发者官方账号 2021.12.15 加入

OpenHarmony是由开放原子开源基金会(OpenAtom Foundation)孵化及运营的开源项目,目标是面向全场景、全连接、全智能时代,基于开源的方式,搭建一个智能终端设备操作系统的框架和平台,促进万物互联产业的繁荣发展

评论

发布
暂无评论
OpenHarmony源码解析之电话子系统——通话流程_OpenHarmony_OpenHarmony开发者社区_InfoQ写作社区