OpenHarmony源碼解析之電話子系統——通話流程

来源:https://www.cnblogs.com/openharmony/archive/2022/05/10/16255283.html
-Advertisement-
Play Games

OpenHarmony電話子系統為 OS 提供了基礎的無線通信能力。支持 TD-LTE/FDD-LTE/TD-SCDMA/WCDMA/EVDO/CDMA1X/GSM 等網路制式的通信模塊,能夠提供高速的無線數據傳輸、互聯網接入等業務,具備語音、簡訊、彩信、SIM 卡等功能。 ...


(以下內容來自開發者分享,不代表 OpenHarmony 項目群工作委員會觀點)

 

王大鵬 深圳開鴻數字產業發展有限公司

 

一、簡介


OpenAtom OpenHarmony(以下簡稱“OpenHarmony”)電話子系統為 OS 提供了基礎的無線通信能力。


支持 TD-LTE/FDD-LTE/TD-SCDMA/WCDMA/EVDO/CDMA1X/GSM 等網路制式的通信模塊,能夠提供高速的無線數據傳輸、互聯網接入等業務,具備語音、簡訊、彩信、SIM 卡等功能。


以下行文如無特別說明,所述說均指 OpenHarmony 系統(OpenHarmony 3.0 LTS版本)。

二、OpenHarmony架構圖

 

三、基礎知識


1. 電話子系統


電話子系統做為 OpenHarmony 很重要的組成部分,為系統提供基礎的通信功能,包括 CS 域的服務,比如語音呼叫、簡訊、呼叫管理;也包括 PS 域的相關服務,比如 MMS、數據業務等,另外 SIM 和 RIL 的業務也在該子系統內。


2. 電話子系統架構圖


OpenHarmony 現有電話子系統蜂窩通話通話相關框架圖:

 

 

應用層 :各種需要通話、SMS、數據業務、SIM 卡功能的應用,例如call 應用、SMS 應用、launcher 應用等等。

 

框架層 :

①SDK :給應用提供標準介面,包括 JS 介面和 C++ 介面。

②Framework:嚮應用層提供對應模塊穩定的基礎能力,包括 network、call、sms、sim相關的功能,包括通話管理,短彩編輯以及發送接收,sim 卡的識別駐網,數據業務的管理等。在目前的 OpenHarmony 版本中,call_manager、cellular_call、cellular_data、data_storage、sms_mms、state_registry、core_service 等模塊都屬於框架層。

 

Hril層 :相當於安卓的 RILJ,由於不同方案使用的 Modem 不一樣,所以各種指令格式,初始化序列都不一樣,為了消除這些差別,而 Hril 則提供了無線硬體設備與電話服務之間的抽象層。

 

Vendor lib層 :類似於安卓的 RILD,負責與 modem 模塊交互,發送各模塊對應的 AT 命令。

 

Modem層:現在的基帶處理器,主要處理數字信號、語音信號的編碼解碼以及通信協議,而基帶處理器、射頻和其它外圍晶元作為一個 Modem  模塊,提供 AT 命令介面給上層用於交互。通信模塊 Modem 通過與通信網路進行溝通、傳輸語音及數據、完成呼叫、簡訊等相關電話功能。

 

3. 電話子系統代碼結構


由於電話子系統包含較多模塊,所以將各模塊分開描述:

 

通話管理模塊:主要管理 CS(Circuit Switch,電路交換)、IMS(IP Multimedia Subsystem,IP 多媒體子系統)和 OTT(over the top,OTT 解決方案)三種類型的通話,負責申請通話所需要的音視頻資源,並處理多路通話時產生的各種衝突。

 

1.  /base/telephony/call_manager  
2.  ├─ frameworks                              # napi介面存放目錄  
3.  ├─ interfaces                              # 對外部暴露的介面  
4.  │  ├─ innerkits                            # 部件間的內部介面  
5.  │  └─ kits                                 # js介面存放目錄  
6.  ├─ sa_profile                              # 啟動配置文件  
7.  ├─ services                                # 服務內部代碼  
8.  │  ├─ audio                                # 音頻管理相關代碼  
9.  │  ├─ bluetooth                            # 藍牙通話相關代碼  
10.  │  ├─ call                                 # 通話業務相關代碼  
11.  │  ├─ call_manager_service                 # 進程服務管理相關代碼  
12.  │  ├─ call_setting                         # 通話設置相關代碼  
13.  │  ├─ telephony_interaction                # 電話核心服務交互相關代碼  
14.  │  └─ call_ui_interaction                  # UI交互相關代碼  
15.  ├─ test                                    # 單元測試相關代碼  
16.  └─ utils                                   # 通用工具類  
17.  ``` 

 

蜂窩通話模塊:支持基於運營商網路的基礎通話實現,包含基於 2G/3G 的 CS(Circuit Switch,電路交換)通話和基於 4G/5G 的 IMS(IP Multimedia Subsystem,IP 多媒體子系統)通話,包含 VoLTE/ VoWIFI/ VoNR 語音、視頻、會議,支持 CS 和 IMS 通話之間的域選控制和切換,支持緊急通話。支持主流 modem 晶元平臺。

 

1.  /base/telephony/cellular_call     # 蜂窩通話子組件  
2.  ├─ BUILD.gn                       # 編譯gn腳本  
3.  ├─ README.md                      # Readme文檔  
4.  ├─ services  
5.  │  ├─ common                      # 工具倉  
6.  │  ├─ connection                  # 連接層  
7.  │  ├─ control                     # 控制業務層  
8.  │  └─ manager                     # 管理層  
9.  ├─ sa_profile                     # sa文件  
10.  ├─ ohos.build                     # 編譯build  
11.  └─ test                           # 測試相關  
12.  ```  


蜂窩數據模塊:作為電話子系統可裁剪部件,依賴於 core_service 核心服務、ril_adapter。具有蜂窩數據激活、蜂窩數據異常檢測與恢復、蜂窩數據狀態管理、蜂窩數據開關管理、蜂窩數據漫游管理、APN 管理、網路管理交互等功能。

 

1.  base/telephony/cellular_data  
2.  ├── figures  
3.  ├── frameworks  
4.  │   ├── js                              # js文件  
5.  │   │   └── napi  
6.  │   │       ├── include  
7.  │   │       └── src  
8.  │   └── native  
9.  │       └── src  
10.  ├── interfaces  
11.  │   ├── innerkits                       # 對外提供的介面  
12.  │   └── kits  
13.  │       └── js                          # 對外提供的js介面  
14.  │           └── declaration  
15.  ├── sa_profile                          # SA配置  
16.  ├── services  
17.  │   ├── include                         # 頭文件  
18.  │       ├── apn_manager  
19.  │   │   ├── common  
20.  │   │   ├── state_machine  
21.  │   │   └── utils  
22.  │   └── src                             # 源文件  
23.  │       ├── apn_manager  
24.  │       ├── state_machine  
25.  │       └── utils  
26.  └── test  
27.      └── unit_test                       # 單元測試相關代碼  
28.  ```  

 

電話核心服務模塊:主要功能是初始化 RIL 管理、SIM 卡和搜網模塊,以及獲取 RIL Adapter 服務。通過註冊回調服務,實現與 RIL Adapter 進行通信;通過發佈訂閱,來實現與各功能模塊的通信。

 

1.  /base/telphony/core_service  
2.  ├── interfaces             # 介面目錄  
3.  │   ├── innerkits          # 部件間的內部介面  
4.  │   └── kits               # 對應用提供的介面(例如JS介面)  
5.  ├── services               # 核心服務實現代碼目錄  
6.  │   ├── include  
7.  │   └── src  
8.  ├── etc                    # 核心服務的驅動腳本目錄  
9.  │   └── init  
10.  ├── sa_profile             # 核心服務的啟動文件目錄  
11.  ├── tel_ril                # 核心服務與RIL Adapter通信代碼目錄  
12.  │   ├── include          
13.  │   ├── src  
14.  │   └── test  
15.  ├── network_search         # 搜網服務代碼目錄  
16.  │   ├── include  
17.  │   ├── src  
18.  │   └── test  
19.  ├── sim                    # SIM卡服務代碼目錄  
20.  │   ├── include  
21.  │   ├── src  
22.  │   └── test  
23.  ├── frameworks             # frameworks目錄  
24.  │   ├── js  
25.  │   ├── nstive  
26.  ├── common                 # 各個業務模塊頭文件目錄  
27.  │   ├── api  
28.  │   ├── call_manager  
29.  │   ├── network_search  
30.  │   └── sim  
31.  ├── utils  
32.  │   ├── log                # 核心服務日誌列印目錄  
33.  │   ├── preferences  
34.  │   ├── common  
35.  ```

 

資料庫及持久化模塊:負責電話服務子系統中的 SIM 卡/短彩信等模塊持久化數據存儲,提供 DataAbility 訪問介面。

 

1.  /base/telephony/data_storage     # 資料庫及持久化  
2.  ├─ BUILD.gn                         # 編譯gn腳本  
3.  ├─ README.md                        # Readme文檔  
4.  ├─ common                           # 公共、通用文件  
5.  │  ├─ include                         
6.  │  └─ src                             
7.  ├─ pdp_profile                      # 網路運營商  
8.  │  ├─ include                         
9.  │  └─ src                             
10.  ├─ sim                              # sim卡  
11.  │  ├─ include                         
12.  │  └─ src                             
13.  ├─ sms_mms                          # 短彩信  
14.  │  ├─ include                        
15.  │  └─ src                             
16.  ├─ ohos.build                       # 編譯build  
17.  └─ test                             # 測試相關  
18.  ```  

 

RIL Adapter模塊:主要包括廠商庫載入,業務介面實現以及事件調度管理。主要用於屏蔽不同 modem 廠商硬體差異,為上層提供統一的介面,通過註冊 HDF 服務與上層介面通訊。

 

1.  base/telephony/ril_adapter  
2.  ├─ hril                            # hri層的各個業務模塊介面實現  
3.  ├─ hril_hdf                        # HDF服務  
4.  ├─ include                         # 頭文件存放目錄  
5.  ├─ interfaces                      # 對應提供上層各業務內部介面  
6.  │  └─ innerkits  
7.  ├─ test                            # 單元測試相關代碼  
8.  │  ├─ mock  
9.  │  └─ unittest                     # 單元測試代碼  
10.  └─ vendor                          # 廠商庫代碼  
11.  │  └─ include  
12.  ``` 

 

短彩信模塊:為移動數據用戶提供簡訊收發和彩信編解碼功能,主要功能有 GSM/CDMA 簡訊收發、簡訊 PDU(Protocol data unit,協議數據單元)編解碼、Wap Push 接收處理 、小區廣播接收、彩信通知、 彩信編解碼和 SIM 卡簡訊記錄增刪改查等。

 

1.  /base/telephony/sms_mms  
2.  ├─ interfaces               # 對外暴露的介面  
3.  │  └─ kits  
4.  ├─ sa_profile               # 啟動配置文件  
5.  ├─ services                 # 服務內部代碼  
6.  │  ├─ include               # 頭文件目錄  
7.  │  ├─ cdma                  # CDMA制式源文件  
8.  │  └─ gsm                   # GSM制式源文件  
9.  ├─ test                     # 單元測試目錄  
10.  └─ utils                    # 通用工具相關  
11.  ``` 

 

狀態註冊模塊:主要負責提供電話服務子系統各種消息事件的訂閱以及取消訂閱的 API。事件類型包括網路狀態變化、信號強度變化、小區信息變化、蜂窩數據連接狀態變化、通話狀態變化等等。

 

1.  /base/telephony/state_registry      # 狀態註冊轉發服務  
2.  ├─ BUILD.gn                         # 編譯gn腳本  
3.  ├─ README.md                        # Readme文檔  
4.  ├─ interfaces                       # API,js文件  
5.  ├─ service  
6.  │  ├─ include                       # 頭文件  
7.  │  └─ src                           # 源文件  
8.  ├─ sa_profile                       # sa文件  
9.  ├─ ohos.build                       # 編譯build  
10.  └─ test                             # 測試相關  
11.  ```  

 

4. 相關倉

 

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

蜂窩通話 :https://gitee.com/openharmony/telephony_cellular_call

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

註冊服務 :https://gitee.com/openharmony/telephony_state_registry

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

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

數據業務 :https://gitee.com/openharmony/telephony_cellular_data

數據存儲 :https://gitee.com/openharmony/telephony_data_storage

網路管理 :https://gitee.com/openharmony/communication_netmanager_standard


5. 電話子系統(call)核心類


 

四、源碼解析


作為電話子系統的核心業務,通話功能(Call)除了需要硬體支持,比如音頻模塊、基帶模塊等,還需要系統本身的許多服務相互配合才能實現該功能,比如:通話管理(call_manager)、蜂窩通話服務(cellular_call)、Telephony 核心服務(core_service)、RIL 適配(ril_adapter),狀態註冊服務(state_registry)等。

 

通話目前主要分成三種:CS Call(Circuit Switch,電路交換)、IMS Call(IP Multimedia Subsystem,IP 多媒體子系統)和 OTT Call(over the top,OTT 解決方案)三種類型的通話。對上層 Call 應用暴露的介面包括:dial、answer、reject、hangup、holdCall、unHoldCall、switchCal、startDTMF、stopDTMF 等。由於事件較多,而且各事件處理流程比較類似,所以我們此處以 Call 的 Answer 事件處理流程來闡述。

 

1. 通話上層調用代碼分析


當有電話呼入時,Callui 界面會顯示電話呼入,用戶點擊 answer 按鍵,則會激活 Call 的 Answer 流程。
 

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


 此處會調用 incomingCom.js 的 onAnswer 函數。
 

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


 由於之前已經 import了callServiceProxy.js 文件。
 

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


所以我們來看下 callServiceProxy 的 acceptCall 函數。

 

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


從代碼中我們能看到此函數實際調用了 call 的 answer 函數,那麼這個哪來的呢。call 是電話子系統框架層通過 napi 對外封裝的介面實現的,call_manager 中的 @ohos.telephony.call.d.ts 中有對應的介面說明,從這裡代碼已經完成了從 app 到 framework 的調用。
 

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


2. 通話框架層代碼分析


3.2.1通話框架層代碼調用時序圖

 

3.2.2通話框架層代碼分析

 

從上邊的時序圖可以看出,整個 Answer 的調用流程是比較長的,整個框架層處理跨越包括 call_manager、cellular_call、core_service、IPC、ril_adapter 等多個服務。由於處理流程過長,具體的調用情況可以參照時序圖,此處我們將框架層的處理一些關鍵地方根據調用不同的服務來進行描述。

 

3.2.2.1Answer事件在call_manager中的處理

 

之前應用層調用的 call.answer 是通過 @ohos.telephony.call 引入的,而實際定義在 call_manage 服務中的 interfaces 內的 napi 介面中實現。

 

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


註冊要用到介面以及一些枚舉參數napi_valueNapiCallManager::RegisterCallManagerFunc(napi_env env, napi_value exports)

 

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

 

這裡我們需要的是 CallBasis 介面,具體內容如下所述,這些就是之前應用層調用的一些事件對應的處理函數。

 

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


因為我們對應的是 answer 事件,所以這裡的對應函數是 AnswerCall。
 

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


繼續往下調用,從之前的函數看 AnswerCall 應該是非同步處理的。
 

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


在一路調用的的過程中,會調用 CallPolicy 類的 AnswerCallPolicy 函數用來判斷對應 callId 的 CallObject 是否存在,如有有就判斷 Call 所處的狀態。
 

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


在後續調用到 CallRequestHandlerService 的 AnswerCall 進行處理時,如果有 CallRequestHandler 的 handler_存在,則 make 個 AnswerCallPara 的 unique_ptr,然後將 callid 和 videostate 傳入該指針。調用 SendEvent 將 HANDLER_ANSWER_CALL_REQUEST 發出。
 

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


從代碼的處理流程看,這裡發出的 event 會被同一個文件中的 CallRequestHandler 類捕獲,觸發 ProcessEvent 處理。
 

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


memberFuncMap 會根據 event 的 id 從 memberFuncMap_拿到對應的 memberFunc,然後進入對應的處理函數。
 

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


這裡對應的處理函數是 AcceptCallEvent(),這部分函數無返回值,又從 event 中取出 callId 和 videoState。
 

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


繼續調用 CallRequestProcessl 類中的 AnswerReques 函數時,會通過 GetOneCallObject 拿到不同的 CallObject。
 

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


拿到了對應的 call 之後,就可以調用對應的 call 的 AnswerCall 函數,這個函數會覆蓋 BaseCall 的虛函數。由於 CSCall、IMSCall、OTTCall 都有對應的 AnswerCall 函數,所以實際使用那個是由 BaseCall 類的虛函數 AnswerCall 被那個子類重寫,從而調用不同的 AnswerCall 函數。這裡我們假設為 CsCall 。
 

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


調用到 CarrierCall 的 CarrierAcceptCall 進行後續處理。其中 AcceptCallBase() 函數會判斷 Call 狀態,如果是 CALL_RUNNING_STATE_RINGING 狀態,則會調用 AudioControlManager 的 SetVolumeAudible 來設置 audio 的相關內容。
 

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


處理完 AcceptCallBase() 後,需要用 PackCellularCallInfo 將 call 的信息打包進給 callinfo 中,後續直接該 callInfo 傳遞出去。
 

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


下一步會調用 CellularCallIpcInterfaceProxy 類的來 Answer 函數來繼續處理。首先要判斷是否有 ReConnectService 的必要,這樣操作是為了保證有可用的 cellularCallInterfacePtr_。cellularCallInterfacePtr_ 是一個 IRemoteBroker 類,該類是一個 IPC 基類介面用於 IPC 通信。
 

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


在此之前 call_manager 已經在 CellularCallIpcInterfaceProxy 的初始化中將 systemAbilityId(TELEPHONY_CELLULAR_CALL_SYS_ABILITY_ID) 通過ConnectService()完成對應 SA 代理 IRemoteObject 的獲取,然後構造對應的 Proxy 類,這樣就能保證 IPC 通信的 proxy 和 stub 的對應。下節我們會看到,對應的 stub 其實是在 cellular_call 中註冊的。
 

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


當調用到 cellularCallInterfacePtr_->Answer 時,CellularCallInterface 的 Answer 虛函數,會被 CellularCallProxy 的 Answer 重寫,所以實際執行 CellularCallProxy 的 Answer 函數,它是一個 IRemoteProxy 類。而 callInfo 信息則轉換成 MessageParcel 形式通過 IPC 的 SendRequest 發出。
 
class CellularCallProxy : public IRemoteProxy<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_CONFERENC

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 離線CDH集群自動化部署工具 離線CDH集群安裝與部署的自動化腳本工具,簡單支持「離線一鍵裝機」。 腳本將對系統配置做出一定修改,使用前請務必確認當前伺服器無其他人員、任務使用,以免造成不必要的麻煩,建議提前使用測試伺服器或虛擬機測試體驗。 一、Features 已實現的自動化功能(僅支持Redha ...
  • 一、引言 需求描述:現實工作中,有一些很特別的需求:在一個彙總表中,需要顯示明細數據。因為是在彙總表中,所以明細數據只能顯示在某一列中,這個列,就是多行數據合併為一行之後的結果。 案例描述:比如,在物料凈需求表中,需求量扣減庫存量、在途量等等之後,結果為剩餘量,剩餘量為負則需要採購。此時,不管剩餘量 ...
  • 5月中國資料庫排行榜已出爐!各大資料庫廠商表現如何?排名有何變化?一起來解讀吧! ...
  • 本文和接下來的幾篇文章為閱讀郭霖先生所著《第一行代碼:Android(篇第2版)》的學習筆記,按照書中的內容順序進行記錄,書中的Demo本人全部都做過了。 每一章節本人都做了詳細的記錄,以下是我學習記錄(包含大量書中內容的整理和自己在學習中遇到的各種bug及解決方案),方便以後閱讀和查閱。最後,感激 ...
  • 本文和接下來的幾篇文章為閱讀郭霖先生所著《第一行代碼:Android(篇第2版)》的學習筆記,按照書中的內容順序進行記錄,書中的Demo本人全部都做過了。 每一章節本人都做了詳細的記錄,以下是我學習記錄(包含大量書中內容的整理和自己在學習中遇到的各種bug及解決方案),方便以後閱讀和查閱。最後,感激 ...
  • 本文和接下來的幾篇文章為閱讀郭霖先生所著《第一行代碼:Android(篇第2版)》的學習筆記,按照書中的內容順序進行記錄,書中的Demo本人全部都做過了。 每一章節本人都做了詳細的記錄,以下是我學習記錄(包含大量書中內容的整理和自己在學習中遇到的各種bug及解決方案),方便以後閱讀和查閱。最後,感激 ...
  • 本文和接下來的幾篇文章為閱讀郭霖先生所著《第一行代碼:Android(篇第2版)》的學習筆記,按照書中的內容順序進行記錄,書中的Demo本人全部都做過了。 每一章節本人都做了詳細的記錄,以下是我學習記錄(包含大量書中內容的整理和自己在學習中遇到的各種bug及解決方案),方便以後閱讀和查閱。最後,感激 ...
  • 本文和接下來的幾篇文章為閱讀郭霖先生所著《第一行代碼:Android(篇第2版)》的學習筆記,按照書中的內容順序進行記錄,書中的Demo本人全部都做過了。 每一章節本人都做了詳細的記錄,以下是我學習記錄(包含大量書中內容的整理和自己在學習中遇到的各種bug及解決方案),方便以後閱讀和查閱。最後,非常 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...