1

Author: Wang Dapeng

Introduction

OpenAtom OpenHarmony (hereinafter referred to as "OpenHarmony") telephony subsystem provides basic wireless communication capabilities for the OS.
Support TD-LTE/FDD-LTE/TD-SCDMA/WCDMA/EVDO/CDMA1X/GSM and other network standard communication modules, can provide high-speed wireless data transmission, Internet access and other services, with voice, SMS, MMS, SIM card and other functions.
Unless otherwise stated, the following text refers to the OpenHarmony system (OpenHarmony 3.0 LTS version).

OpenHarmony Architecture Diagram

Basic knowledge

telephone subsystem

As an important part of OpenHarmony, the telephony subsystem provides basic communication functions for the system, including services in the CS domain, such as voice calls, SMS, and call management; it also includes related services in the PS domain, such as MMS, data services, etc. In addition, the business of SIM and RIL is also in this subsystem.

Phone Subsystem Architecture Diagram

OpenHarmony's existing phone subsystem cellular phone call related frame diagram:

Application layer: Various applications that require calls, SMS, data services, and SIM card functions, such as call applications, SMS applications, launcher applications, and so on.

Frame layer:

1.SDK: Provide standard interfaces for applications, including JS interfaces and C++ interfaces.

2.Framework: Provide stable basic capabilities of corresponding modules to the application layer, including network, call, sms, sim-related functions, including call management, short color editing, sending and receiving, sim card identification, network, data service management, etc. . In the current OpenHarmony version, modules such as call_manager, cellular_call, cellular_data, data_storage, sms_mms, state_registry, and core_service all belong to the framework layer.

Hril layer: equivalent to Android's RILJ. Since the Modem used in different schemes is different, various instruction formats and initialization sequences are different. In order to eliminate these differences, Hril provides an abstraction between wireless hardware devices and telephony services. layer.

Vendor lib layer : similar to Android's RILD, responsible for interacting with the modem module and sending AT commands corresponding to each module.

Modem layer: The current baseband processor mainly deals with the encoding and decoding of digital signals, voice signals and communication protocols, while the baseband processor, radio frequency and other peripheral chips, as a Modem module, provide an AT command interface to the upper layer for interaction. The communication module Modem communicates with the communication network, transmits voice and data, completes calls, text messages and other related telephone functions.

Telephony Subsystem Code Structure

Since the telephony subsystem contains many modules, each module is described separately:

Call management module: mainly manages three types of calls: CS (Circuit Switch, circuit switching), IMS (IP Multimedia Subsystem, IP Multimedia Subsystem) and OTT (over the top, OTT solution), and is responsible for applying for the audio required for calls. video resources, and handle various conflicts arising from multiple calls.

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

Cellular call module: supports basic call implementation based on carrier network, including 2G/3G-based CS (Circuit Switch) calls and 4G/5G-based IMS (IP Multimedia Subsystem, IP Multimedia Subsystem) calls, including VoLTE / VoWIFI/ VoNR voice, video, conference, support domain selection control and switching between CS and IMS calls, support emergency calls. Support mainstream modem chip platforms.

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

Cellular data module: As a tailorable component of the telephony subsystem, it depends on the core_service core service, ril_adapter. It has functions such as cellular data activation, cellular data abnormality detection and recovery, cellular data status management, cellular data switch management, cellular data roaming management, APN management, and network management interaction.

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

Telephone core service module: The main function is to initialize RIL management, SIM card and network search module, and obtain RIL Adapter service. By registering the callback service, it can communicate with the RIL Adapter; by publishing and subscribing, it can communicate with each functional module.

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

Database and persistence module: responsible for persistent data storage of modules such as SIM card/SMS and MMS in the telephone service subsystem, and provide DataAbility access interface.

 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 module: mainly includes vendor library loading, business interface implementation, and event scheduling management. It is mainly used to shield the hardware differences of different modem manufacturers, provide a unified interface for the upper layer, and communicate with the upper layer interface by registering the HDF service.

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

Short and MMS module: Provide SMS sending and receiving and MMS encoding and decoding functions for mobile data users. The main functions include GSM/CDMA SMS sending and receiving, SMS PDU (Protocol data unit, protocol data unit) encoding and decoding, Wap Push receiving and processing, cell broadcast receiving, MMS Notification, MMS codec and SIM card text message record addition, deletion, modification and checking, etc.

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

Status registration module: It is mainly responsible for providing subscription and unsubscription APIs for various message events of the telephony service subsystem. Event types include network status changes, signal strength changes, cell information changes, cellular data connection status changes, call status changes, and more.

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

Related warehouse

Core service: https://gitee.com/openharmony/telephony_core_service

Cellular call: https://gitee.com/openharmony/telephony_cellular_call

Call management: https://gitee.com/openharmony/telephony_call_manager

Registration Service: https://gitee.com/openharmony/telephony_state_registry

SMS MMS: https://gitee.com/openharmony/telephony_sms_mms

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

Data business: https://gitee.com/openharmony/telephony_cellular_data

Data storage: https://gitee.com/openharmony/telephony_data_storage

Network management: https://gitee.com/openharmony/communication_netmanager_standard

Telephone subsystem (call) core class

Source code analysis

As the core business of the telephone subsystem, the call function (Call) requires not only hardware support, such as audio modules, baseband modules, etc., but also the cooperation of many services of the system itself to achieve this function, such as: call management (call_manager), cellular Call service (cellular_call), Telephony core service (core_service), RIL adaptation (ril_adapter), state registration service (state_registry), etc.

Calls are currently divided into three types: CS Call (Circuit Switch, circuit switching), IMS Call (IP Multimedia Subsystem, IP Multimedia Subsystem) and OTT Call (over the top, OTT solution) three types of calls. The interfaces exposed to the upper-layer Call application include dial, answer, reject, hangup, holdCall, unHoldCall, switchCal, startDTMF, stopDTMF, etc. Since there are many events, and the processing flow of each event is similar, we will use the Answer event processing flow of Call to describe here.

Call upper layer calling code analysis

When there is an incoming call, the Callui interface will display the incoming call, and the user clicks the answer button to activate the Answer process of Call.

 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>

The onAnswer function of incomingCom.js is called here.

 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.  },

Since the callServiceProxy.js file has been imported before.

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

So let's take a look at the acceptCall function of callServiceProxy.

 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.  };

From the code, we can see that this function actually calls the answer function of call, so where does this come from. call is implemented by the telephony subsystem framework layer through the interface encapsulated by napi. There is a corresponding interface description in @ohos.telephony.call.d.ts in call_manager. From here, the code has completed the call from app to framework.

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

Call framework layer code analysis

3.2.1 Call framework layer code call sequence diagram

3.2.2 Code Analysis of Call Framework Layer

As can be seen from the sequence diagram above, the calling process of the entire Answer is relatively long, and the entire framework layer handles multiple services including call_manager, cellular_call, core_service, IPC, and ril_adapter. Because the processing flow is too long, the specific call situation can refer to the sequence diagram. Here we describe some key parts of the processing of the framework layer according to the calling of different services.

3.2.2.1Answer event processing in call_manager

The call.answer called by the application layer before is introduced by @ohos.telephony.call, and the actual definition is implemented in the napi interface in the interfaces in the call_manage service.

 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.  };

Registration uses an interface and some enumeration parameters

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.  }

What we need here is the CallBasis interface. The details are as follows. These are the processing functions corresponding to some events called by the application layer before.

 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.  }

Because we correspond to the answer event, the corresponding function here is 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.  }

Continue to call down, from the previous function, AnswerCall should be processed asynchronously.

 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.  }

In the process of calling all the way, the AnswerCallPolicy function of the CallPolicy class will be called to judge whether the CallObject corresponding to the callId exists, and if so, judge the state of the 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.  }

In the subsequent call to AnswerCall of CallRequestHandlerService for processing, if there is a handler_ of CallRequestHandler, make a unique_ptr of AnswerCallPara, and then pass callid and videostate into the pointer. Call SendEvent to issue 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.  }

From the processing flow of the code, the event sent here will be captured by the CallRequestHandler class in the same file, triggering ProcessEvent processing.

 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 will get the corresponding memberFunc from memberFuncMap_ according to the id of the event, and then enter the corresponding processing function.

 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.  }

The corresponding processing function here is AcceptCallEvent(). This part of the function has no return value, and takes callId and videoState from the event.

 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.  }

When you continue to call the AnswerReques function in the CallRequestProcessl class, you will get a different CallObject through GetOneCallObject.

 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.  }

After getting the corresponding call, you can call the AnswerCall function of the corresponding call, which will overwrite the virtual function of BaseCall. Since CSCall, IMSCall, and OTTCall all have corresponding AnswerCall functions, the virtual function AnswerCall of the BaseCall class that is actually used is rewritten by that subclass to call different AnswerCall functions. Here we assume CsCall.

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

Call CarrierAcceptCall of CarrierCall for subsequent processing. The AcceptCallBase() function will judge the Call state. If it is in the CALL_RUNNING_STATE_RINGING state, it will call SetVolumeAudible of AudioControlManager to set the audio content.

 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.  }

After processing AcceptCallBase(), you need to use PackCellularCallInfo to pack the call information into callinfo, and then pass the callInfo directly.

 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.  }

The next step is to call the Answer function of the CellularCallIpcInterfaceProxy class to continue processing. First of all, it is necessary to judge whether there is a need for ReConnectService. This operation is to ensure that there is an available cellularCallInterfacePtr_. cellularCallInterfacePtr_ is an IRemoteBroker class, which is an IPC base class interface for IPC communication.

 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.  }

Before this, call_manager has completed the acquisition of the corresponding SA proxy IRemoteObject through ConnectService() in the initialization of CellularCallIpcInterfaceProxy with systemAbilityId (TELEPHONY_CELLULAR_CALL_SYS_ABILITY_ID), and then constructed the corresponding Proxy class, so as to ensure the correspondence between proxy and stub of IPC communication. We will see in the next section that the corresponding stub is actually registered in 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.  }

When cellularCallInterfacePtr_->Answer is called, the Answer virtual function of CellularCallInterface will be rewritten by the Answer function of CellularCallProxy, so the Answer function of CellularCallProxy is actually executed, which is an IRemoteProxy class. The callInfo information is converted into the form of MessageParcel and sent through SendRequest of IPC.
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.  }

From here, the processing of the Answer process in call_manager has been completed.

3.2.2.2 Handling of Answer events in cellular_call

IPC (Inter-Process Communication) and RPC (Remote Procedure Call) mechanisms are used to implement cross-process communication. The difference is that the former uses Binder drivers for cross-process communication within devices, while the latter uses soft bus drivers for cross-device communication. Cross-process communication. IPC and RPC usually use a client-server (Client-Server) model. The service requester (Client) can obtain the proxy (Proxy) that provides the service provider (Server), and read and write data through this proxy to realize inter-process data. communication. Usually, the Server will first register the system capability (System Ability) with the System Ability Manager (System Ability Manager, abbreviated as SAMgr), and SAMgr is responsible for managing these SAs and providing relevant interfaces to the Client. To communicate with a specific SA, the client must first obtain the proxy of the SA from SAMgr, and then use the proxy to communicate with the SA. The following uses Proxy to represent the service requester, and Stub to represent the service provider. The implementation code is in the /foundation/communication/ipc directory.

SA registration and startup: SA needs to register its own AbilityStub instance with SystemAbilityManager through the AddSystemAbility interface.

SA acquisition and invocation: Obtain the proxy IRemoteObject corresponding to the SA through the GetSystemAbility method of the SystemAbilityManager, and then construct the AbilityProxy. This ensures the correspondence between proxy and stub.

In the previous section, we saw the SA acquisition process in call_manager. Let's take a look at the SA registration process here. Remember the systemAbilityId TELEPHONY_CELLULAR_CALL_SYS_ABILITY_ID? It's the key to our SA registration.

 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.  }

In this way, we complete the registration of the stub, and the messages sent through the proxy will be passed to the stub here through IPC. The IPC process is relatively complicated, so I won't discuss it in depth here. Interested students can learn it by themselves.

Since the OnRemoteRequest of IPCObjectStub is a virtual function, it will be overridden by the OnRemoteRequest function of CellularCallStub that inherits the IPCObjectStub class.

 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.  }

Here, the corresponding processing function will be found according to the map table. Before our code = 4 is ANSWER, the corresponding processing should be performed here.

 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 will perform subsequent processing. Here, the corresponding callinfo information will be parsed from the data, and the Answer() of this file will be called at the end of the function, and the returned result will be written into reply. This return value is the result of the function execution. Int32 integer value.

 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.  }

The Answer function is the key to completing the interaction with ril. First, the calltype is obtained according to the information of callInfo. Currently, three calltypes are supported, namely cs_call, ims_call, and ott_call. After determining the calltype, obtain the corresponding CSControl object in GetCsControl() through slotId_.

 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.  }

This call to the CSControl::Answer function will get the corresponding CellularCallConnectionCS and CALL_STATUS according to callInfo. If the status is incoming call, ringing, or waiting, the function corresponding to CellularCallConnectionCS will be called.

 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.  }

Subsequently, CellularCallConnectionCS::AnswerRequest will be called to interact with core_service. GetCore will get the corresponding Core according to the slotId, and then use the Get function to get the corresponding event, set the Owner to CellularCallHandler, and call back the corresponding handler when waiting for the return.

 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.  }

This completes the invocation of the Answer process in cellular_call.

3.2.2.3 Handling of Answer events in core_servicel

This part of the code will call the Answer function corresponding to the core, and the message will be passed to the tel_ril code responsible for the communication and interaction between the core service and the RIL Adapter.

 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.  }

Go further to TelRilManager, the management class responsible for tel_ril, and judge whether the corresponding telRilCall_ is empty. If it is not empty, you can continue to call.

 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_ is in the initialization of TelRilManager. Among them, InitCellularRadio first gets the service corresponding to cellular_radio1 through ServiceManager. Then construct telRilCall_ with cellularRadio_ and observerHandler_ in InitTelInfo.

 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.  }

Call the Answer function of teRilCall for processing, construct telRilRequest with CreateTelRilRequest, and send it out through SendInt32Event of TelRilBase.

 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.  }

From the calling situation, the dispatchId and data are sent to cellular_radio1 through the SendRequest request of IPC.

 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.  }

At this point, the processing of the Answer process in the core_service service is completed.

3.2.2.4 Handling of Answer events in ril_adapter

From the current code call, it is possible to call the dispatch of hril_hdf from the vendor, and then pass the message to ril_adapter

The following is the initialization process of 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);

You can see that the initialization of hdf includes MODULE_ NAME, RilAdapterBind, and RilAdapterInit.

The Bind process is as follows.

 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.  }

You can see that the dispatch of the service corresponds to RilAdapterDispatch.

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

The Init process is as follows.

 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.  }

Among them, LoadVendor will load the corresponding vendor's 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.  }

It has been mentioned before that it is possible that the vendor called dispatch, and now the corresponding RilAdapterDispatch has been bound, and the subsequent processing is as follows.

IPC written in C++ and IPC written in C language can communicate with each other, and the format is different. HdfSBuf can also be converted to and from 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.  }

After the Request message is sent to RilAdapter through IPC, it will be distributed through 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.  }

In subsequent calls we know that the code is HREQ_CALL_ANSWER, so DispatchModule will be called.

 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.  }

Get the Dispatch function corresponding to g_manager according to slotId.

 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.  }

Since our code = 4, between 0 and 100, it will be judged as callrequest

So call the following function.

 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 is as follows.

 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_CO

OpenHarmony开发者
160 声望1.1k 粉丝

OpenHarmony是由开放原子开源基金会(OpenAtom Foundation)孵化及运营的开源项目,