作者:王大鵬
簡介
OpenAtom OpenHarmony(以下簡稱“OpenHarmony”)電話子系統為 OS 提供了基礎的無線通訊能力。
支援 TD-LTE/FDD-LTE/TD-SCDMA/WCDMA/EVDO/CDMA1X/GSM 等網路制式的通訊模組,能夠提供高速的無線資料傳輸、網際網路接入等業務,具備語音、簡訊、彩信、SIM 卡等功能。
以下行文如無特別說明,所述說均指 OpenHarmony 系統(OpenHarmony 3.0 LTS版本)。
OpenHarmony架構圖
基礎知識
電話子系統
電話子系統做為 OpenHarmony 很重要的組成部分,為系統提供基礎的通訊功能,包括 CS 域的服務,比如語音呼叫、簡訊、呼叫管理;也包括 PS 域的相關服務,比如 MMS、資料業務等,另外 SIM 和 RIL 的業務也在該子系統內。
電話子系統架構圖
OpenHarmony 現有電話子系統蜂窩通話通話相關框架圖:
應用層 :各種需要通話、SMS、資料業務、SIM 卡功能的應用,例如call 應用、SMS 應用、launcher 應用等等。
框架層 :
1.SDK :給應用提供標準介面,包括 JS 介面和 C++ 介面。
2.Framework:嚮應用層提供對應模組穩定的基礎能力,包括 network、call、sms、sim相關的功能,包括通話管理,短彩編輯以及傳送接收,sim 卡的識別駐網,資料業務的管理等。在目前的 OpenHarmony 版本中,call_manager、cellular_call、cellular_data、data_storage、sms_mms、state_registry、core_service 等模組都屬於框架層。
Hril層 :相當於安卓的 RILJ,由於不同方案使用的 Modem 不一樣,所以各種指令格式,初始化序列都不一樣,為了消除這些差別,而 Hril 則提供了無線硬體裝置與電話服務之間的抽象層。
Vendor lib層 :類似於安卓的 RILD,負責與 modem 模組互動,傳送各模組對應的 AT 命令。
Modem層:現在的基帶處理器,主要處理數字訊號、語音訊號的編碼解碼以及通訊協議,而基帶處理器、射頻和其它外圍晶片作為一個 Modem 模組,提供 AT 命令介面給上層用於互動。通訊模組 Modem 通過與通訊網路進行溝通、傳輸語音及資料、完成呼叫、簡訊等相關電話功能。
電話子系統程式碼結構
由於電話子系統包含較多模組,所以將各模組分開描述:
通話管理模組:主要管理 CS(Circuit Switch,電路交換)、IMS(IP Multimedia Subsystem,IP 多媒體子系統)和 OTT(over the top,OTT 解決方案)三種型別的通話,負責申請通話所需要的音視訊資源,並處理多路通話時產生的各種衝突。
1. /base/telephony/call_manager
2. ├─ frameworks # napi介面存放目錄
3. ├─ interfaces # 對外部暴露的介面
4. │ ├─ innerkits # 部件間的內部介面
5. │ └─ kits # js介面存放目錄
6. ├─ sa_profile # 啟動配置檔案
7. ├─ services # 服務內部程式碼
8. │ ├─ audio # 音訊管理相關程式碼
9. │ ├─ bluetooth # 藍芽通話相關程式碼
10. │ ├─ call # 通話業務相關程式碼
11. │ ├─ call_manager_service # 程式服務管理相關程式碼
12. │ ├─ call_setting # 通話設定相關程式碼
13. │ ├─ telephony_interaction # 電話核心服務互動相關程式碼
14. │ └─ call_ui_interaction # UI互動相關程式碼
15. ├─ test # 單元測試相關程式碼
16. └─ utils # 通用工具類
17. ```
蜂窩通話模組:支援基於運營商網路的基礎通話實現,包含基於 2G/3G 的 CS(Circuit Switch,電路交換)通話和基於 4G/5G 的 IMS(IP Multimedia Subsystem,IP 多媒體子系統)通話,包含 VoLTE/ VoWIFI/ VoNR 語音、視訊、會議,支援 CS 和 IMS 通話之間的域選控制和切換,支援緊急通話。支援主流 modem 晶片平臺。
1. /base/telephony/cellular_call # 蜂窩通話子元件
2. ├─ BUILD.gn # 編譯gn指令碼
3. ├─ README.md # Readme文件
4. ├─ services
5. │ ├─ common # 工具倉
6. │ ├─ connection # 連線層
7. │ ├─ control # 控制業務層
8. │ └─ manager # 管理層
9. ├─ sa_profile # sa檔案
10. ├─ ohos.build # 編譯build
11. └─ test # 測試相關
12. ```
蜂窩資料模組:作為電話子系統可裁剪部件,依賴於 core_service 核心服務、ril_adapter。具有蜂窩資料啟用、蜂窩資料異常檢測與恢復、蜂窩資料狀態管理、蜂窩資料開關管理、蜂窩資料漫遊管理、APN 管理、網路管理互動等功能。
1. base/telephony/cellular_data
2. ├── figures
3. ├── frameworks
4. │ ├── js # js檔案
5. │ │ └── napi
6. │ │ ├── include
7. │ │ └── src
8. │ └── native
9. │ └── src
10. ├── interfaces
11. │ ├── innerkits # 對外提供的介面
12. │ └── kits
13. │ └── js # 對外提供的js介面
14. │ └── declaration
15. ├── sa_profile # SA配置
16. ├── services
17. │ ├── include # 標頭檔案
18. │ ├── apn_manager
19. │ │ ├── common
20. │ │ ├── state_machine
21. │ │ └── utils
22. │ └── src # 原始檔
23. │ ├── apn_manager
24. │ ├── state_machine
25. │ └── utils
26. └── test
27. └── unit_test # 單元測試相關程式碼
28. ```
電話核心服務模組:主要功能是初始化 RIL 管理、SIM 卡和搜網模組,以及獲取 RIL Adapter 服務。通過註冊回撥服務,實現與 RIL Adapter 進行通訊;通過釋出訂閱,來實現與各功能模組的通訊。
1. /base/telphony/core_service
2. ├── interfaces # 介面目錄
3. │ ├── innerkits # 部件間的內部介面
4. │ └── kits # 對應用提供的介面(例如JS介面)
5. ├── services # 核心服務實現程式碼目錄
6. │ ├── include
7. │ └── src
8. ├── etc # 核心服務的驅動指令碼目錄
9. │ └── init
10. ├── sa_profile # 核心服務的啟動檔案目錄
11. ├── tel_ril # 核心服務與RIL Adapter通訊程式碼目錄
12. │ ├── include
13. │ ├── src
14. │ └── test
15. ├── network_search # 搜網服務程式碼目錄
16. │ ├── include
17. │ ├── src
18. │ └── test
19. ├── sim # SIM卡服務程式碼目錄
20. │ ├── include
21. │ ├── src
22. │ └── test
23. ├── frameworks # frameworks目錄
24. │ ├── js
25. │ ├── nstive
26. ├── common # 各個業務模組標頭檔案目錄
27. │ ├── api
28. │ ├── call_manager
29. │ ├── network_search
30. │ └── sim
31. ├── utils
32. │ ├── log # 核心服務日誌列印目錄
33. │ ├── preferences
34. │ ├── common
35. ```
資料庫及持久化模組:負責電話服務子系統中的 SIM 卡/短彩信等模組持久化資料儲存,提供 DataAbility 訪問介面。
1. /base/telephony/data_storage # 資料庫及持久化
2. ├─ BUILD.gn # 編譯gn指令碼
3. ├─ README.md # Readme文件
4. ├─ common # 公共、通用檔案
5. │ ├─ include
6. │ └─ src
7. ├─ pdp_profile # 網路運營商
8. │ ├─ include
9. │ └─ src
10. ├─ sim # sim卡
11. │ ├─ include
12. │ └─ src
13. ├─ sms_mms # 短彩信
14. │ ├─ include
15. │ └─ src
16. ├─ ohos.build # 編譯build
17. └─ test # 測試相關
18. ```
RIL Adapter模組:主要包括廠商庫載入,業務介面實現以及事件排程管理。主要用於遮蔽不同 modem 廠商硬體差異,為上層提供統一的介面,通過註冊 HDF 服務與上層介面通訊。
1. base/telephony/ril_adapter
2. ├─ hril # hri層的各個業務模組介面實現
3. ├─ hril_hdf # HDF服務
4. ├─ include # 標頭檔案存放目錄
5. ├─ interfaces # 對應提供上層各業務內部介面
6. │ └─ innerkits
7. ├─ test # 單元測試相關程式碼
8. │ ├─ mock
9. │ └─ unittest # 單元測試程式碼
10. └─ vendor # 廠商庫程式碼
11. │ └─ include
12. ```
短彩信模組:為移動資料使用者提供簡訊收發和彩信編解碼功能,主要功能有 GSM/CDMA 簡訊收發、簡訊 PDU(Protocol data unit,協議資料單元)編解碼、Wap Push 接收處理 、小區廣播接收、彩信通知、 彩信編解碼和 SIM 卡簡訊記錄增刪改查等。
1. /base/telephony/sms_mms
2. ├─ interfaces # 對外暴露的介面
3. │ └─ kits
4. ├─ sa_profile # 啟動配置檔案
5. ├─ services # 服務內部程式碼
6. │ ├─ include # 標頭檔案目錄
7. │ ├─ cdma # CDMA制式原始檔
8. │ └─ gsm # GSM制式原始檔
9. ├─ test # 單元測試目錄
10. └─ utils # 通用工具相關
11. ```
狀態註冊模組:主要負責提供電話服務子系統各種訊息事件的訂閱以及取消訂閱的 API。事件型別包括網路狀態變化、訊號強度變化、小區資訊變化、蜂窩資料連線狀態變化、通話狀態變化等等。
1. /base/telephony/state_registry # 狀態註冊轉發服務
2. ├─ BUILD.gn # 編譯gn指令碼
3. ├─ README.md # Readme文件
4. ├─ interfaces # API,js檔案
5. ├─ service
6. │ ├─ include # 標頭檔案
7. │ └─ src # 原始檔
8. ├─ sa_profile # sa檔案
9. ├─ ohos.build # 編譯build
10. └─ test # 測試相關
11. ```
相關倉
核心服務 :https://gitee.com/openharmony...
蜂窩通話 :https://gitee.com/openharmony...
通話管理 :https://gitee.com/openharmony...
註冊服務 :https://gitee.com/openharmony...
短彩信 :https://gitee.com/openharmony...
riladapter:https://gitee.com/openharmony...
資料業務 :https://gitee.com/openharmony...
資料儲存 :https://gitee.com/openharmony...
網路管理 :https://gitee.com/openharmony...
電話子系統(call)核心類
原始碼解析
做為電話子系統的核心業務,通話功能(Call)除了需要硬體支援,比如音訊模組、基帶模組等,還需要系統本身的許多服務相互配合才能實現該功能,比如:通話管理(call_manager)、蜂窩通話服務(cellular_call)、Telephony 核心服務(core_service)、RIL 適配(ril_adapter),狀態註冊服務(state_registry)等。
通話目前主要分成三種:CS Call(Circuit Switch,電路交換)、IMS Call(IP Multimedia Subsystem,IP 多媒體子系統)和 OTT Call(over the top,OTT 解決方案)三種型別的通話。對上層 Call 應用暴露的介面包括:dial、answer、reject、hangup、holdCall、unHoldCall、switchCal、startDTMF、stopDTMF 等。由於事件較多,而且各事件處理流程比較類似,所以我們此處以 Call 的 Answer 事件處理流程來闡述。
通話上層呼叫程式碼分析
當有電話呼入時,Callui 介面會顯示電話呼入,使用者點選 answer 按鍵,則會啟用 Call 的 Answer 流程。
1. <div class="bottom-btn-wrap">
2. <div class="btn-item">
3. <div class="btn-box" @click="onReject">
4. <image src="assets/picture/hangUP.png"></image>
5. </div>
6. </div>
7. <div class="btn-item">
8. <div class="btn-box" @click="onAnswer">
9. <image src="assets/picture/answer.png"></image>
10. </div>
11. </div>
12. </div>
此處會呼叫 incomingCom.js 的 onAnswer 函式。
1. /**
2. * Answer the phone interface
3. */
4. onAnswer() {
5. console.log('onAnswer function start');
6. console.log('this.callData: ' + JSON.stringify(this.callData));
7. acceptCall(this.callData.callId);
8. },
由於之前已經 import了callServiceProxy.js 檔案。
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';
通話框架層程式碼分析
3.2.1通話框架層程式碼呼叫時序圖
3.2.2通話框架層程式碼分析
從上邊的時序圖可以看出,整個 Answer 的呼叫流程是比較長的,整個框架層處理跨越包括 call_manager、cellular_call、core_service、IPC、ril_adapter 等多個服務。由於處理流程過長,具體的呼叫情況可以參照時序圖,此處我們將框架層的處理一些關鍵地方根據呼叫不同的服務來進行描述。
3.2.2.1Answer事件在call_manager中的處理
之前應用層呼叫的 call.answer 是通過 @ohos.telephony.call 引入的,而實際定義在 call_manage 服務中的 interfaces 內的 napi 介面中實現。
1. static napi_module g_nativeCallManagerModule = {
2. .nm_version = NATIVE_VERSION,
3. .nm_flags = NATIVE_FLAGS,
4. .nm_filename = nullptr,
5. .nm_register_func = NapiCallManager::RegisterCallManagerFunc,
6. .nm_modname = "telephony.call",
7. .nm_priv = ((void *)0),
8. .reserved = {0},
9. };
註冊要用到介面以及一些列舉引數
napi_valueNapiCallManager::RegisterCallManagerFunc(napi_env env, napi_value exports)
1. napi_value NapiCallManager::RegisterCallManagerFunc(napi_env env, napi_value exports)
2. {
3. DeclareCallBasisInterface(env, exports);
4. DeclareCallConferenceInterface(env, exports);
5. DeclareCallSupplementInterface(env, exports);
6. DeclareCallExtendInterface(env, exports);
7. DeclareCallMultimediaInterface(env, exports);
8. DeclareCallMediaEnum(env, exports);
9. DeclareCallDialEnum(env, exports);
10. DeclareCallStateEnum(env, exports);
11. DeclareCallEventEnum(env, exports);
12. DeclareCallRestrictionEnum(env, exports);
13. DeclareCallWaitingEnum(env, exports);
14. DeclareCallTransferEnum(env, exports);
15. std::u16string bundleName = GetBundleName(env);
16. Init(bundleName);
17. return exports;
18. }
這裡我們需要的是 CallBasis 介面,具體內容如下所述,這些就是之前應用層呼叫的一些事件對應的處理函式。
1. napi_value NapiCallManager::DeclareCallBasisInterface(napi_env env, napi_value exports)
2. {
3. napi_property_descriptor desc[] = {
4. DECLARE_NAPI_FUNCTION("dial", DialCall),
5. DECLARE_NAPI_FUNCTION("answer", AnswerCall),
6. DECLARE_NAPI_FUNCTION("reject", RejectCall),
7. DECLARE_NAPI_FUNCTION("hangup", HangUpCall),
8. DECLARE_NAPI_FUNCTION("holdCall", HoldCall),
9. DECLARE_NAPI_FUNCTION("unHoldCall", UnHoldCall),
10. DECLARE_NAPI_FUNCTION("switchCall", SwitchCall),
11. DECLARE_NAPI_FUNCTION("upgradeCall", UpgradeCall),
12. DECLARE_NAPI_FUNCTION("downgradeCall", DowngradeCall),
13. DECLARE_NAPI_FUNCTION("setCallPreferenceMode", SetCallPreferenceMode),
14. };
15. NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc));
16. return exports;
17. }
因為我們對應的是 answer 事件,所以這裡的對應函式是 AnswerCall。
1. napi_value NapiCallManager::AnswerCall(napi_env env, napi_callback_info info)
2. {
3. GET_PARAMS(env, info, VALUE_MAXIMUM_LIMIT);
4. NAPI_ASSERT(env, argc <= VALUE_MAXIMUM_LIMIT, "parameter error!");
5. bool matchFlag = MatchValueType(env, argv[ARRAY_INDEX_FIRST], napi_number);
6. NAPI_ASSERT(env, matchFlag, "Type error, should be number type");
7. auto asyncContext = (std::make_unique<AnswerAsyncContext>()).release();
8. napi_get_value_int32(env, argv[ARRAY_INDEX_FIRST], &asyncContext->callId);
9. if (argc == TWO_VALUE_LIMIT) {
10. if (MatchValueType(env, argv[ARRAY_INDEX_SECOND], napi_function)) {
11. napi_create_reference(env, argv[ARRAY_INDEX_SECOND], DATA_LENGTH_ONE, & (asyncContext->callbackRef));
12. } else if (MatchValueType(env, argv[ARRAY_INDEX_SECOND], napi_number)) {
13. asyncContext->videoState = GetIntProperty(env, argv[ARRAY_INDEX_SECOND], "videoState");
14. }
15. } else if (argc == VALUE_MAXIMUM_LIMIT) {
16. asyncContext->videoState = GetIntProperty(env, argv[ARRAY_INDEX_SECOND], "videoState");
17. napi_create_reference(env, argv[ARRAY_INDEX_THIRD], DATA_LENGTH_ONE, & (asyncContext->callbackRef));
18. }
19. return HandleAsyncWork(env, asyncContext, "AnswerCall", NativeAnswerCall, NativeVoidCallBack);
20. }
繼續往下呼叫,從之前的函式看 AnswerCall 應該是非同步處理的。
1. void NapiCallManager::NativeAnswerCall(napi_env env, void *data)
2. {
3. if (data == nullptr) {
4. TELEPHONY_LOGE("data is nullptr");
5. return;
6. }
7. auto asyncContext = (AnswerAsyncContext *)data;
8. int32_t ret = DelayedSingleton<CallManagerProxy>::GetInstance()->AnswerCall(
9. asyncContext->callId, asyncContext->videoState);
10. asyncContext->result = ret;
11. }
在一路呼叫的的過程中,會呼叫 CallPolicy 類的 AnswerCallPolicy 函式用來判斷對應 callId 的 CallObject 是否存在,如有有就判斷 Call 所處的狀態。
1. int32_t CallPolicy::AnswerCallPolicy(int32_t callId)
2. {
3. if (!IsCallExist(callId)) {
4. TELEPHONY_LOGE("callId is invalid, callId:%{public}d", callId);
5. return CALL_ERR_CALLID_INVALID;
6. }
7. TelCallState state = GetCallState(callId);
8. if (state != CALL_STATUS_INCOMING && state != CALL_STATUS_WAITING) {
9. TELEPHONY_LOGE("current call state is:%{public}d, accept call not allowed", state);
10. return CALL_ERR_ILLEGAL_CALL_OPERATION;
11. }
12. return TELEPHONY_SUCCESS;
13. }
在後續呼叫到 CallRequestHandlerService 的 AnswerCall 進行處理時,如果有 CallRequestHandler 的 handler_存在,則 make 個 AnswerCallPara 的 unique_ptr,然後將 callid 和 videostate 傳入該指標。呼叫 SendEvent 將 HANDLER_ANSWER_CALL_REQUEST 發出。
1. int32_t CallRequestHandlerService::AnswerCall(int32_t callId, int32_t videoState)
2. {
3. if (handler_.get() == nullptr) {
4. TELEPHONY_LOGE("handler_ is nullptr");
5. return TELEPHONY_ERR_FAIL;
6. }
7. std::unique_ptr<AnswerCallPara> para = std::make_unique<AnswerCallPara>();
8. if (para.get() == nullptr) {
9. TELEPHONY_LOGE("make_unique AnswerCallPara failed!");
10. return TELEPHONY_ERR_FAIL;
11. }
12. para->callId = callId;
13. para->videoState = videoState;
14. if (!handler_->SendEvent(HANDLER_ANSWER_CALL_REQUEST, std::move(para))) {
15. TELEPHONY_LOGE("send accept event failed!");
16. return TELEPHONY_ERR_FAIL;
17. }
18. return TELEPHONY_SUCCESS;
19. }
從程式碼的處理流程看,這裡發出的 event 會被同一個檔案中的 CallRequestHandler 類捕獲,觸發 ProcessEvent 處理。
1. void CallRequestHandler::ProcessEvent(const AppExecFwk::InnerEvent::Pointer &event)
2. {
3. if (event == nullptr) {
4. TELEPHONY_LOGE("CallRequestHandler::ProcessEvent parameter error");
5. return;
6. }
7. TELEPHONY_LOGD("CallRequestHandler inner event id obtained: %{public}u.", event- >GetInnerEventId());
8. auto itFunc = memberFuncMap_.find(event->GetInnerEventId());
9. if (itFunc != memberFuncMap_.end()) {
10. auto memberFunc = itFunc->second;
11. if (memberFunc != nullptr) {
12. return (this->*memberFunc)(event);
13. }
14. }
15. }
memberFuncMap 會根據 event 的 id 從 memberFuncMap_拿到對應的 memberFunc,然後進入對應的處理函式。
1. CallRequestHandler::CallRequestHandler(const std::shared_ptr<AppExecFwk::EventRunner> &runner)
2. : AppExecFwk::EventHandler(runner), callRequestProcessPtr_(nullptr)
3. {
4. memberFuncMap_[CallRequestHandlerService::HANDLER_DIAL_CALL_REQUEST] = &CallRequestHandler::DialCallEvent;
5. memberFuncMap_[CallRequestHandlerService::HANDLER_ANSWER_CALL_REQUEST] = &CallRequestHandler::AcceptCallEvent;
6. memberFuncMap_[CallRequestHandlerService::HANDLER_REJECT_CALL_REQUEST] = &CallRequestHandler::RejectCallEvent;
7. memberFuncMap_[CallRequestHandlerService::HANDLER_HANGUP_CALL_REQUEST] = &CallRequestHandler::HangUpCallEvent;
8. memberFuncMap_[CallRequestHandlerService::HANDLER_HOLD_CALL_REQUEST] = &CallRequestHandler::HoldCallEvent;
9. memberFuncMap_[CallRequestHandlerService::HANDLER_UNHOLD_CALL_REQUEST] = &CallRequestHandler::UnHoldCallEvent;
10. memberFuncMap_[CallRequestHandlerService::HANDLER_SWAP_CALL_REQUEST] = &CallRequestHandler::SwitchCallEvent;
11. memberFuncMap_[CallRequestHandlerService::HANDLER_COMBINE_CONFERENCE_REQUEST] =
12. &CallRequestHandler::CombineConferenceEvent;
13. memberFuncMap_[CallRequestHandlerService::HANDLER_SEPARATE_CONFERENCE_REQUEST] =
14. &CallRequestHandler::SeparateConferenceEvent;
15. memberFuncMap_[CallRequestHandlerService::HANDLER_UPGRADE_CALL_REQUEST] = &CallRequestHandler::UpgradeCallEvent;
16. memberFuncMap_[CallRequestHandlerService::HANDLER_DOWNGRADE_CALL_REQUEST] =
17. &CallRequestHandler::DowngradeCallEvent;
18. }
這裡對應的處理函式是 AcceptCallEvent(),這部分函式無返回值,又從 event 中取出 callId 和 videoState。
1. void CallRequestHandler::AcceptCallEvent(const AppExecFwk::InnerEvent::Pointer &event)
2. {
3. if (event == nullptr) {
4. TELEPHONY_LOGE("CallRequestHandler::ProcessEvent parameter error");
5. return;
6. }
7. auto object = event->GetUniqueObject<AnswerCallPara>();
8. if (object == nullptr) {
9. TELEPHONY_LOGE("object is nullptr!");
10. return;
11. }
12. AnswerCallPara acceptPara = *object;
13. if (callRequestProcessPtr_ == nullptr) {
14. TELEPHONY_LOGE("callRequestProcessPtr_ is nullptr");
15. return;
16. }
17. callRequestProcessPtr_->AnswerRequest(acceptPara.callId, acceptPara.videoState);
18. }
繼續呼叫 CallRequestProcessl 類中的 AnswerReques 函式時,會通過 GetOneCallObject 拿到不同的 CallObject。
1. void CallRequestProcess::AnswerRequest(int32_t callId, int32_t videoState)
2. {
3. sptr<CallBase> call = GetOneCallObject(callId);
4. if (call == nullptr) {
5. TELEPHONY_LOGE("the call object is nullptr, callId:%{public}d", callId);
6. return;
7. }
8. int32_t ret = call->AnswerCall(videoState);
9. if (ret != TELEPHONY_SUCCESS) {
10. TELEPHONY_LOGE("AnswerCall failed!");
11. return;
12. }
13. DelayedSingleton<CallControlManager>::GetInstance()->NotifyIncomingCallAnswered(call);
14. }
拿到了對應的 call 之後,就可以呼叫對應的 call 的 AnswerCall 函式,這個函式會覆蓋 BaseCall 的虛擬函式。由於 CSCall、IMSCall、OTTCall 都有對應的 AnswerCall 函式,所以實際使用那個是由 BaseCall 類的虛擬函式 AnswerCall 被那個子類重寫,從而呼叫不同的 AnswerCall 函式。這裡我們假設為 CsCall 。
1. int32_t CSCall::AnswerCall(int32_t videoState)
2. {
3. return CarrierAcceptCall(videoState);
4. }
呼叫到 CarrierCall 的 CarrierAcceptCall 進行後續處理。其中 AcceptCallBase() 函式會判斷 Call 狀態,如果是 CALL_RUNNING_STATE_RINGING 狀態,則會呼叫 AudioControlManager 的 SetVolumeAudible 來設定 audio 的相關內容。
1. int32_t CarrierCall::CarrierAcceptCall(int32_t videoState)
2. {
3. CellularCallInfo callInfo;
4. AcceptCallBase();
5. PackCellularCallInfo(callInfo);
6. int32_t ret = DelayedSingleton<CellularCallIpcInterfaceProxy>::GetInstance()->Answer(callInfo);
7. if (ret != TELEPHONY_SUCCESS) {
8. TELEPHONY_LOGE("Accept failed!");
9. return CALL_ERR_ACCEPT_FAILED;
10. }
11. return TELEPHONY_SUCCESS;
12. }
處理完 AcceptCallBase() 後,需要用 PackCellularCallInfo 將 call 的資訊打包進給 callinfo 中,後續直接該 callInfo 傳遞出去。
1. void CarrierCall::PackCellularCallInfo(CellularCallInfo &callInfo)
2. {
3. callInfo.callId = callId_;
4. callInfo.callType = callType_;
5. callInfo.videoState = (int32_t)videoState_;
6. callInfo.index = index_;
7. callInfo.slotId = slotId_;
8. (void)memset_s(callInfo.phoneNum, kMaxNumberLen, 0, kMaxNumberLen);
9. if (memcpy_s(callInfo.phoneNum, kMaxNumberLen, accountNumber_.c_str(), accountNumber_.length()) != 0) {
10. TELEPHONY_LOGW("memcpy_s failed!");
11. return;
12. }
13. }
下一步會呼叫 CellularCallIpcInterfaceProxy 類的來 Answer 函式來繼續處理。首先要判斷是否有 ReConnectService 的必要,這樣操作是為了保證有可用的 cellularCallInterfacePtr_。cellularCallInterfacePtr_ 是一個 IRemoteBroker 類,該類是一個 IPC 基類介面用於 IPC 通訊。
1. int CellularCallIpcInterfaceProxy::Answer(const CellularCallInfo &callInfo)
2. {
3. if (ReConnectService() != TELEPHONY_SUCCESS) {
4. return TELEPHONY_ERR_IPC_CONNECT_STUB_FAIL;
5. }
6. std::lock_guard<std::mutex> lock(mutex_);
7. int errCode = cellularCallInterfacePtr_->Answer(callInfo);
8. if (errCode != TELEPHONY_SUCCESS) {
9. TELEPHONY_LOGE("answering call failed, errcode:%{public}d", errCode);
10. return TELEPHONY_ERR_FAIL;
11. }
12. return TELEPHONY_SUCCESS;
13. }
在此之前 call_manager 已經在 CellularCallIpcInterfaceProxy 的初始化中將 systemAbilityId(TELEPHONY_CELLULAR_CALL_SYS_ABILITY_ID) 通過ConnectService()完成對應 SA 代理 IRemoteObject 的獲取,然後構造對應的 Proxy 類,這樣就能保證 IPC 通訊的 proxy 和 stub 的對應。下節我們會看到,對應的 stub 其實是在 cellular_call 中註冊的。
1. void CellularCallIpcInterfaceProxy::Init(int32_t systemAbilityId)
2. {
3. systemAbilityId_ = systemAbilityId;
4.
5. int32_t result = ConnectService();
6. if (result != TELEPHONY_SUCCESS) {
7. TELEPHONY_LOGE("connect service failed,errCode: %{public}X", result);
8. Timer::start(CONNECT_SERVICE_WAIT_TIME, CellularCallIpcInterfaceProxy::task);
9. return;
10. }
11. TELEPHONY_LOGI("connected to cellular call service successfully!");
12. }
當呼叫到 cellularCallInterfacePtr_->Answer 時,CellularCallInterface 的 Answer 虛擬函式,會被 CellularCallProxy 的 Answer 重寫,所以實際執行 CellularCallProxy 的 Answer 函式,它是一個 IRemoteProxy 類。而 callInfo 資訊則轉換成 MessageParcel 形式通過 IPC 的 SendRequest 發出。
class CellularCallProxy : public IRemoteProxy<CellularCallInterface> {
1. int32_t CellularCallProxy::Answer(const CellularCallInfo &callInfo)
2. {
3. MessageOption option;
4. MessageParcel in;
5. MessageParcel out;
6.
7. if (!in.WriteInterfaceToken(CellularCallProxy::GetDescriptor())) {
8. return TELEPHONY_ERR_WRITE_DESCRIPTOR_TOKEN_FAIL;
9. }
10. if (!in.WriteInt32(MAX_SIZE)) {
11. return TELEPHONY_ERR_WRITE_DATA_FAIL;
12. }
13. if (!in.WriteRawData((const void *)&callInfo, sizeof(CellularCallInfo))) {
14. return TELEPHONY_ERR_WRITE_DATA_FAIL;
15. }
16. int32_t error = Remote()->SendRequest(ANSWER, in, out, option);
17. if (error == ERR_NONE) {
18. return out.ReadInt32();
19. }
20. return error;
21. }
從這裡開始,Answer 流程在 call_manager 的處理已經完成。
3.2.2.2 Answer事件在cellular_call中的處理
IPC(Inter-Process Communication)與RPC(Remote Procedure Call)機制用於實現跨程式通訊,不同的是前者使用 Binder 驅動,用於裝置內的跨程式通訊,而後者使用軟匯流排驅動,用於跨裝置跨程式通訊。IPC 和 RPC 通常採用客戶端-伺服器(Client-Server)模型,服務請求方(Client)可獲取提供服務提供方(Server)的代理 (Proxy),並通過此代理讀寫資料來實現程式間的資料通訊。通常,Server 會先註冊系統能力(System Ability)到系統能力管理者(System Ability Manager,縮寫 SAMgr)中,SAMgr 負責管理這些 SA 並向 Client 提供相關的介面。Client 要和某個具體的 SA 通訊,必須先從 SAMgr 中獲取該 SA 的代理,然後使用代理和 SA 通訊。下文使用 Proxy 表示服務請求方,Stub 表示服務提供方。實現程式碼在 /foundation/communication/ipc 目錄下。
SA註冊與啟動:SA 需要將自己的 AbilityStub 例項通過 AddSystemAbility 介面註冊到 SystemAbilityManager。
SA獲取與呼叫:通過 SystemAbilityManager 的 GetSystemAbility 方法獲取到對應 SA 的代理 IRemoteObject,然後構造 AbilityProxy。這樣就能保證 proxy 和 stub 的對應。
上一節我們在 call_manager 中看到了 SA 的獲取過程,那這裡我們來看下該 SA 的註冊過程,還記得 TELEPHONY_CELLULAR_CALL_SYS_ABILITY_ID這個systemAbilityId嗎?它就是我們註冊 SA 的關鍵。
1. bool g_registerResult =
2. SystemAbility::MakeAndRegisterAbility(DelayedSingleton<CellularCallService>::GetInstance().get());
3.
4. CellularCallService::CellularCallService() : SystemAbility(TELEPHONY_CELLULAR_CALL_SYS_ABILITY_ID, true)
5. {
6. state_ = ServiceRunningState::STATE_STOPPED;
7. }
這樣我們就完成 stub 的註冊,之前通過 proxy 發出的訊息就會通過 IPC 傳遞到這裡的 stub 中。IPC 過程比較複雜,這裡就不深入討論了,有興趣的同學可以自學一下。
由於 IPCObjectStub 的 OnRemoteRequest 是虛擬函式會被繼承 IPCObjectStub 類的 CellularCallStub 的 OnRemoteRequest 函式重寫。
1. int32_t CellularCallStub::OnRemoteRequest(
2. uint32_t code, MessageParcel &data, MessageParcel &reply, MessageOption &option)
3. {
4. std::u16string myDescriptor = CellularCallStub::GetDescriptor();
5. std::u16string remoteDescriptor = data.ReadInterfaceToken();
6. if (myDescriptor != remoteDescriptor) {
7. TELEPHONY_LOGE("descriptor checked fail");
8. return TELEPHONY_ERR_DESCRIPTOR_MISMATCH;
9. }
10.
11. auto itFunc = requestFuncMap_.find(code);
12. if (itFunc != requestFuncMap_.end()) {
13. auto requestFunc = itFunc->second;
14. if (requestFunc != nullptr) {
15. return (this->*requestFunc)(data, reply);
16. }
17. }
18. TELEPHONY_LOGD("CellularCallStub::OnRemoteRequest, default case, need check.");
19. return IPCObjectStub::OnRemoteRequest(code, data, reply, option);
20. }
此處會根據 map 表來找到對應的處理函式,之前我們的 code = 4 是 ANSWER,此處應該會進行對應的處理。
1. CellularCallStub::CellularCallStub()
2. {
3. Init();
4. TELEPHONY_LOGD("CellularCallStub::CellularCallStub");
5. requestFuncMap_[DIAL] = &CellularCallStub::DialInner;
6. requestFuncMap_[HANG_UP] = &CellularCallStub::HangUpInner;
7. requestFuncMap_[REJECT] = &CellularCallStub::RejectInner;
8. requestFuncMap_[ANSWER] = &CellularCallStub::AnswerInner;
9. requestFuncMap_[EMERGENCY_CALL] = &CellularCallStub::IsEmergencyPhoneNumberInner;
10. requestFuncMap_[HOLD_CALL] = &CellularCallStub::HoldCallInner;
11. requestFuncMap_[UN_HOLD_CALL] = &CellularCallStub::UnHoldCallInner;
12. requestFuncMap_[SWITCH_CALL] = &CellularCallStub::SwitchCallInner;
13. requestFuncMap_[COMBINE_CONFERENCE] = &CellularCallStub::CombineConferenceInner;
14. requestFuncMap_[SEPARATE_CONFERENCE] = &CellularCallStub::SeparateConferenceInner;
15. requestFuncMap_[CALL_SUPPLEMENT] = &CellularCallStub::CallSupplementInner;
16. requestFuncMap_[REGISTER_CALLBACK] = &CellularCallStub::RegisterCallBackInner;
17. requestFuncMap_[UNREGISTER_CALLBACK] = &CellularCallStub::UnRegisterCallBackInner;
18. requestFuncMap_[START_DTMF] = &CellularCallStub::StartDtmfInner;
19. requestFuncMap_[STOP_DTMF] = &CellularCallStub::StopDtmfInner;
20. requestFuncMap_[SEND_DTMF] = &CellularCallStub::SendDtmfInner;
21. requestFuncMap_[SEND_DTMF_STRING] = &CellularCallStub::SendDtmfStringInner;
22. requestFuncMap_[SET_CALL_TRANSFER] = &CellularCallStub::SetCallTransferInner;
23. requestFuncMap_[GET_CALL_TRANSFER] = &CellularCallStub::GetCallTransferInner;
24. requestFuncMap_[SET_CALL_WAITING] = &CellularCallStub::SetCallWaitingInner;
25. requestFuncMap_[GET_CALL_WAITING] = &CellularCallStub::GetCallWaitingInner;
26. requestFuncMap_[SET_CALL_RESTRICTION] = &CellularCallStub::SetCallRestrictionInner;
27. requestFuncMap_[GET_CALL_RESTRICTION] = &CellularCallStub::GetCallRestrictionInner;
28. requestFuncMap_[SET_CALL_PREFERENCE_MODE] = &CellularCallStub::SetCallPreferenceModeInner;
29. requestFuncMap_[GET_CALL_PREFERENCE_MODE] = &CellularCallStub::GetCallPreferenceModeInner;
30. requestFuncMap_[SET_LTE_IMS_SWITCH_STATUS] = &CellularCallStub::SetLteImsSwitchStatusInner;
31. requestFuncMap_[GET_LTE_IMS_SWITCH_STATUS] = &CellularCallStub::GetLteImsSwitchStatusInner;
32. requestFuncMap_[CTRL_CAMERA] = &CellularCallStub::CtrlCameraInner;
33. requestFuncMap_[SET_PREVIEW_WINDOW] = &CellularCallStub::SetPreviewWindowInner;
34. requestFuncMap_[SET_DISPLAY_WINDOW] = &CellularCallStub::SetDisplayWindowInner;
35. requestFuncMap_[SET_CAMERA_ZOOM] = &CellularCallStub::SetCameraZoomInner;
36. requestFuncMap_[SET_PAUSE_IMAGE] = &CellularCallStub::SetPauseImageInner;
37. requestFuncMap_[SET_DEVICE_DIRECTION] = &CellularCallStub::SetDeviceDirectionInner;
38. }
AnswerInner 會進行後續的處理,這裡會從 data 中解析出對應的 callinfo 資訊,在函式末尾出呼叫本檔案的 Answer(),並將返回的結果寫入 reply 中,這個返回值是函式執行的結果,為 Int32 整形值。
1. int32_t CellularCallStub::AnswerInner(MessageParcel &data, MessageParcel &reply)
2. {
3. TELEPHONY_LOGD("CellularCallStub::AnswerInner ANSWER");
4. int32_t size = data.ReadInt32();
5. TELEPHONY_LOGD("CellularCallStub::OnRemoteRequest:size=%{public}u, MAX_SIZE=%{public}u\n", size, MAX_SIZE);
6. size = ((size > MAX_SIZE) ? 0 : size);
7. if (size <= 0) {
8. TELEPHONY_LOGE("CellularCallStub::OnRemoteRequest data size error");
9. return TELEPHONY_ERR_FAIL;
10. }
11. auto pCallInfo = (CellularCallInfo *)data.ReadRawData(sizeof(CellularCallInfo));
12. if (pCallInfo == nullptr) {
13. TELEPHONY_LOGE("AnswerInner return, pCallInfo is nullptr.");
14. return TELEPHONY_ERR_ARGUMENT_INVALID;
15. }
16. reply.WriteInt32(Answer(*pCallInfo));
17. return TELEPHONY_SUCCESS;
18. }
Answer 函式是來完成跟ril互動的關鍵。首先根據 callInfo 的資訊獲得 calltype,目前支援三種 calltype,分別為 cs_call、ims_call、ott_call。在確定 calltype 後,通過 slotId_ 在 GetCsControl() 獲得對應的 CSControl 物件。
1. int32_t CellularCallStub::Answer(const CellularCallInfo &callInfo)
2. {
3. if (!IsValidSlotId(callInfo.slotId)) {
4. TELEPHONY_LOGE("CellularCallStub::Answer return, invalid slot id");
5. return CALL_ERR_INVALID_SLOT_ID;
6. }
7. if (CallType::TYPE_CS == callInfo.callType) {
8. auto control = GetCsControl(slotId_);
9. if (control == nullptr) {
10. TELEPHONY_LOGE("CellularCallStub::Answer return, control is nullptr");
11. return TELEPHONY_ERR_LOCAL_PTR_NULL;
12. }
13. return control->Answer(callInfo);
14. } else if (CallType::TYPE_IMS == callInfo.callType) {
15. auto imsControl = GetImsControl(slotId_);
16. if (imsControl == nullptr) {
17. TELEPHONY_LOGE("CellularCallStub::Answer return, imsControl is nullptr");
18. return TELEPHONY_ERR_LOCAL_PTR_NULL;
19. }
20. return imsControl->Answer(callInfo);
21. }
22. TELEPHONY_LOGE("CellularCallStub::Answer return, call type error.");
23. return TELEPHONY_ERR_ARGUMENT_INVALID;
24. }
這個呼叫到 CSControl::Answer 函式,會根據 callInfo 拿到對應的 CellularCallConnectionCS 和 CALL_STATUS,狀態為來電、響鈴、等待狀態,則會呼叫 CellularCallConnectionCS 對應的函式。
1. int32_t CSControl::Answer(const CellularCallInfo &callInfo)
2. {
3. auto pConnection =
4. GetConnectionData<CsConnectionMap &, CellularCallConnectionCS *>(connectionMap_, callInfo.phoneNum);
5. if (pConnection == nullptr) {
6. TELEPHONY_LOGI("Answer: connection cannot be matched, use index directly");
7. pConnection =
8. FindConnectionByIndex<CsConnectionMap &, CellularCallConnectionCS *>(connectionMap_, callInfo.index);
9. }
10. if (pConnection == nullptr) {
11. TELEPHONY_LOGE("Answer return, error type: connection is null");
12. return CALL_ERR_CALL_CONNECTION_NOT_EXIST;
13. }
14.
15. /**
16. * <stat> (state of the call):
17. * 0 active
18. * 1 held
19. * 2 dialing (MO call)
20. * 3 alerting (MO call)
21. * 4 incoming (MT call)
22. * 5 waiting (MT call)
23. */
24. // There is an active call when you call, or third party call waiting
25. if (IsInState(connectionMap_, CALL_STATUS_ACTIVE) || pConnection->GetStatus() == CALL_STATUS_WAITING) {
26. TELEPHONY_LOGD("Answer there is an active call when you call, or third party call waiting");
27. auto con =
28. FindConnectionByState<CsConnectionMap &, CellularCallConnectionCS *>(connectionMap_, CALL_STATUS_ACTIVE);
29. if (con != nullptr) {
30. /**
31. * shows commands to start the call, to switch from voice to data (In Call Modification) and to hang up
32. * the call. +CMOD and +FCLASS commands indicate the current settings before dialling or answering
33. * command, not that they shall be given just before D or A command.
34. */
35. TELEPHONY_LOGD("Answer: There is an active session currently, and it needs to hold");
36. con->SwitchCallRequest(GetSlotId());
37. } else {
38. TELEPHONY_LOGE("Answer return, error type: con is null, there are no active calls");
39. }
40. }
41. if (pConnection->GetStatus() == CALL_STATUS_INCOMING || pConnection->GetStatus() == CALL_STATUS_ALERTING ||
42. pConnection->GetStatus() == CALL_STATUS_WAITING) {
43. return pConnection->AnswerRequest(GetSlotId());
44. }
45.
46. TELEPHONY_LOGE("CSControl::Answer return, error type: call state error, phone not ringing.");
47. return CALL_ERR_CALL_STATE;
48. }
後續會呼叫 CellularCallConnectionCS::AnswerRequest 和 core_service 進行互動了。GetCore 會根據 slotId 得到對應的 Core,然後用 Get 函式得到對應的 event,設定 Owner 為 CellularCallHandler,等待返回時進行回撥對應的 handler。
1. int32_t CellularCallConnectionCS::AnswerRequest(int32_t slotId)
2. {
3. auto core = GetCore(slotId);
4. if (core == nullptr) {
5. TELEPHONY_LOGE("AnswerRequest return, error type: core is nullptr.");
6. return CALL_ERR_RESOURCE_UNAVAILABLE;
7. }
8. auto event = AppExecFwk::InnerEvent::Get(ObserverHandler::RADIO_ACCEPT_CALL);
9. if (event == nullptr) {
10. TELEPHONY_LOGE("AnswerRequest return, error type: event is nullptr.");
11. return CALL_ERR_RESOURCE_UNAVAILABLE;
12. }
13. if (DelayedSingleton<CellularCallService>::GetInstance() == nullptr) {
14. TELEPHONY_LOGE("AnswerRequest return, error type: GetInstance() is nullptr.");
15. return CALL_ERR_RESOURCE_UNAVAILABLE;
16. }
17. event->SetOwner(DelayedSingleton<CellularCallService>::GetInstance()->GetHandler(slotId));
18. core->Answer(event);
19. return TELEPHONY_SUCCESS;
20. }
這裡就完成了 Answer 流程在 cellular_call 的呼叫。
3.2.2.3 Answer事件在core_servicel中的處理
這部分的程式碼會呼叫 core 對應的 Answer 函式,訊息就會傳遞到負責核心服務與 RIL Adapter 通訊互動的 tel_ril 程式碼中。
1. void Core::Answer(const AppExecFwk::InnerEvent::Pointer &result)
2. {
3. if (telRilManager_ == nullptr) {
4. TELEPHONY_LOGE("telRilManager is null!");
5. return;
6. }
7. telRilManager_->Answer(result);
8. }
進一步到負責 tel_ril 的管理類 TelRilManager 中,判斷下對應的 telRilCall_ 是否為空,如果不為空就可以繼續呼叫了。
1. void TelRilManager::Answer(const AppExecFwk::InnerEvent::Pointer &result)
2. {
3. if (telRilCall_ != nullptr) {
4. telRilCall_->Answer(result);
5. } else {
6. TELEPHONY_LOGE("telRilCall_ is null");
7. }
8. }
telRilCall_ 是在 TelRilManager 的初始化中進行的。其中 InitCellularRadio 首先通過 ServiceManager 拿到 cellular_radio1 對應的 service。然後在 InitTelInfo 中用 cellularRadio_ 和 observerHandler_ 構造 telRilCall_。
1. bool TelRilManager::OnInit()
2. {
3. bool res = false;
4. int i = 0;
5.
6. do {
7. res = InitCellularRadio(true);
8. if (!res) {
9. i++;
10. sleep(1);
11. TELEPHONY_LOGD("Initialization cellular radio failed. Try initialization again!");
12. } else {
13. InitTelInfo();
14. }
15. } while (!res && (i < RIL_INIT_COUNT_MAX));
16. return res;
17. }
呼叫到 teRilCall 的 Answer 函式進行處理,用 CreateTelRilRequest 構造 telRilRequest ,通過 TelRilBase 的 SendInt32Event 發出。
1. void TelRilCall::Answer(const AppExecFwk::InnerEvent::Pointer &result)
2. {
3. std::shared_ptr<TelRilRequest> telRilRequest = CreateTelRilRequest(HREQ_CALL_ANSWER, result);
4. if (telRilRequest == nullptr) {
5. TELEPHONY_LOGE("telRilRequest is nullptr");
6. return;
7. }
8. if (cellularRadio_ == nullptr) {
9. TELEPHONY_LOGE("%{public}s cellularRadio_ == nullptr", __func__);
10. ErrorResponse(telRilRequest->serialId_, HRilErrType::HRIL_ERR_GENERIC_FAILURE);
11. return;
12. }
13.
14. int ret = SendInt32Event(HREQ_CALL_ANSWER, telRilRequest->serialId_);
15. TELEPHONY_LOGD("HREQ_CALL_ANSWER ret %{public}d", ret);
16. }
從呼叫的情況來看,則又通過 IPC 的 SendRequest 請求將 dispatchId 和 data 發給 cellular_radio1。
1. int32_t TelRilBase::SendInt32Event(int32_t dispatchId, int32_t value)
2. {
3. int status = 0;
4. if (cellularRadio_ != nullptr) {
5. MessageParcel data;
6. MessageParcel reply;
7. data.WriteInt32(value);
8. OHOS::MessageOption option = {OHOS::MessageOption::TF_ASYNC};
9. status = cellularRadio_->SendRequest(dispatchId, data, reply, option);
10. TELEPHONY_LOGD("TelRilBase SendInt32Event, dispatchId:%{public}d, status:%{public}d", dispatchId, status);
11. } else {
12. TELEPHONY_LOGE("cellularRadio_ is nullptr!!!");
13. }
14. return status;
15. }
到這裡 Answer 流程在 core_service 服務中的處理就完畢了。
3.2.2.4 Answer事件在ril_adapter中的處理
從目前的程式碼呼叫來看有可能是從 vendor 呼叫 hril_hdf 的 dispatch,然後傳遞訊息到 ril_adapter 中
下邊為 hril_hdf 的初始化過程。
1. struct HdfDriverEntry g_rilAdapterDevEntry = {
2. .moduleVersion = 1,
3. .moduleName = MODULE_NAME,
4. .Bind = RilAdapterBind,
5. .Init = RilAdapterInit,
6. .Release = RilAdapterRelease,
7. };
8. HDF_INIT(g_rilAdapterDevEntry);
可以看到 hdf 的初始化包括 MODULE_ NAME、RilAdapterBind、RilAdapterInit。
Bind 過程如下。
1. static int32_t RilAdapterBind(struct HdfDeviceObject *device)
2. {
3. if (device == NULL) {
4. return HDF_ERR_INVALID_OBJECT;
5. }
6. device->service = &g_rilAdapterService;
7. return HDF_SUCCESS;
8. }
可以看看到 service 的 dispatch 對應於 RilAdapterDispatch。
1. static struct IDeviceIoService g_rilAdapterService = {
2. .Dispatch = RilAdapterDispatch,
3. .Open = NULL,
4. .Release = NULL,
5. };
Init 過程如下。
1. static int32_t RilAdapterInit(struct HdfDeviceObject *device)
2. {
3. if (device == NULL) {
4. return HDF_ERR_INVALID_OBJECT;
5. }
6. DFX_InstallSignalHandler();
7. TELEPHONY_LOGD("Start %{public}s hdf service!", HdfDeviceGetServiceName(device));
8. struct HdfSBuf *sbuf = HdfSBufTypedObtain(SBUF_IPC);
9. if (sbuf == NULL) {
10. TELEPHONY_LOGE("HdfSampleDriverBind, failed to obtain ipc sbuf");
11. return HDF_ERR_INVALID_OBJECT;
12. }
13. if (!HdfSbufWriteString(sbuf, "string")) {
14. TELEPHONY_LOGE("HdfSampleDriverBind, failed to write string to ipc sbuf");
15. HdfSBufRecycle(sbuf);
16. return HDF_FAILURE;
17. }
18. if (sbuf != NULL) {
19. HdfSBufRecycle(sbuf);
20. }
21. TELEPHONY_LOGD("sbuf IPC obtain test success!");
22. if (!IsLoadedVendorLib()) {
23. LoadVendor();
24. } else {
25. TELEPHONY_LOGI("The vendor library has been loaded!");
26. }
27. return HDF_SUCCESS;
28. }
其中 LoadVendor 會載入對應 vendor 的 rilLib。
1. static void LoadVendor(void)
2. {
3. const char *rilLibPath = NULL;
4. char vendorLibPath[PARAMETER_ZISE] = {0};
5. // Pointer to ril init function in vendor ril
6. const HRilOps *(*rilInitOps)(const struct HRilReport *) = NULL;
7. // functions returned by ril init function in vendor ril
8. const HRilOps *ops = NULL;
9.
10. if (GetVendorLibPath(vendorLibPath) == HDF_SUCCESS) {
11. rilLibPath = vendorLibPath;
12. } else {
13. rilLibPath = g_modem_list[MODEM_INDEX].path;
14. }
15. if (rilLibPath == NULL) {
16. TELEPHONY_LOGE("dynamic library path is empty");
17. return;
18. }
19. TELEPHONY_LOGD("RilInit LoadVendor start with rilLibPath:%{public}s", rilLibPath);
20. g_dlHandle = dlopen(rilLibPath, RTLD_NOW);
21. if (g_dlHandle == NULL) {
22. TELEPHONY_LOGE("dlopen %{public}s is fail. %{public}s", rilLibPath, dlerror());
23. return;
24. }
25. rilInitOps = (const HRilOps *(*)(const struct HRilReport *))dlsym(g_dlHandle, "RilInitOps");
26. if (rilInitOps == NULL) {
27. dlclose(g_dlHandle);
28. TELEPHONY_LOGE("RilInit not defined or exported");
29. return;
30. }
31. ops = rilInitOps(&g_reportOps);
32. TELEPHONY_LOGD("RilInit completed");
33. HRilRegOps(ops);
34. }
之前已經說到有可能是 vendor 呼叫了 dispatch,現在已經繫結了對應的 RilAdapterDispatch,後續處理如下。
C++ 寫的 IPC 和 C 語言寫的 IPC 是可以相互通訊的,格式並不同。HdfSBuf 也可以和 MessageParcel 互轉。
1. static int32_t RilAdapterDispatch(
2. struct HdfDeviceIoClient *client, int32_t cmd, struct HdfSBuf *data, struct HdfSBuf *reply)
3. {
4. int32_t ret;
5. static pthread_mutex_t dispatchMutex = PTHREAD_MUTEX_INITIALIZER;
6. pthread_mutex_lock(&dispatchMutex);
7. TELEPHONY_LOGD("RilAdapterDispatch cmd:%{public}d", cmd);
8. ret = DispatchRequest(SLOTID, cmd, data);
9. pthread_mutex_unlock(&dispatchMutex);
10. return ret;
11. }
Request 訊息通過 IPC 傳到 RilAdapter 後會通過 DispatchRequest 分發出去。
1. int32_t DispatchRequest(int32_t slotId, int32_t cmd, struct HdfSBuf *data)
2. {
3. if (data == nullptr) {
4. TELEPHONY_LOGE("miss callback parameter");
5. return HDF_ERR_INVALID_PARAM;
6. }
7. switch (cmd) {
8. case HRIL_ADAPTER_RADIO_INDICATION: {
9. HdfRemoteService *serviceCallbackInd = HdfSBufReadRemoteService(data);
10. if (serviceCallbackInd == nullptr) {
11. TELEPHONY_LOGE("miss callback parameter");
12. return HDF_ERR_INVALID_PARAM;
13. }
14. RegisterManagerNotifyCallback(slotId, serviceCallbackInd);
15. break;
16. }
17. case HRIL_ADAPTER_RADIO_RESPONSE: {
18. HdfRemoteService *serviceCallback = HdfSBufReadRemoteService(data);
19. if (serviceCallback == nullptr) {
20. TELEPHONY_LOGE("miss callback parameter");
21. return HDF_ERR_INVALID_PARAM;
22. }
23. RegisterManagerResponseCallback(slotId, serviceCallback);
24. break;
25. }
26. default:
27. DispatchModule(slotId, cmd, data);
28. }
29. return HDF_SUCCESS;
30. }
在後續呼叫中我們知道 code 為 HREQ_CALL_ANSWER,所以會呼叫 DispatchModule。
1. static void DispatchModule(int32_t slotId, int32_t cmd, struct HdfSBuf *data)
2. {
3. auto itFunc = g_manager.find(slotId);
4. if (itFunc != g_manager.end()) {
5. auto &manager = itFunc->second;
6. if (manager != nullptr) {
7. int32_t ret = manager->Dispatch(slotId, cmd, data);
8. if (ret != HDF_SUCCESS) {
9. TELEPHONY_LOGE("HRilManager::Dispatch is failed!");
10. }
11. } else {
12. TELEPHONY_LOGE("Manager is nullptr, id:%{public}d, addr:%{public}p!", slotId, &manager);
13. }
14. } else {
15. TELEPHONY_LOGE("Can not find slotId in g_manager: %{public}d!", slotId);
16. }
17. }
根據 slotId 拿到對應 g_manager 的 Dispatch 函式。
1. int32_t HRilManager::Dispatch(int32_t slotId, int32_t code, struct HdfSBuf *data)
2. {
3. if (hrilCall_ != nullptr && hrilCall_->IsCallRespOrNotify(code)) {
4. hrilCall_->ProcessCallRequest(slotId, code, data);
5. return HDF_SUCCESS;
6. }
7. if (hrilSms_ != nullptr && hrilSms_->IsSmsRespOrNotify(code)) {
8. hrilSms_->ProcessSmsRequest(slotId, code, data);
9. return HDF_SUCCESS;
10. }
11. if (hrilSim_ != nullptr && hrilSim_->IsSimRespOrNotify(code)) {
12. hrilSim_->ProcessSimRequest(slotId, code, data);
13. return HDF_SUCCESS;
14. }
15. if (hrilNetwork_ != nullptr && hrilNetwork_->IsNetworkRespOrNotify(code)) {
16. hrilNetwork_->ProcessNetworkRequest(slotId, code, data);
17. return HDF_SUCCESS;
18. }
19. if (hrilModem_ != nullptr && hrilModem_->IsModemRespOrNotify(code)) {
20. hrilModem_->ProcessCommonRequest(slotId, code, data);
21. return HDF_SUCCESS;
22. }
23. if (hrilData_ != nullptr && hrilData_->IsDataRespOrNotify(code)) {
24. hrilData_->ProcessDataRequest(slotId, code, data);
25. return HDF_SUCCESS;
26. }
27. return HDF_FAILURE;
28. }
由於我們的 code = 4,在 0 到 100 之間,所以會被判定為 callrequest
所以呼叫如下函式。
1. void HRilCall::ProcessCallRequest(int32_t slotId, int32_t code, struct HdfSBuf *data)
2. {
3. auto itFunc = reqMemberFuncMap_.find(code);
4. if (itFunc != reqMemberFuncMap_.end()) {
5. auto memberFunc = itFunc->second;
6. if (memberFunc != nullptr) {
7. (this->*memberFunc)(slotId, data);
8. }
9. } else {
10. TELEPHONY_LOGE("Can not find CallRequest code in reqMemberFuncMap_!");
11. }
12. }
Request 如下。
1. // request
2. reqMemberFuncMap_[HREQ_CALL_GET_CALL_LIST] = &HRilCall::GetCallList;
3. reqMemberFuncMap_[HREQ_CALL_DIAL] = &HRilCall::Dial;
4. reqMemberFuncMap_[HREQ_CALL_HANGUP] = &HRilCall::Hangup;
5. reqMemberFuncMap_[HREQ_CALL_REJECT] = &HRilCall::Reject;
6. reqMemberFuncMap_[HREQ_CALL_ANSWER] = &HRilCall::Answer;
7. reqMemberFuncMap_[HREQ_CALL_HOLD_CALL] = &HRilCall::HoldCall;
8. reqMemberFuncMap_[HREQ_CALL_UNHOLD_CALL] = &HRilCall::UnHoldCall;
9. reqMemberFuncMap_[HREQ_CALL_SWITCH_CALL] = &HRilCall::SwitchCall;
10. reqMemberFuncMap_[HREQ_CALL_GET_CLIP] = &HRilCall::GetClip;
11. reqMemberFuncMap_[HREQ_CALL_SET_CLIP] = &HRilCall::SetClip;
12. reqMemberFuncMap_[HREQ_CALL_COMBINE_CONFERENCE] = &HRilCall::CombineConference;
13. reqMemberFuncMap_[HREQ_CALL_SEPARATE_CONFERENCE] = &HRilCall::SeparateConference;
14. reqMemberFuncMap_[HREQ_CALL_CALL_SUPPLEMENT] = &HRilCall::CallSupplement;
15. reqMemberFuncMap_[HREQ_CALL_GET_CALL_WAITING] = &HRilCall::GetCallWaiting;
16. reqMemberFuncMap_[HREQ_CALL_SET_CALL_WAITING] = &HRilCall::SetCallWaiting;
17. reqMemberFuncMap_[HREQ_CALL_GET_CALL_TRANSFER_INFO] = &HRilCall::GetCallTransferInfo;
18. reqMemberFuncMap_[HREQ_CALL_SET_CALL_TRANSFER_INFO] = &HRilCall::SetCallTransferInfo;
19. reqMemberFuncMap_[HREQ_CALL_GET_CALL_RESTRICTION] = &HRilCall::GetCallRestriction;
20. reqMemberFuncMap_[HREQ_CALL_SET_CALL_RESTRICTION] = &HRilCall::SetCallRestriction;
21. reqMemberFuncMap_[HREQ_CALL_GET_CLIR] = &HRilCall::GetClir;
22. reqMemberFuncMap_[HREQ_CALL_SET_CLIR] = &HRilCall::SetClir;
23. reqMemberFuncMap_[HREQ_CALL_START_DTMF] = &HRilCall::StartDtmf;
24. reqMemberFuncMap_[HREQ_CALL_SEND_DTMF] = &HRilCall::SendDtmf;
25. reqMemberFuncMap_[HREQ_CALL_STOP_DTMF] = &HRilCall::StopDtmf;
26. reqMemberFuncMap_[HREQ_CALL_GET_IMS_CALL_LIST] = &HRilCall::GetImsCallList;
27. reqMemberFuncMap_[HREQ_CALL_GET_CALL_PREFERENCE] = &HRilCall::GetCallPreferenceMode;
28. reqMemberFuncMap_[HREQ_CALL_SET_CALL_PREFERENCE] = &HRilCall::SetCallPreferenceMode;
29. reqMemberFuncMap_[HREQ_CALL_GET_LTEIMSSWITCH_STATUS] = &HRilCall::GetLteImsSwitchStatus;
30. reqMemberFuncMap_[HREQ_CALL_SET_LTEIMSSWITCH_STATUS] = &HRilCall::SetLteImsSwitchStatus;
呼叫 request 對應處理函式為 Answer。
1. void HRilCall::Answer(int32_t slotId, struct HdfSBuf *data)
2. {
3. int32_t serial = 0;
4. if (!HdfSbufReadInt32(data, &serial)) {
5. TELEPHONY_LOGE("miss serial parameter");
6. return;
7. }
8. ReqDataInfo *requestInfo = CreateHRilRequest(serial, slotId, HREQ_CALL_ANSWER);
9. if (requestInfo == nullptr) {
10. TELEPHONY_LOGE("RilAdapter failed to do Create Answer HRilRequest!");
11. return;
12. }
13. if (callFuncs_ == nullptr) {
14. TELEPHONY_LOGE("RilAdapter HRilCall::Dial callFuncs_ is nullptr!");
15. SafeFrees(requestInfo);
16. return;
17. }
18. callFuncs_->Answer(requestInfo);
19. SafeFrees(requestInfo);
20. }
首先要建立 HRilRequest,將資訊封裝進 requestInfo 中。
1. ReqDataInfo *CreateHRilRequest(int32_t serial, int32_t slotId, int32_t request)
2. {
3. ReqDataInfo *requestInfo = nullptr;
4. HRilSimSlotId simSlotId = (HRilSimSlotId)slotId;
5. requestInfo = (ReqDataInfo *)calloc(1, sizeof(ReqDataInfo));
6. if (requestInfo == nullptr) {
7. return nullptr;
8. }
9. requestInfo->slotId = simSlotId;
10. requestInfo->request = request;
11. requestInfo->serial = serial;
12. return requestInfo;
13. }
在 LoadVendor 時呼叫 HRilRegOps(ops) 時會進行 register。
1. void HRilRegOps(const HRilOps *hrilOps)
2. {
3. int i;
4. if (hrilOps == nullptr) {
5. TELEPHONY_LOGE("HRilRegOps: HRilRegOps * nullptr");
6. return;
7. }
8. if (rilRegisterStatus > RIL_REGISTER_IS_NONE) {
9. TELEPHONY_LOGE("HRilRegOps is running!!!!");
10. return;
11. }
12. rilRegisterStatus = RIL_REGISTER_IS_RUNNING;
13. vendorLibLoadStatus = RIL_REGISTER_IS_RUNNING;
14. (void)memcpy_s(&g_callBacks, sizeof(HRilOps), hrilOps, sizeof(HRilOps));
15.
16. for (i = HRIL_SIM_SLOT_1; i < HRIL_SIM_SLOT_NUM; i++) {
17. g_manager[i] = std::make_unique<HRilManager>();
18. if (g_callBacks.smsOps != nullptr) {
19. g_manager[i]->RegisterSmsFuncs(g_callBacks.smsOps);
20. }
21. if (g_callBacks.callOps != nullptr) {
22. g_manager[i]->RegisterCallFuncs(g_callBacks.callOps);
23. }
24. if (g_callBacks.dataOps != nullptr) {
25. g_manager[i]->RegisterDataFuncs(g_callBacks.dataOps);
26. }
27. if (g_callBacks.modemOps != nullptr) {
28. g_manager[i]->RegisterModemFuncs(g_callBacks.modemOps);
29. }
30. if (g_callBacks.networkOps != nullptr) {
31. g_manager[i]->RegisterNetworkFuncs(g_callBacks.networkOps);
32. }
33. if (g_callBacks.simOps != nullptr) {
34. g_manager[i]->RegisterSimFuncs(g_callBacks.simOps);
35. }
36. }
37. }
這裡會將 callOps 註冊到 callFuncs_ 中,而 g_callBacks 實際是對應 hrilOps。
1. HRilOps g_hrilOps = {
2. .callOps = &g_callReqOps,
3. .simOps = &g_simReqOps,
4. .smsOps = &g_smsReqOps,
5. .networkOps = &g_networkReqOps,
6. .dataOps = &g_dataReqOps,
7. .modemOps = &g_modemReqOps,
8. };
.callOps 對應於 &g_callReqOps,對應的函式對照表如下。
1. static const HRilCallReq g_callReqOps = {
2. .GetCallList = ReqGetCallList,
3. .Dial = ReqDial,
4. .Hangup = ReqHangup,
5. .Reject = ReqReject,
6. .Answer = ReqAnswer,
7. .GetClip = ReqGetClip,
8. .SetClip = ReqSetClip,
9. .HoldCall = ReqHoldCall,
10. .UnHoldCall = ReqUnHoldCall,
11. .SwitchCall = ReqSwitchCall,
12. .CombineConference = ReqCombineConference,
13. .SeparateConference = ReqSeparateConference,
14. .CallSupplement = ReqCallSupplement,
15. .GetCallWaiting = ReqGetCallWaiting,
16. .SetCallWaiting = ReqSetCallWaiting,
17. .GetCallTransferInfo = ReqGetCallTransferInfo,
18. .SetCallTransferInfo = ReqSetCallTransferInfo,
19. .GetCallRestriction = ReqGetCallRestriction,
20. .SetCallRestriction = ReqSetCallRestriction,
21. .GetClir = ReqGetClir,
22. .SetClir = ReqSetClir,
23. .StartDtmf = ReqStartDtmf,
24. .SendDtmf = ReqSendDtmf,
25. .StopDtmf = ReqStopDtmf,
26. .GetImsCallList = ReqGetImsCallList,
27. .GetCallPreferenceMode = ReqGetCallPreferenceMode,
28. .SetCallPreferenceMode = ReqSetCallPreferenceMode,
29. .GetLteImsSwitchStatus = ReqGetLteImsSwitchStatus,
30. .SetLteImsSwitchStatus = ReqSetLteImsSwitchStatus,
31. };
找對對應的 ReqAnswer 進行後續處理,這裡其實已經走到 AT 命令的處理層。
1. void ReqAnswer(const ReqDataInfo *requestInfo)
2. {
3. int32_t ret;
4. int32_t err = HRIL_ERR_SUCCESS;
5. struct ReportInfo reportInfo = {0};
6. ResponseInfo *pResponse = NULL;
7.
8. ret = SendCommandLock("ATA", NULL, 0, &pResponse);
9. if (ret != HRIL_ERR_SUCCESS || !pResponse->success) {
10. err = HRIL_ERR_GENERIC_FAILURE;
11. }
12. reportInfo = CreateReportInfo(requestInfo, err, HRIL_RESPONSE, 0);
13. OnCallReport(HRIL_SIM_SLOT_1, reportInfo, NULL, 0);
14. FreeResponseInfo(pResponse);
15. }
後續呼叫 SendCommandLock 函式,這裡就會將 requestInfo 處理成 AT 命令。
1. int SendCommandLock(const char *command, const char *prefix, long long timeout, ResponseInfo **outResponse)
2. {
3. const char *atCmd = "AT";
4. int err;
5. if (pthread_equal(g_reader, pthread_self()) != 0) {
6. TELEPHONY_LOGE("The read thread prohibits sending commands.");
7. return AT_ERR_INVALID_THREAD;
8. }
9.
10. TELEPHONY_LOGD("command %{public}s, NeedATPause:%{public}d, atCmd:%{public}s", command, g_isNeedATPause, atCmd);
11. pthread_mutex_lock(&g_commandmutex);
12. if (g_isNeedATPause) {
13. pthread_cond_signal(&g_commandcond);
14. err = SendCommandNoLock(atCmd, timeout, outResponse);
15. if (err != 0) {
16. TELEPHONY_LOGD("NeedATPause err = %{public}d cmd:%{public}s", err, command);
17. }
18. if (g_atWatch != NULL) {
19. g_atWatch();
20. }
21. g_isNeedATPause = false;
22. alarm(0);
23. }
24. g_prefix = prefix;
25. err = SendCommandNoLock(command, timeout, outResponse);
26. pthread_mutex_unlock(&g_commandmutex);
27. TELEPHONY_LOGD("err = %{public}d, cmd:%{public}s", err, command);
28. // when timeout to process
29. if (err == AT_ERR_TIMEOUT && g_onTimeout != NULL) {
30. g_onTimeout();
31. } else if (err == AT_ERR_GENERIC) {
32. TELEPHONY_LOGD("OnReaderClosed() err = %{public}d", err);
33. OnReaderClosed();
34. }
35. return err;
36. }
實際的處理在這個函式 SendCommandNoLock。
1. int SendCommandNoLock(const char *command, long long timeout, ResponseInfo **outResponse)
2. {
3. long long defaultTimeout = 50000;
4. int err = 0;
5. struct timespec time;
6. if (g_response != NULL) {
7. err = AT_ERR_COMMAND_PENDING;
8. TELEPHONY_LOGE("g_response is not null, so the command cannot be sent.");
9. ClearCurCommand();
10. return err;
11. }
12. g_response = (ResponseInfo *)calloc(1, sizeof(ResponseInfo));
13. if (g_response == NULL) {
14. err = AT_ERR_GENERIC;
15. TELEPHONY_LOGE("g_response calloc is fail, err:%{public}d.", err);
16. ClearCurCommand();
17. return err;
18. }
19. err = WriteATCommand(command, 0, g_atFd);
20. if (err != VENDOR_SUCCESS) {
21. TELEPHONY_LOGE("send AT cmd is fail, err:%{public}d.", err);
22. ClearCurCommand();
23. return err;
24. }
25. SetWaitTimeout(&time, (timeout != 0) ? timeout : defaultTimeout);
26. while (g_response->result == NULL && g_readerClosed == 0) {
27. err = pthread_cond_timedwait(&g_commandcond, &g_commandmutex, &time);
28. if (err == ETIMEDOUT) {
29. err = AT_ERR_TIMEOUT;
30. TELEPHONY_LOGE("pthread cond timedwait is timeout, err:%{public}d.", err);
31. ClearCurCommand();
32. return err;
33. }
34. }
35. if (outResponse == NULL) {
36. FreeResponseInfo((ResponseInfo *)g_response);
37. } else {
38. *outResponse = (ResponseInfo *)g_response;
39. }
40. g_response = NULL;
41. if (g_readerClosed > 0) {
42. err = AT_ERR_CHANNEL_CLOSED;
43. TELEPHONY_LOGE("g_readerClosed is closed, err:%{public}d.", err);
44. ClearCurCommand();
45. return err;
46. }
47. err = 0;
48. return err;
49. }
通過 WriteATCommand 將 AT 命令發給 modem,然後等待 modem 回覆處理的結果。
1. int WriteATCommand(const char *s, int isPdu, int atFd)
2. {
3. TELEPHONY_LOGD("cmd:%{public}s", s);
4. ssize_t ret;
5. size_t i = 0;
6. size_t len = strlen(s);
7. if (atFd < 0) {
8. return AT_ERR_CHANNEL_CLOSED;
9. }
10.
11. while (i < len) {
12. do {
13. ret = write(atFd, s + i, len - i);
14. } while (ret < 0 && errno == EINTR);
15. if (ret < 0) {
16. return AT_ERR_GENERIC;
17. }
18. i += ret;
19. }
20. if (isPdu != 0) {
21. do {
22. ret = write(atFd, "\x1A", 1);
23. } while ((ret < 0 && errno == EINTR) || (ret == 0));
24. } else {
25. do {
26. ret = write(atFd, "\r", 1);
27. } while ((ret < 0 && errno == EINTR) || (ret == 0));
28. }
29. if (ret < 0) {
30. return AT_ERR_GENERIC;
31. }
32. return VENDOR_SUCCESS;
33. }
到這裡為止,Answer 流程已經完成了從 app 到 modem 的資訊傳遞。
總結
從第三章的分析過程,我們已經完成了從 Call ui 響應來電提示,然後一步一步完成了整個框架層的呼叫過程,其實在結尾的時候我們只是完成了訊息的下傳到 modem,這個過程後 modem 也進行回覆,這個過程也比較長此處就不再贅述了。有機會的話我們可以在後續的文章中進行分析。
整個文件由於本人能力和時間的限制,後續儘可能將遺漏地方補全。從 core_service 到 ril_adapter 的呼叫,是在 vendor 目錄中將 cellular_radio1 的 libhril_hdf.z.so 載入。由於目前的程式碼還在完善中,有可能後續最新版的程式碼會有所改變。
高清流程圖下載:https://ost.51cto.com/resourc...