( The following content is shared by developers , Do not represent OpenHarmony Views of project group working committee )

One 、 brief introduction
OpenAtom OpenHarmony( hereinafter referred to as “OpenHarmony”) The telephone subsystem is OS It provides basic wireless communication capability .
Support TD-LTE/FDD-LTE/TD-SCDMA/WCDMA/EVDO/CDMA1X/GSM And other network communication modules , It can provide high-speed wireless data transmission 、 Internet access and other services , With voice 、 SMS 、 MMS 、SIM Card and other functions .
Unless otherwise specified in the following lines , All statements refer to OpenHarmony System (OpenHarmony 3.0 LTS edition ).
Two 、OpenHarmony Architecture diagram
3、 ... and 、 Basic knowledge of
1. The telephone subsystem
The telephone subsystem is used as OpenHarmony A very important part , Provide basic communication functions for the system , Include CS Domain services , Like voice call 、 SMS 、 Call management ; Also include PS Domain related services , such as MMS、 Data services, etc , in addition SIM and RIL Our business is also in this subsystem .
2. Telephone subsystem architecture diagram
OpenHarmony Related frame diagram of cellular call of existing telephone subsystem :
application layer : All kinds of calls 、SMS、 Data business 、SIM Application of card function , for example call application 、SMS application 、launcher Application and so on .
Frame layer :
①SDK : Provide standard interfaces for applications , Include JS Interface and C++ Interface .
②Framework: Provide stable basic capabilities of corresponding modules to the application layer , Include network、call、sms、sim Related functions , Including call management , Short color editing, sending and receiving ,sim The identification of the card is stationed in the network , Data service management, etc . At present OpenHarmony In the version ,call_manager、cellular_call、cellular_data、data_storage、sms_mms、state_registry、core_service And other modules belong to the framework layer .
Hril layer : Equivalent to Android RILJ, Because different schemes use Modem Dissimilarity , So various instruction formats , The initialization sequence is different , To eliminate these differences , and Hril It provides an abstraction layer between wireless hardware devices and telephone services .
Vendor lib layer : Like Android RILD, Responsible for working with modem Module interaction , Send the corresponding information of each module AT command .
Modem layer : Today's baseband processor , It mainly processes digital signals 、 Speech signal encoding and decoding and communication protocol , And baseband processor 、 RF and other peripheral chips as a Modem modular , Provide AT Command interface to the upper layer for interaction . Communication module Modem Communicate with the communication network 、 Transmit voice and data 、 Complete the call 、 SMS and other related telephone functions .
3. Telephone subsystem code structure
Because the telephone subsystem contains many modules , So describe each module separately :
Call management module : Main management CS(Circuit Switch, Circuit switching )、IMS(IP Multimedia Subsystem,IP Multimedia subsystem ) and OTT(over the top,OTT Solution ) Three types of calls , Be responsible for applying for audio and video resources required for calls , And deal with various conflicts arising from multi-channel calls .
1. /base/telephony/call_manager
2. ├─ frameworks # napi Interface storage directory
3. ├─ interfaces # Interfaces exposed to the outside
4. │ ├─ innerkits # Internal interfaces between components
5. │ └─ kits # js Interface storage directory
6. ├─ sa_profile # Startup profile
7. ├─ services # Service internal code
8. │ ├─ audio # Audio management related code
9. │ ├─ bluetooth # Bluetooth call related code
10. │ ├─ call # Call service related code
11. │ ├─ call_manager_service # Process service management related code
12. │ ├─ call_setting # Call settings related codes
13. │ ├─ telephony_interaction # Telephone core service interaction related code
14. │ └─ call_ui_interaction # UI Interaction related code
15. ├─ test # Unit test related code
16. └─ utils # General tools
17. ```
Cellular call module : Support the implementation of basic call based on operator network , Include based on 2G/3G Of CS(Circuit Switch, Circuit switching ) Call and based on 4G/5G Of IMS(IP Multimedia Subsystem,IP Multimedia subsystem ) conversation , contain VoLTE/ VoWIFI/ VoNR voice 、 video 、 meeting , Support CS and IMS Domain selection control and switching between calls , Support emergency calls . Support mainstream modem Chip platform .
1. /base/telephony/cellular_call # Cellular call subassembly
2. ├─ BUILD.gn # compile gn Script
3. ├─ README.md # Readme file
4. ├─ services
5. │ ├─ common # Tool warehouse
6. │ ├─ connection # adjoining course
7. │ ├─ control # Control the business layer
8. │ └─ manager # management layer
9. ├─ sa_profile # sa file
10. ├─ ohos.build # compile build
11. └─ test # Test related
12. ```
Cellular data module : As a tailorable part of the telephone subsystem , Depend on core_service Core services 、ril_adapter. With cellular data activation 、 Cellular data anomaly detection and recovery 、 Cellular data state management 、 Cellular data switch management 、 Cellular data roaming management 、APN management 、 Network management interaction and other functions .
1. base/telephony/cellular_data
2. ├── figures
3. ├── frameworks
4. │ ├── js # js file
5. │ │ └── napi
6. │ │ ├── include
7. │ │ └── src
8. │ └── native
9. │ └── src
10. ├── interfaces
11. │ ├── innerkits # External interface
12. │ └── kits
13. │ └── js # Externally provided js Interface
14. │ └── declaration
15. ├── sa_profile # SA To configure
16. ├── services
17. │ ├── include # The header file
18. │ ├── apn_manager
19. │ │ ├── common
20. │ │ ├── state_machine
21. │ │ └── utils
22. │ └── src # Source file
23. │ ├── apn_manager
24. │ ├── state_machine
25. │ └── utils
26. └── test
27. └── unit_test # Unit test related code
28. ```
Telephone core service module : The main function is initialization RIL management 、SIM Card and network search module , And get RIL Adapter service . By registering the callback service , Realization and RIL Adapter communicate ; Subscribe by publishing , To realize the communication with each functional module .
1. /base/telphony/core_service
2. ├── interfaces # Interface directory
3. │ ├── innerkits # Internal interfaces between components
4. │ └── kits # Interface provided for application ( for example JS Interface )
5. ├── services # Core service implementation code directory
6. │ ├── include
7. │ └── src
8. ├── etc # Driver script directory of core services
9. │ └── init
10. ├── sa_profile # Directory of startup files for core services
11. ├── tel_ril # Core services and RIL Adapter Communication code directory
12. │ ├── include
13. │ ├── src
14. │ └── test
15. ├── network_search # Search service code directory
16. │ ├── include
17. │ ├── src
18. │ └── test
19. ├── sim # SIM Card service code directory
20. │ ├── include
21. │ ├── src
22. │ └── test
23. ├── frameworks # frameworks Catalog
24. │ ├── js
25. │ ├── nstive
26. ├── common # Header file directory of each business module
27. │ ├── api
28. │ ├── call_manager
29. │ ├── network_search
30. │ └── sim
31. ├── utils
32. │ ├── log # Core service log print Directory
33. │ ├── preferences
34. │ ├── common
35. ```
Database and persistence module : In charge of the telephone service subsystem SIM card / Short MMS and other modules for persistent data storage , Provide DataAbility Access interface .
1. /base/telephony/data_storage # Database and persistence
2. ├─ BUILD.gn # compile gn Script
3. ├─ README.md # Readme file
4. ├─ common # public 、 General documents
5. │ ├─ include
6. │ └─ src
7. ├─ pdp_profile # Network operators
8. │ ├─ include
9. │ └─ src
10. ├─ sim # sim card
11. │ ├─ include
12. │ └─ src
13. ├─ sms_mms # SMS message
14. │ ├─ include
15. │ └─ src
16. ├─ ohos.build # compile build
17. └─ test # Test related
18. ```
RIL Adapter modular : Mainly including vendor library loading , Business interface implementation and event scheduling management . Mainly used to shield different modem Vendor hardware differences , Provide a unified interface for the upper layer , By registering HDF The service communicates with the upper interface .
1. base/telephony/ril_adapter
2. ├─ hril # hri The interface implementation of each business module of layer
3. ├─ hril_hdf # HDF service
4. ├─ include # Header file storage directory
5. ├─ interfaces # Provide corresponding internal interfaces of each business of the upper layer
6. │ └─ innerkits
7. ├─ test # Unit test related code
8. │ ├─ mock
9. │ └─ unittest # Unit test code
10. └─ vendor # Vendor library code
11. │ └─ include
12. ```
Short MMS module : Provide SMS sending and receiving and MMS encoding and decoding functions for mobile data users , The main functions are GSM/CDMA Text messaging 、 SMS PDU(Protocol data unit, Protocol data unit ) codec 、Wap Push Reception processing 、 Cell broadcast reception 、 MMS notification 、 MMS codec and SIM Add, delete, modify and check card SMS records, etc .
1. /base/telephony/sms_mms
2. ├─ interfaces # Exposed interface
3. │ └─ kits
4. ├─ sa_profile # Startup profile
5. ├─ services # Service internal code
6. │ ├─ include # Header Directory
7. │ ├─ cdma # CDMA Standard source file
8. │ └─ gsm # GSM Standard source file
9. ├─ test # Unit test directory
10. └─ utils # General tools related
11. ```
Status registration module : It is mainly responsible for the subscription and unsubscribe of various message events in the telephone service subsystem API. Event types include network state changes 、 Signal strength changes 、 Cell information changes 、 Cellular data connection status changes 、 Call status changes, etc .
1. /base/telephony/state_registry # Status registration forwarding service
2. ├─ BUILD.gn # compile gn Script
3. ├─ README.md # Readme file
4. ├─ interfaces # API,js file
5. ├─ service
6. │ ├─ include # The header file
7. │ └─ src # Source file
8. ├─ sa_profile # sa file
9. ├─ ohos.build # compile build
10. └─ test # Test related
11. ```
4. Relevant warehouse
Core services :https://gitee.com/openharmony/telephony_core_service
Cellular call :https://gitee.com/openharmony/telephony_cellular_call
call management :https://gitee.com/openharmony/telephony_call_manager
Registration service :https://gitee.com/openharmony/telephony_state_registry
SMS message :https://gitee.com/openharmony/telephony_sms_mms
riladapter:https://gitee.com/openharmony/telephony_ril_adapter
Data business :https://gitee.com/openharmony/telephony_cellular_data
data storage :https://gitee.com/openharmony/telephony_data_storage
Network management :https://gitee.com/openharmony/communication_netmanager_standard
5. The telephone subsystem (call) Core class
Four 、 The source code parsing
As the core business of telephone subsystem , Call function (Call) In addition to requiring hardware support , Such as audio module 、 Baseband module, etc , Many services of the system itself need to cooperate with each other to realize this function , such as : call management (call_manager)、 Cellular call service (cellular_call)、Telephony Core services (core_service)、RIL adapter (ril_adapter), Status registration service (state_registry) etc. .
At present, calls are mainly divided into three types :CS Call(Circuit Switch, Circuit switching )、IMS Call(IP Multimedia Subsystem,IP Multimedia subsystem ) and OTT Call(over the top,OTT Solution ) Three types of calls . On the upper Call The interfaces exposed by the application include :dial、answer、reject、hangup、holdCall、unHoldCall、switchCal、startDTMF、stopDTMF etc. . Because there are so many incidents , Moreover, the event handling processes are similar , So here we take Call Of Answer Event handling process .
1. Call upper layer call code analysis
When there is an incoming call ,Callui The interface will display the incoming call , The user clicks answer Key , Will be activated Call Of Answer technological process .
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>
Call here incomingCom.js Of onAnswer function .
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. },
Because before has import 了 callServiceProxy.js file .
1. import {acceptCall, rejectCall, hangUpCall} from'../../../model/callServiceProxy.js';
So let's take a look at callServiceProxy Of acceptCall function .
1. /**
2. * accept call
3. * @param { number } callId - call id
4. */
5. export const acceptCall = function (callId) {
6. call.answer(callId).then((res) => {
7. console.log(prefixLog + 'then:acceptCall' + JSON.stringify(res));
8. }).catch((err) => {
9. console.log(prefixLog + 'catch:acceptCall' + JSON.stringify(err));
10. });
11. };
From the code, we can see that this function actually calls call Of answer function , So where did this come from .call It is the framework layer of telephone subsystem napi The interface of external encapsulation is realized ,call_manager Medium @ohos.telephony.call.d.ts There are corresponding interface descriptions in , From here, the code has been completed from app To framework Call to .
1. import call from '@ohos.telephony.call';
2. Call framework layer code analysis
3.2.1 Call frame layer code call sequence diagram
3.2.2 Call framework layer code analysis
As can be seen from the sequence diagram above , Whole Answer The calling process of is relatively long , The whole frame layer processing span includes call_manager、cellular_call、core_service、IPC、ril_adapter And so on . Because the processing flow is too long , Refer to the sequence diagram for specific calling conditions , Here, we will describe some key parts of the processing of the framework layer according to calling different services .
3.2.2.1Answer The incident happened in call_manager Processing in
Previously called by the application layer call.answer It's through @ohos.telephony.call Introduced , The actual definition is call_manage In service interfaces Internal napi Interface .
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. };
The interface and some enumeration parameters are required for registration napi_valueNapiCallManager::RegisterCallManagerFunc(napi_env env, napi_value exports)
1. napi_value NapiCallManager::RegisterCallManagerFunc(napi_env env, napi_value exports)
2. {
3. DeclareCallBasisInterface(env, exports);
4. DeclareCallConferenceInterface(env, exports);
5. DeclareCallSupplementInterface(env, exports);
6. DeclareCallExtendInterface(env, exports);
7. DeclareCallMultimediaInterface(env, exports);
8. DeclareCallMediaEnum(env, exports);
9. DeclareCallDialEnum(env, exports);
10. DeclareCallStateEnum(env, exports);
11. DeclareCallEventEnum(env, exports);
12. DeclareCallRestrictionEnum(env, exports);
13. DeclareCallWaitingEnum(env, exports);
14. DeclareCallTransferEnum(env, exports);
15. std::u16string bundleName = GetBundleName(env);
16. Init(bundleName);
17. return exports;
18. }
What we need here is CallBasis Interface , The details are as follows , These are the handler functions corresponding to some events called by the application layer before .
1. napi_value NapiCallManager::DeclareCallBasisInterface(napi_env env, napi_value exports)
2. {
3. napi_property_descriptor desc[] = {
4. DECLARE_NAPI_FUNCTION("dial", DialCall),
5. DECLARE_NAPI_FUNCTION("answer", AnswerCall),
6. DECLARE_NAPI_FUNCTION("reject", RejectCall),
7. DECLARE_NAPI_FUNCTION("hangup", HangUpCall),
8. DECLARE_NAPI_FUNCTION("holdCall", HoldCall),
9. DECLARE_NAPI_FUNCTION("unHoldCall", UnHoldCall),
10. DECLARE_NAPI_FUNCTION("switchCall", SwitchCall),
11. DECLARE_NAPI_FUNCTION("upgradeCall", UpgradeCall),
12. DECLARE_NAPI_FUNCTION("downgradeCall", DowngradeCall),
13. DECLARE_NAPI_FUNCTION("setCallPreferenceMode", SetCallPreferenceMode),
14. };
15. NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc));
16. return exports;
17. }
Because we correspond to answer event , So the corresponding function here is AnswerCall.
1. napi_value NapiCallManager::AnswerCall(napi_env env, napi_callback_info info)
2. {
3. GET_PARAMS(env, info, VALUE_MAXIMUM_LIMIT);
4. NAPI_ASSERT(env, argc <= VALUE_MAXIMUM_LIMIT, "parameter error!");
5. bool matchFlag = MatchValueType(env, argv[ARRAY_INDEX_FIRST], napi_number);
6. NAPI_ASSERT(env, matchFlag, "Type error, should be number type");
7. auto asyncContext = (std::make_unique<AnswerAsyncContext>()).release();
8. napi_get_value_int32(env, argv[ARRAY_INDEX_FIRST], &asyncContext->callId);
9. if (argc == TWO_VALUE_LIMIT) {
10. if (MatchValueType(env, argv[ARRAY_INDEX_SECOND], napi_function)) {
11. napi_create_reference(env, argv[ARRAY_INDEX_SECOND], DATA_LENGTH_ONE, & (asyncContext->callbackRef));
12. } else if (MatchValueType(env, argv[ARRAY_INDEX_SECOND], napi_number)) {
13. asyncContext->videoState = GetIntProperty(env, argv[ARRAY_INDEX_SECOND], "videoState");
14. }
15. } else if (argc == VALUE_MAXIMUM_LIMIT) {
16. asyncContext->videoState = GetIntProperty(env, argv[ARRAY_INDEX_SECOND], "videoState");
17. napi_create_reference(env, argv[ARRAY_INDEX_THIRD], DATA_LENGTH_ONE, & (asyncContext->callbackRef));
18. }
19. return HandleAsyncWork(env, asyncContext, "AnswerCall", NativeAnswerCall, NativeVoidCallBack);
20. }
Continue to call , From the previous function AnswerCall It should be processed asynchronously .
1. void NapiCallManager::NativeAnswerCall(napi_env env, void *data)
2. {
3. if (data == nullptr) {
4. TELEPHONY_LOGE("data is nullptr");
5. return;
6. }
7. auto asyncContext = (AnswerAsyncContext *)data;
8. int32_t ret = DelayedSingleton<CallManagerProxy>::GetInstance()->AnswerCall(
9. asyncContext->callId, asyncContext->videoState);
10. asyncContext->result = ret;
11. }
In the process of calling all the way , Would call CallPolicy Class AnswerCallPolicy The function is used to determine the corresponding callId Of CallObject Whether there is , If so, judge Call Status .
1. int32_t CallPolicy::AnswerCallPolicy(int32_t callId)
2. {
3. if (!IsCallExist(callId)) {
4. TELEPHONY_LOGE("callId is invalid, callId:%{public}d", callId);
5. return CALL_ERR_CALLID_INVALID;
6. }
7. TelCallState state = GetCallState(callId);
8. if (state != CALL_STATUS_INCOMING && state != CALL_STATUS_WAITING) {
9. TELEPHONY_LOGE("current call state is:%{public}d, accept call not allowed", state);
10. return CALL_ERR_ILLEGAL_CALL_OPERATION;
11. }
12. return TELEPHONY_SUCCESS;
13. }
In the subsequent call to CallRequestHandlerService Of AnswerCall When processing , If there is CallRequestHandler Of handler_ There is , be make individual AnswerCallPara Of unique_ptr, And then callid and videostate Pass in the pointer . call SendEvent take HANDLER_ANSWER_CALL_REQUEST issue .
1. int32_t CallRequestHandlerService::AnswerCall(int32_t callId, int32_t videoState)
2. {
3. if (handler_.get() == nullptr) {
4. TELEPHONY_LOGE("handler_ is nullptr");
5. return TELEPHONY_ERR_FAIL;
6. }
7. std::unique_ptr<AnswerCallPara> para = std::make_unique<AnswerCallPara>();
8. if (para.get() == nullptr) {
9. TELEPHONY_LOGE("make_unique AnswerCallPara failed!");
10. return TELEPHONY_ERR_FAIL;
11. }
12. para->callId = callId;
13. para->videoState = videoState;
14. if (!handler_->SendEvent(HANDLER_ANSWER_CALL_REQUEST, std::move(para))) {
15. TELEPHONY_LOGE("send accept event failed!");
16. return TELEPHONY_ERR_FAIL;
17. }
18. return TELEPHONY_SUCCESS;
19. }
From the code processing flow , It's coming from here event Will be by... In the same file CallRequestHandler Class capture , Trigger ProcessEvent Handle .
1. void CallRequestHandler::ProcessEvent(const AppExecFwk::InnerEvent::Pointer &event)
2. {
3. if (event == nullptr) {
4. TELEPHONY_LOGE("CallRequestHandler::ProcessEvent parameter error");
5. return;
6. }
7. TELEPHONY_LOGD("CallRequestHandler inner event id obtained: %{public}u.", event- >GetInnerEventId());
8. auto itFunc = memberFuncMap_.find(event->GetInnerEventId());
9. if (itFunc != memberFuncMap_.end()) {
10. auto memberFunc = itFunc->second;
11. if (memberFunc != nullptr) {
12. return (this->*memberFunc)(event);
13. }
14. }
15. }
memberFuncMap Will be based on event Of id from memberFuncMap_ Get the corresponding memberFunc, Then enter the corresponding processing function .
1. CallRequestHandler::CallRequestHandler(const std::shared_ptr<AppExecFwk::EventRunner> &runner)
2. : AppExecFwk::EventHandler(runner), callRequestProcessPtr_(nullptr)
3. {
4. memberFuncMap_[CallRequestHandlerService::HANDLER_DIAL_CALL_REQUEST] = &CallRequestHandler::DialCallEvent;
5. memberFuncMap_[CallRequestHandlerService::HANDLER_ANSWER_CALL_REQUEST] = &CallRequestHandler::AcceptCallEvent;
6. memberFuncMap_[CallRequestHandlerService::HANDLER_REJECT_CALL_REQUEST] = &CallRequestHandler::RejectCallEvent;
7. memberFuncMap_[CallRequestHandlerService::HANDLER_HANGUP_CALL_REQUEST] = &CallRequestHandler::HangUpCallEvent;
8. memberFuncMap_[CallRequestHandlerService::HANDLER_HOLD_CALL_REQUEST] = &CallRequestHandler::HoldCallEvent;
9. memberFuncMap_[CallRequestHandlerService::HANDLER_UNHOLD_CALL_REQUEST] = &CallRequestHandler::UnHoldCallEvent;
10. memberFuncMap_[CallRequestHandlerService::HANDLER_SWAP_CALL_REQUEST] = &CallRequestHandler::SwitchCallEvent;
11. memberFuncMap_[CallRequestHandlerService::HANDLER_COMBINE_CONFERENCE_REQUEST] =
12. &CallRequestHandler::CombineConferenceEvent;
13. memberFuncMap_[CallRequestHandlerService::HANDLER_SEPARATE_CONFERENCE_REQUEST] =
14. &CallRequestHandler::SeparateConferenceEvent;
15. memberFuncMap_[CallRequestHandlerService::HANDLER_UPGRADE_CALL_REQUEST] = &CallRequestHandler::UpgradeCallEvent;
16. memberFuncMap_[CallRequestHandlerService::HANDLER_DOWNGRADE_CALL_REQUEST] =
17. &CallRequestHandler::DowngradeCallEvent;
18. }
The corresponding processing function here is AcceptCallEvent(), This part of the function has no return value , Again from event Remove from callId and 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. }
Continue to call CallRequestProcessl Class AnswerReques Function time , Will pass GetOneCallObject Get different 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. }
Got the corresponding call after , You can call the corresponding call Of AnswerCall function , This function will override BaseCall The virtual function . because CSCall、IMSCall、OTTCall There are corresponding AnswerCall function , So the actual use of that is by BaseCall Class AnswerCall Overridden by that subclass , To call different AnswerCall function . Let's assume here that CsCall .
1. int32_t CSCall::AnswerCall(int32_t videoState)
2. {
3. return CarrierAcceptCall(videoState);
4. }
Call to CarrierCall Of CarrierAcceptCall Follow up . among AcceptCallBase() The function will judge Call state , If it is CALL_RUNNING_STATE_RINGING state , It will call AudioControlManager Of SetVolumeAudible To set up audio Related content of .
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. }
processed AcceptCallBase() after , Need to use PackCellularCallInfo take call The information is packaged callinfo in , Follow up directly to this callInfo Pass out .
1. void CarrierCall::PackCellularCallInfo(CellularCallInfo &callInfo)
2. {
3. callInfo.callId = callId_;
4. callInfo.callType = callType_;
5. callInfo.videoState = (int32_t)videoState_;
6. callInfo.index = index_;
7. callInfo.slotId = slotId_;
8. (void)memset_s(callInfo.phoneNum, kMaxNumberLen, 0, kMaxNumberLen);
9. if (memcpy_s(callInfo.phoneNum, kMaxNumberLen, accountNumber_.c_str(), accountNumber_.length()) != 0) {
10. TELEPHONY_LOGW("memcpy_s failed!");
11. return;
12. }
13. }
The next step is to call CellularCallIpcInterfaceProxy Class come Answer Function to continue processing . First, judge whether there is ReConnectService The necessary , This operation is to ensure that there are available cellularCallInterfacePtr_.cellularCallInterfacePtr_ It's a IRemoteBroker class , This class is a IPC The base class interface is used for IPC signal communication .
1. int CellularCallIpcInterfaceProxy::Answer(const CellularCallInfo &callInfo)
2. {
3. if (ReConnectService() != TELEPHONY_SUCCESS) {
4. return TELEPHONY_ERR_IPC_CONNECT_STUB_FAIL;
5. }
6. std::lock_guard<std::mutex> lock(mutex_);
7. int errCode = cellularCallInterfacePtr_->Answer(callInfo);
8. if (errCode != TELEPHONY_SUCCESS) {
9. TELEPHONY_LOGE("answering call failed, errcode:%{public}d", errCode);
10. return TELEPHONY_ERR_FAIL;
11. }
12. return TELEPHONY_SUCCESS;
13. }
Before that call_manager Already in CellularCallIpcInterfaceProxy The initialization of will systemAbilityId(TELEPHONY_CELLULAR_CALL_SYS_ABILITY_ID) adopt ConnectService() Complete the correspondence SA agent IRemoteObject Acquisition , Then construct the corresponding Proxy class , So you can make sure IPC communication proxy and stub Corresponding . In the next section we will see , Corresponding stub In fact cellular_call Registered in .
1. void CellularCallIpcInterfaceProxy::Init(int32_t systemAbilityId)
2. {
3. systemAbilityId_ = systemAbilityId;
4.
5. int32_t result = ConnectService();
6. if (result != TELEPHONY_SUCCESS) {
7. TELEPHONY_LOGE("connect service failed,errCode: %{public}X", result);
8. Timer::start(CONNECT_SERVICE_WAIT_TIME, CellularCallIpcInterfaceProxy::task);
9. return;
10. }
11. TELEPHONY_LOGI("connected to cellular call service successfully!");
12. }
When called to cellularCallInterfacePtr_->Answer when ,CellularCallInterface Of Answer Virtual functions , Will be CellularCallProxy Of Answer rewrite , So actually implement CellularCallProxy Of Answer function , It's a IRemoteProxy class . and callInfo Information is converted into MessageParcel Form through IPC Of SendRequest issue .
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. }
So let's start here ,Answer Process in call_manager The processing of has been completed .
3.2.2.2 Answer The incident happened in cellular_call Processing in
IPC(Inter-Process Communication) And RPC(Remote Procedure Call) Mechanism is used to realize cross process communication , The difference is that the former uses Binder drive , Used for cross process communication within the device , The latter uses soft bus driver , Used for cross device and cross process communication .IPC and RPC The client is usually used - The server (Client-Server) Model , Service requester (Client) Available service providers (Server) Agent for (Proxy), The data communication between processes is realized by reading and writing data through this agent .
Usually ,Server Will register the system capability first (System Ability) To the System Capability Manager (System Ability Manager, abbreviation SAMgr) in ,SAMgr Responsible for managing these SA And to Client Provide relevant interfaces .Client With a specific SA signal communication , You have to start with SAMgr Get the SA Agent for , Then use agents and SA signal communication . Use Proxy Indicates the service requestor ,Stub Indicates that the service provider . Implementation code in /foundation/communication/ipc Under the table of contents .
SA Register and start :SA Need to put your own AbilityStub Case passed AddSystemAbility Interface registered to SystemAbilityManager.
SA Get and call : adopt SystemAbilityManager Of GetSystemAbility Method to get the corresponding SA Agent for IRemoteObject, And then construct AbilityProxy. So you can make sure proxy and stub Corresponding .
Last time we were in call_manager I see SA The acquisition process of , Let's take a look at this SA The registration process , Remember TELEPHONY_CELLULAR_CALL_SYS_ABILITY_ID This systemAbilityId Do you ? It's our registration SA The key to .
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. }
So we can finish stub Registration of , Passed before proxy The message will pass IPC It's been delivered here stub in .IPC The process is complicated , There will be no further discussion here , Interested students can learn by themselves .
because IPCObjectStub Of OnRemoteRequest Are virtual functions that will be inherited IPCObjectStub Class CellularCallStub Of OnRemoteRequest Function rewriting .
1. int32_t CellularCallStub::OnRemoteRequest(
2. uint32_t code, MessageParcel &data, MessageParcel &reply, MessageOption &option)
3. {
4. std::u16string myDescriptor = CellularCallStub::GetDescriptor();
5. std::u16string remoteDescriptor = data.ReadInterfaceToken();
6. if (myDescriptor != remoteDescriptor) {
7. TELEPHONY_LOGE("descriptor checked fail");
8. return TELEPHONY_ERR_DESCRIPTOR_MISMATCH;
9. }
10.
11. auto itFunc = requestFuncMap_.find(code);
12. if (itFunc != requestFuncMap_.end()) {
13. auto requestFunc = itFunc->second;
14. if (requestFunc != nullptr) {
15. return (this->*requestFunc)(data, reply);
16. }
17. }
18. TELEPHONY_LOGD("CellularCallStub::OnRemoteRequest, default case, need check.");
19. return IPCObjectStub::OnRemoteRequest(code, data, reply, option);
20. }
Here will be based on map Table to find the corresponding processing function , Before our code = 4 yes ANSWER, Corresponding processing should be performed here .
1. CellularCallStub::CellularCallStub()
2. {
3. Init();
4. TELEPHONY_LOGD("CellularCallStub::CellularCallStub");
5. requestFuncMap_[DIAL] = &CellularCallStub::DialInner;
6. requestFuncMap_[HANG_UP] = &CellularCallStub::HangUpInner;
7. requestFuncMap_[REJECT] = &CellularCallStub::RejectInner;
8. requestFuncMap_[ANSWER] = &CellularCallStub::AnswerInner;
9. requestFuncMap_[EMERGENCY_CALL] = &CellularCallStub::IsEmergencyPhoneNumberInner;
10. requestFuncMap_[HOLD_CALL] = &CellularCallStub::HoldCallInner;
11. requestFuncMap_[UN_HOLD_CALL] = &CellularCallStub::UnHoldCallInner;
12. requestFuncMap_[SWITCH_CALL] = &CellularCallStub::SwitchCallInner;
13. requestFuncMap_[COMBINE_CONFERENCE] = &CellularCallStub::CombineConferenceInner;
14. requestFuncMap_[SEPARATE_CONFERENCE] = &CellularCallStub::SeparateConferenceInner;
15. requestFuncMap_[CALL_SUPPLEMENT] = &CellularCallStub::CallSupplementInner;
16. requestFuncMap_[REGISTER_CALLBACK] = &CellularCallStub::RegisterCallBackInner;
17. requestFuncMap_[UNREGISTER_CALLBACK] = &CellularCallStub::UnRegisterCallBackInner;
18. requestFuncMap_[START_DTMF] = &CellularCallStub::StartDtmfInner;
19. requestFuncMap_[STOP_DTMF] = &CellularCallStub::StopDtmfInner;
20. requestFuncMap_[SEND_DTMF] = &CellularCallStub::SendDtmfInner;
21. requestFuncMap_[SEND_DTMF_STRING] = &CellularCallStub::SendDtmfStringInner;
22. requestFuncMap_[SET_CALL_TRANSFER] = &CellularCallStub::SetCallTransferInner;
23. requestFuncMap_[GET_CALL_TRANSFER] = &CellularCallStub::GetCallTransferInner;
24. requestFuncMap_[SET_CALL_WAITING] = &CellularCallStub::SetCallWaitingInner;
25. requestFuncMap_[GET_CALL_WAITING] = &CellularCallStub::GetCallWaitingInner;
26. requestFuncMap_[SET_CALL_RESTRICTION] = &CellularCallStub::SetCallRestrictionInner;
27. requestFuncMap_[GET_CALL_RESTRICTION] = &CellularCallStub::GetCallRestrictionInner;
28. requestFuncMap_[SET_CALL_PREFERENCE_MODE] = &CellularCallStub::SetCallPreferenceModeInner;
29. requestFuncMap_[GET_CALL_PREFERENCE_MODE] = &CellularCallStub::GetCallPreferenceModeInner;
30. requestFuncMap_[SET_LTE_IMS_SWITCH_STATUS] = &CellularCallStub::SetLteImsSwitchStatusInner;
31. requestFuncMap_[GET_LTE_IMS_SWITCH_STATUS] = &CellularCallStub::GetLteImsSwitchStatusInner;
32. requestFuncMap_[CTRL_CAMERA] = &CellularCallStub::CtrlCameraInner;
33. requestFuncMap_[SET_PREVIEW_WINDOW] = &CellularCallStub::SetPreviewWindowInner;
34. requestFuncMap_[SET_DISPLAY_WINDOW] = &CellularCallStub::SetDisplayWindowInner;
35. requestFuncMap_[SET_CAMERA_ZOOM] = &CellularCallStub::SetCameraZoomInner;
36. requestFuncMap_[SET_PAUSE_IMAGE] = &CellularCallStub::SetPauseImageInner;
37. requestFuncMap_[SET_DEVICE_DIRECTION] = &CellularCallStub::SetDeviceDirectionInner;
38. }
AnswerInner Subsequent processing will be carried out , This is from data The corresponding callinfo Information , At the end of the function, the... Calling this file is listed Answer(), And write the returned result to reply in , This return value is the result of the function execution , by Int32 Shaping values .
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 The function is to complete with ril Key to interaction . First of all, according to the callInfo Get information about calltype, There are currently three kinds of calltype, Respectively cs_call、ims_call、ott_call. In determining calltype after , adopt slotId_ stay GetCsControl() Get the corresponding CSControl object .
1. int32_t CellularCallStub::Answer(const CellularCallInfo &callInfo)
2. {
3. if (!IsValidSlotId(callInfo.slotId)) {
4. TELEPHONY_LOGE("CellularCallStub::Answer return, invalid slot id");
5. return CALL_ERR_INVALID_SLOT_ID;
6. }
7. if (CallType::TYPE_CS == callInfo.callType) {
8. auto control = GetCsControl(slotId_);
9. if (control == nullptr) {
10. TELEPHONY_LOGE("CellularCallStub::Answer return, control is nullptr");
11. return TELEPHONY_ERR_LOCAL_PTR_NULL;
12. }
13. return control->Answer(callInfo);
14. } else if (CallType::TYPE_IMS == callInfo.callType) {
15. auto imsControl = GetImsControl(slotId_);
16. if (imsControl == nullptr) {
17. TELEPHONY_LOGE("CellularCallStub::Answer return, imsControl is nullptr");
18. return TELEPHONY_ERR_LOCAL_PTR_NULL;
19. }
20. return imsControl->Answer(callInfo);
21. }
22. TELEPHONY_LOGE("CellularCallStub::Answer return, call type error.");
23. return TELEPHONY_ERR_ARGUMENT_INVALID;
24. }
This calls to CSControl::Answer function , Will be based on callInfo Get the corresponding CellularCallConnectionCS and CALL_STATUS, The status is incoming 、 Ring the bell 、 Wait state , It will call CellularCallConnectionCS Corresponding function .
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. }
Later, it will call CellularCallConnectionCS::AnswerRequest and core_service It's interactive .GetCore Will be based on slotId Get the corresponding Core, And then use Get Function to get the corresponding event, Set up Owner by CellularCallHandler, When waiting for the return, call back the corresponding 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. }
It's done here Answer Process in cellular_call Call to .
3.2.2.3 Answer The incident happened in core_servicel Processing in
This part of the code will call core Corresponding Answer function , The message will be delivered to those responsible for the core services and RIL Adapter Communication interaction tel_ril In the code .
1. void Core::Answer(const AppExecFwk::InnerEvent::Pointer &result)
2. {
3. if (telRilManager_ == nullptr) {
4. TELEPHONY_LOGE("telRilManager is null!");
5. return;
6. }
7. telRilManager_->Answer(result);
8. }
Go further to be responsible for tel_ril Management category TelRilManager in , Judge the corresponding telRilCall_ Is it empty , If it is not empty, you can continue to call .
1. void TelRilManager::Answer(const AppExecFwk::InnerEvent::Pointer &result)
2. {
3. if (telRilCall_ != nullptr) {
4. telRilCall_->Answer(result);
5. } else {
6. TELEPHONY_LOGE("telRilCall_ is null");
7. }
8. }
telRilCall_ Is in TelRilManager In the initialization of . among InitCellularRadio First, through ServiceManager Get cellular_radio1 Corresponding service. And then in InitTelInfo of use cellularRadio_ and observerHandler_ structure 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. }
Call to teRilCall Of Answer Function to process , use CreateTelRilRequest structure telRilRequest , adopt TelRilBase Of SendInt32Event issue .
1. void TelRilCall::Answer(const AppExecFwk::InnerEvent::Pointer &result)
2. {
3. std::shared_ptr<TelRilRequest> telRilRequest = CreateTelRilRequest(HREQ_CALL_ANSWER, result);
4. if (telRilRequest == nullptr) {
5. TELEPHONY_LOGE("telRilRequest is nullptr");
6. return;
7. }
8. if (cellularRadio_ == nullptr) {
9. TELEPHONY_LOGE("%{public}s cellularRadio_ == nullptr", __func__);
10. ErrorResponse(telRilRequest->serialId_, HRilErrType::HRIL_ERR_GENERIC_FAILURE);
11. return;
12. }
13.
14. int ret = SendInt32Event(HREQ_CALL_ANSWER, telRilRequest->serialId_);
15. TELEPHONY_LOGD("HREQ_CALL_ANSWER ret %{public}d", ret);
16. }
From the situation of calling , Then through IPC Of SendRequest The request will be dispatchId and data issue 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. }
Come here Answer Process in core_service The processing in the service is finished .
3.2.2.4 Answer The incident happened in ril_adapter Processing in
From the current code call, it may be from vendor call hril_hdf Of dispatch, Then deliver the message to ril_adapter in
Below is hril_hdf The initialization process .
1. struct HdfDriverEntry g_rilAdapterDevEntry = {
2. .moduleVersion = 1,
3. .moduleName = MODULE_NAME,
4. .Bind = RilAdapterBind,
5. .Init = RilAdapterInit,
6. .Release = RilAdapterRelease,
7. };
8. HDF_INIT(g_rilAdapterDevEntry);
You can see hdf Initialization of includes MODULE_ NAME、RilAdapterBind、RilAdapterInit.
Bind The process is as follows .
1. static int32_t RilAdapterBind(struct HdfDeviceObject *device)
2. {
3. if (device == NULL) {
4. return HDF_ERR_INVALID_OBJECT;
5. }
6. device->service = &g_rilAdapterService;
7. return HDF_SUCCESS;
8. }
You can see service Of dispatch Corresponding to RilAdapterDispatch.
1. static struct IDeviceIoService g_rilAdapterService = {
2. .Dispatch = RilAdapterDispatch,
3. .Open = NULL,
4. .Release = NULL,
5. };
Init The process is as follows :
1. static int32_t RilAdapterInit(struct HdfDeviceObject *device)
2. {
3. if (device == NULL) {
4. return HDF_ERR_INVALID_OBJECT;
5. }
6. DFX_InstallSignalHandler();
7. TELEPHONY_LOGD("Start %{public}s hdf service!", HdfDeviceGetServiceName(device));
8. struct HdfSBuf *sbuf = HdfSBufTypedObtain(SBUF_IPC);
9. if (sbuf == NULL) {
10. TELEPHONY_LOGE("HdfSampleDriverBind, failed to obtain ipc sbuf");
11. return HDF_ERR_INVALID_OBJECT;
12. }
13. if (!HdfSbufWriteString(sbuf, "string")) {
14. TELEPHONY_LOGE("HdfSampleDriverBind, failed to write string to ipc sbuf");
15. HdfSBufRecycle(sbuf);
16. return HDF_FAILURE;
17. }
18. if (sbuf != NULL) {
19. HdfSBufRecycle(sbuf);
20. }
21. TELEPHONY_LOGD("sbuf IPC obtain test success!");
22. if (!IsLoadedVendorLib()) {
23. LoadVendor();
24. } else {
25. TELEPHONY_LOGI("The vendor library has been loaded!");
26. }
27. return HDF_SUCCESS;
28. }
among LoadVendor The corresponding... Will be loaded vendor Of 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. }
As I said before, it could be vendor Called dispatch, Now the corresponding RilAdapterDispatch, The follow-up treatment is as follows .
C++ Written IPC and C Written language IPC Can communicate with each other , The format is not the same .HdfSBuf Can also be combined with MessageParcel Interturn .
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 Message through IPC to RilAdapter It will pass DispatchRequest Distribute .
1. int32_t DispatchRequest(int32_t slotId, int32_t cmd, struct HdfSBuf *data)
2. {
3. if (data == nullptr) {
4. TELEPHONY_LOGE("miss callback parameter");
5. return HDF_ERR_INVALID_PARAM;
6. }
7. switch (cmd) {
8. case HRIL_ADAPTER_RADIO_INDICATION: {
9. HdfRemoteService *serviceCallbackInd = HdfSBufReadRemoteService(data);
10. if (serviceCallbackInd == nullptr) {
11. TELEPHONY_LOGE("miss callback parameter");
12. return HDF_ERR_INVALID_PARAM;
13. }
14. RegisterManagerNotifyCallback(slotId, serviceCallbackInd);
15. break;
16. }
17. case HRIL_ADAPTER_RADIO_RESPONSE: {
18. HdfRemoteService *serviceCallback = HdfSBufReadRemoteService(data);
19. if (serviceCallback == nullptr) {
20. TELEPHONY_LOGE("miss callback parameter");
21. return HDF_ERR_INVALID_PARAM;
22. }
23. RegisterManagerResponseCallback(slotId, serviceCallback);
24. break;
25. }
26. default:
27. DispatchModule(slotId, cmd, data);
28. }
29. return HDF_SUCCESS;
30. }
In subsequent calls, we know code by HREQ_CALL_ANSWER, So I call 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. }
according to slotId Get the corresponding g_manager Of Dispatch function .
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. }
Because of our code = 4, stay 0 To 100 Between , So it will be judged as callrequest
So call the following function .
1. void HRilCall::ProcessCallRequest(int32_t slotId, int32_t code, struct HdfSBuf *data)
2. {
3. auto itFunc = reqMemberFuncMap_.find(code);
4. if (itFunc != reqMemberFuncMap_.end()) {
5. auto memberFunc = itFunc->second;
6. if (memberFunc != nullptr) {
7. (this->*memberFunc)(slotId, data);
8. }
9. } else {
10. TELEPHONY_LOGE("Can not find CallRequest code in reqMemberFuncMap_!");
11. }
12. }
Request as follows .
1. // request
2. reqMemberFuncMap_[HREQ_CALL_GET_CALL_LIST] = &HRilCall::GetCallList;
3. reqMemberFuncMap_[HREQ_CALL_DIAL] = &HRilCall::Dial;
4. reqMemberFuncMap_[HREQ_CALL_HANGUP] = &HRilCall::Hangup;
5. reqMemberFuncMap_[HREQ_CALL_REJECT] = &HRilCall::Reject;
6. reqMemberFuncMap_[HREQ_CALL_ANSWER] = &HRilCall::Answer;
7. reqMemberFuncMap_[HREQ_CALL_HOLD_CALL] = &HRilCall::HoldCall;
8. reqMemberFuncMap_[HREQ_CALL_UNHOLD_CALL] = &HRilCall::UnHoldCall;
9. reqMemberFuncMap_[HREQ_CALL_SWITCH_CALL] = &HRilCall::SwitchCall;
10. reqMemberFuncMap_[HREQ_CALL_GET_CLIP] = &HRilCall::GetClip;
11. reqMemberFuncMap_[HREQ_CALL_SET_CLIP] = &HRilCall::SetClip;
12. reqMemberFuncMap_[HREQ_CALL_COMBINE_CONFERENCE] = &HRilCall::CombineConference;
13. reqMemberFuncMap_[HREQ_CALL_SEPARATE_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;
call request The corresponding processing function is 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. }
The first thing to do is to create HRilRequest, Encapsulate information into requestInfo in .
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. }
stay LoadVendor Called when the HRilRegOps(ops) It's going to be 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. }
Here will be callOps Sign up to callFuncs_ in , and g_callBacks It actually corresponds to 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 Corresponding to &g_callReqOps, The corresponding function comparison table is as follows .
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. };
Find the right one ReqAnswer Follow up , In fact, we have reached AT Command processing layer .
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. }
Subsequent calls SendCommandLock function , Here will be requestInfo Processing into AT command .
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. }
The actual processing is in this function 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. }
adopt WriteATCommand take AT Command to modem, And then wait modem The result of reply processing .
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. }
So far ,Answer The process has been completed from app To modem The transmission of information .
5、 ... and 、 summary
From the analysis process of the third chapter , We've finished from Call ui Respond to incoming calls , Then the whole framework layer call process is completed step by step , In fact, at the end, we just finished the downward transmission of the message modem, After this process modem Also reply , This process is also relatively long, so I won't repeat it here . If we have the opportunity, we can analyze it in the following articles .
The whole document is limited by my ability and time , Make up the missing parts as much as possible . from core_service To ril_adapter Call to , Is in vendor In the directory cellular_radio1 Of libhril_hdf.z.so load . Because the current code is still improving , It is possible that the latest version of the code will change .
HD flowchart download :https://ost.51cto.com/resource/1716