作者:王大鹏
简介
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…