1/*
2 * Copyright (C) 2021-2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#include "ims_control.h"
17
18#include "cellular_call_hisysevent.h"
19#include "cellular_call_register.h"
20#include "emergency_utils.h"
21#include "module_service_utils.h"
22#include "securec.h"
23#include "standardize_utils.h"
24
25namespace OHOS {
26namespace Telephony {
27IMSControl::~IMSControl()
28{
29    TELEPHONY_LOGI("~IMSControl start");
30    ReleaseAllConnection();
31}
32
33int32_t IMSControl::Dial(const CellularCallInfo &callInfo, bool isEcc)
34{
35    TELEPHONY_LOGI("Dial start");
36    DelayedSingleton<CellularCallHiSysEvent>::GetInstance()->SetCallParameterInfo(
37        callInfo.slotId, static_cast<int32_t>(callInfo.callType), callInfo.videoState);
38    int32_t ret = DialPreJudgment(callInfo, isEcc);
39    if (ret != TELEPHONY_SUCCESS) {
40        return ret;
41    }
42    ModuleServiceUtils moduleServiceUtils;
43    RegServiceState regState = moduleServiceUtils.GetPsRegState(callInfo.slotId);
44    if (!(regState == RegServiceState::REG_STATE_IN_SERVICE || isEcc)) {
45        TELEPHONY_LOGE("can not dial.");
46        return TELEPHONY_ERR_NETWORK_NOT_IN_SERVICE;
47    }
48    // sip uri needs to remove separator
49    std::string newPhoneNum(callInfo.phoneNum);
50    StandardizeUtils standardizeUtils;
51    if (newPhoneNum.find('@') != std::string::npos || newPhoneNum.find("%40") != std::string::npos) {
52        newPhoneNum = standardizeUtils.RemoveSeparatorsPhoneNumber(newPhoneNum);
53    }
54
55    CLIRMode clirMode = CLIRMode::DEFAULT;
56    if (IsNeedExecuteMMI(callInfo.slotId, newPhoneNum, clirMode, true)) {
57        TELEPHONY_LOGI("Dial return, mmi code type.");
58        return RETURN_TYPE_MMI;
59    }
60    return DialJudgment(callInfo.slotId, newPhoneNum, clirMode, callInfo.videoState);
61}
62
63int32_t IMSControl::DialJudgment(int32_t slotId, const std::string &phoneNum, CLIRMode &clirMode, int32_t videoState)
64{
65    TELEPHONY_LOGI("DialJudgment entry.");
66    std::lock_guard<std::recursive_mutex> lock(connectionMapMutex_);
67    if (!CanCall(connectionMap_)) {
68        TELEPHONY_LOGE("DialJudgment return, error type: call state error.");
69        CellularCallHiSysEvent::WriteDialCallFaultEvent(
70            slotId, INVALID_PARAMETER, videoState, CALL_ERR_CALL_COUNTS_EXCEED_LIMIT, "ims dial call state error");
71        return CALL_ERR_CALL_COUNTS_EXCEED_LIMIT;
72    }
73    pendingPhoneNumber_ = phoneNum;
74    // Calls can be put on hold, recovered, released, added to conversation,
75    // and transferred similarly as defined in 3GPP TS 22.030 [19].
76    for (auto &connection : connectionMap_) {
77        if (connection.second.GetStatus() == TelCallState::CALL_STATUS_ACTIVE &&
78            !connection.second.IsPendingHangup()) {
79            TELEPHONY_LOGI("DialJudgment, have connection in active state.");
80            EmergencyUtils emergencyUtils;
81            bool isEmergency = false;
82            emergencyUtils.IsEmergencyCall(slotId, phoneNum, isEmergency);
83            connection.second.SetHoldToDialInfo(phoneNum, clirMode, videoState, isEmergency);
84            connection.second.SetDialFlag(true);
85            // - a call can be temporarily disconnected from the ME but the connection is retained by the network
86            return connection.second.SwitchCallRequest(slotId);
87        }
88    }
89    return EncapsulateDial(slotId, phoneNum, clirMode, videoState);
90}
91
92int32_t IMSControl::EncapsulateDial(
93    int32_t slotId, const std::string &phoneNum, CLIRMode &clirMode, int32_t videoState) const
94{
95    TELEPHONY_LOGI("EncapsulateDial start");
96
97    ImsDialInfoStruct dialInfo;
98    dialInfo.videoState = videoState;
99    dialInfo.bEmergencyCall = false;
100    EmergencyUtils emergencyUtils;
101    emergencyUtils.IsEmergencyCall(slotId, phoneNum, dialInfo.bEmergencyCall);
102
103    /**
104     * <idx>: integer type;
105     * call identification number as described in 3GPP TS 22.030 [19] subclause 4.5.5.1
106     * this number can be used in +CHLD command operations
107     * <dir>:
108     */
109    dialInfo.phoneNum = phoneNum;
110    /**
111     * <n> (parameter sets the adjustment for outgoing calls):
112     *  0	presentation indicator is used according to the subscription of the CLIR service
113     *  1	CLIR invocation
114     *  2	CLIR suppression
115     */
116    dialInfo.clirMode = clirMode;
117    /**
118     * An example of voice group call service request usage:
119     * ATD*17*753#500; (originate voice group call with the priority level 3)
120     * OK (voice group call setup was successful)
121     */
122
123    CellularCallConnectionIMS cellularCallConnectionIms;
124    return cellularCallConnectionIms.DialRequest(slotId, dialInfo);
125}
126
127int32_t IMSControl::HangUp(const CellularCallInfo &callInfo, CallSupplementType type)
128{
129    TELEPHONY_LOGI("HangUp start");
130    switch (type) {
131        case CallSupplementType::TYPE_DEFAULT: {
132            std::lock_guard<std::recursive_mutex> lock(connectionMapMutex_);
133            auto pConnection = FindConnectionByIndex<ImsConnectionMap &, CellularCallConnectionIMS *>(
134                connectionMap_, callInfo.index);
135            if (pConnection == nullptr) {
136                TELEPHONY_LOGE("HangUp return, error type: connection is null");
137                return CALL_ERR_CALL_CONNECTION_NOT_EXIST;
138            }
139            pConnection->SetHangupFlag(true);
140            if (DelayedSingleton<CellularCallRegister>::GetInstance() != nullptr) {
141                DelayedSingleton<CellularCallRegister>::GetInstance()->ReportSingleCallInfo(
142                    pConnection->GetCallReportInfo(), TelCallState::CALL_STATUS_DISCONNECTING);
143            }
144            return pConnection->HangUpRequest(callInfo.slotId, callInfo.phoneNum, callInfo.index);
145        }
146        case CallSupplementType::TYPE_HANG_UP_HOLD_WAIT:
147            // release the second (active) call and recover the first (held) call
148        case CallSupplementType::TYPE_HANG_UP_ACTIVE: {
149            CellularCallConnectionIMS connection;
150            return connection.CallSupplementRequest(callInfo.slotId, type);
151        }
152        case CallSupplementType::TYPE_HANG_UP_ALL: {
153            TELEPHONY_LOGI("HangUp, hang up all call");
154            CellularCallConnectionIMS connection;
155            // The AT command for hanging up all calls is the same as the AT command for rejecting calls,
156            // so the reject interface is reused.
157            return connection.RejectRequest(callInfo.slotId, callInfo.phoneNum, callInfo.index);
158        }
159        default: {
160            TELEPHONY_LOGE("HangUp warring, type is invalid");
161            return TELEPHONY_ERR_ARGUMENT_INVALID;
162        }
163    }
164}
165
166int32_t IMSControl::Answer(const CellularCallInfo &callInfo)
167{
168    TELEPHONY_LOGI("IMSControl::Answer start");
169    std::lock_guard<std::recursive_mutex> lock(connectionMapMutex_);
170    auto pConnection = FindConnectionByIndex<ImsConnectionMap &, CellularCallConnectionIMS *>(
171        connectionMap_, callInfo.index);
172    if (pConnection == nullptr) {
173        TELEPHONY_LOGE("HangUp return, error type: connection is null");
174        return CALL_ERR_CALL_CONNECTION_NOT_EXIST;
175    }
176    if (IsInState(connectionMap_, TelCallState::CALL_STATUS_HOLDING) &&
177        IsInState(connectionMap_, TelCallState::CALL_STATUS_ACTIVE)) {
178        TELEPHONY_LOGD("already threeway mode. hangup holding call and pickup new call");
179        int32_t ret = CheckAndHangupHoldingCall();
180        if (ret != TELEPHONY_SUCCESS) {
181            TELEPHONY_LOGE("hangup holding call failed");
182            return ret;
183        }
184    }
185    auto con = FindConnectionByState<ImsConnectionMap &, CellularCallConnectionIMS *>(
186        connectionMap_, TelCallState::CALL_STATUS_ACTIVE);
187    if (con != nullptr && !con->IsPendingHold() &&
188        pConnection->GetStatus() == TelCallState::CALL_STATUS_WAITING) {
189        TELEPHONY_LOGI("Answer there is an active call when you call, or third party call waiting");
190        return con->SwitchCallRequest(callInfo.slotId);
191    }
192    if (pConnection->GetStatus() == TelCallState::CALL_STATUS_ALERTING ||
193        pConnection->GetStatus() == TelCallState::CALL_STATUS_INCOMING ||
194        pConnection->GetStatus() == TelCallState::CALL_STATUS_WAITING) {
195        return pConnection->AnswerRequest(callInfo.slotId, callInfo.phoneNum, callInfo.videoState, callInfo.index);
196    }
197    TELEPHONY_LOGE("IMSControl::Answer return, error type: call state error, phone not ringing.");
198    return CALL_ERR_CALL_STATE;
199}
200
201int32_t IMSControl::CheckAndHangupHoldingCall()
202{
203    for (auto &it : connectionMap_) {
204        CellularCallConnectionIMS holdConn = it.second;
205        if (holdConn.GetStatus() == TelCallState::CALL_STATUS_HOLDING) {
206            auto callReportInfo = holdConn.GetCallReportInfo();
207            int32_t result = holdConn.HangUpRequest(callReportInfo.accountId,
208                callReportInfo.accountNum, callReportInfo.index);
209            if (result != TELEPHONY_SUCCESS) {
210                return result;
211            }
212        }
213    }
214    return TELEPHONY_SUCCESS;
215}
216
217int32_t IMSControl::Reject(const CellularCallInfo &callInfo)
218{
219    TELEPHONY_LOGI("IMSControl::Reject start");
220    std::lock_guard<std::recursive_mutex> lock(connectionMapMutex_);
221    auto pConnection =
222        FindConnectionByIndex<ImsConnectionMap &, CellularCallConnectionIMS *>(connectionMap_, callInfo.index);
223    if (pConnection == nullptr) {
224        TELEPHONY_LOGE("IMSControl::Reject, error type: connection is null");
225        return CALL_ERR_CALL_CONNECTION_NOT_EXIST;
226    }
227    if (!pConnection->IsRingingState()) {
228        TELEPHONY_LOGE("IMSControl::Reject return, error type: call state error, phone not ringing.");
229        return CALL_ERR_CALL_STATE;
230    }
231    if (DelayedSingleton<CellularCallRegister>::GetInstance() != nullptr) {
232        DelayedSingleton<CellularCallRegister>::GetInstance()->ReportSingleCallInfo(
233            pConnection->GetCallReportInfo(), TelCallState::CALL_STATUS_DISCONNECTING);
234    }
235    return pConnection->RejectRequest(callInfo.slotId, callInfo.phoneNum, callInfo.index);
236}
237
238int32_t IMSControl::HoldCall(int32_t slotId)
239{
240    TELEPHONY_LOGI("HoldCall start");
241    std::lock_guard<std::recursive_mutex> lock(connectionMapMutex_);
242    if (IsInState(connectionMap_, TelCallState::CALL_STATUS_INCOMING)) {
243        TELEPHONY_LOGE("HoldCall return, error type: call state error.");
244        return CALL_ERR_CALL_STATE;
245    }
246    CellularCallConnectionIMS cellularCallConnectionIms;
247    return cellularCallConnectionIms.HoldCallRequest(slotId);
248}
249
250int32_t IMSControl::UnHoldCall(int32_t slotId)
251{
252    TELEPHONY_LOGI("UnHoldCall start");
253    CellularCallConnectionIMS cellularCallConnectionIms;
254    return cellularCallConnectionIms.UnHoldCallRequest(slotId);
255}
256
257int32_t IMSControl::SwitchCall(int32_t slotId)
258{
259    TELEPHONY_LOGI("SwitchCall start");
260    CellularCallConnectionIMS cellularCallConnectionIms;
261    return cellularCallConnectionIms.SwitchCallRequest(slotId);
262}
263
264/**
265 * Add another remote party, to which a private communication has been established using
266 * the same procedures as in Section 1.3.8.1, if the number of remote parties does not then
267 * exceed the maximum number allowed, which results in an active multiParty call.
268 */
269int32_t IMSControl::CombineConference(int32_t slotId)
270{
271    TELEPHONY_LOGI("CombineConference entry");
272    CellularCallConnectionIMS connection;
273    int32_t voiceCall = 0;
274    return connection.CombineConferenceRequest(slotId, voiceCall);
275}
276
277int32_t IMSControl::HangUpAllConnection(int32_t slotId)
278{
279    TELEPHONY_LOGI("HangUpAllConnection entry");
280    CellularCallConnectionIMS connection;
281    std::lock_guard<std::recursive_mutex> lock(connectionMapMutex_);
282    if (connectionMap_.empty()) {
283        TELEPHONY_LOGI("connectionMap_ is empty.");
284        return TELEPHONY_ERROR;
285    }
286    for (auto &it : connectionMap_) {
287        int32_t index = it.second.GetIndex();
288        std::string number = it.second.GetNumber();
289        connection.RejectRequest(slotId, number, index);
290    }
291    // The AT command for hanging up all calls is the same as the AT command for rejecting calls,
292    // so the reject interface is reused.
293    return TELEPHONY_SUCCESS;
294}
295
296int32_t IMSControl::InviteToConference(int32_t slotId, const std::vector<std::string> &numberList)
297{
298    TELEPHONY_LOGI("InviteToConference entry");
299    CellularCallConnectionIMS connection;
300    return connection.InviteToConferenceRequest(slotId, numberList);
301}
302
303int32_t IMSControl::KickOutFromConference(int32_t slotId, const std::string &KickOutString, int32_t index)
304{
305    TELEPHONY_LOGI("KickOutFromConference entry");
306    if (KickOutString.empty()) {
307        TELEPHONY_LOGW("KickOutFromConference, splitString is empty.");
308    }
309
310    std::lock_guard<std::recursive_mutex> lock(connectionMapMutex_);
311    auto pConnection = FindConnectionByIndex<ImsConnectionMap &, CellularCallConnectionIMS *>(connectionMap_, index);
312    if (pConnection != nullptr) {
313        return pConnection->KickOutFromConferenceRequest(slotId, pConnection->GetIndex());
314    }
315
316    TELEPHONY_LOGI("KickOutFromConference: connection cannot be matched, use index directly");
317    CellularCallConnectionIMS connection;
318    return connection.KickOutFromConferenceRequest(slotId, index);
319}
320
321int32_t IMSControl::StartRtt(int32_t slotId, const std::string &msg)
322{
323    TELEPHONY_LOGI("StartRtt entry");
324    CellularCallConnectionIMS connection;
325    return connection.StartRttRequest(slotId, msg);
326}
327
328int32_t IMSControl::StopRtt(int32_t slotId)
329{
330    TELEPHONY_LOGI("StopRtt entry");
331    CellularCallConnectionIMS connection;
332    return connection.StopRttRequest(slotId);
333}
334
335void IMSControl::ReleaseAllConnection()
336{
337    TELEPHONY_LOGI("ReleaseAllConnection entry");
338    std::lock_guard<std::recursive_mutex> lock(connectionMapMutex_);
339    connectionMap_.clear();
340}
341
342ImsConnectionMap IMSControl::GetConnectionMap()
343{
344    TELEPHONY_LOGI("GetConnectionMap entry");
345    std::lock_guard<std::recursive_mutex> lock(connectionMapMutex_);
346    return connectionMap_;
347}
348
349int32_t IMSControl::ReportImsCallsData(int32_t slotId, const ImsCurrentCallList &callInfoList)
350{
351    std::lock_guard<std::recursive_mutex> lock(connectionMapMutex_);
352    if (callInfoList.callSize <= 0) {
353        return ReportHangUpInfo(slotId);
354    } else if (callInfoList.callSize > 0 && connectionMap_.empty()) {
355        return ReportIncomingInfo(slotId, callInfoList);
356    } else if (callInfoList.callSize > 0 && !connectionMap_.empty()) {
357        return ReportUpdateInfo(slotId, callInfoList);
358    }
359    return TELEPHONY_ERROR;
360}
361
362int32_t IMSControl::ReportCallsData(int32_t slotId, const CallInfoList &callInfoList)
363{
364    return TELEPHONY_ERROR;
365}
366
367int32_t IMSControl::ReportHangUpInfo(int32_t slotId)
368{
369    TELEPHONY_LOGI("ReportHangUpInfo entry");
370    CallsReportInfo callsReportInfo;
371    for (auto &it : connectionMap_) {
372        CallReportInfo reportInfo = it.second.GetCallReportInfo();
373        reportInfo.state = TelCallState::CALL_STATUS_DISCONNECTED;
374        reportInfo.accountId = slotId;
375        callsReportInfo.callVec.push_back(reportInfo);
376        GetCallFailReason(slotId, connectionMap_);
377    }
378    if (connectionMap_.empty()) {
379        TELEPHONY_LOGI("connectionMap_ is empty");
380        CallReportInfo reportInfo;
381        reportInfo.state = TelCallState::CALL_STATUS_DISCONNECTED;
382        reportInfo.accountId = slotId;
383        callsReportInfo.callVec.push_back(reportInfo);
384    }
385    if (DelayedSingleton<CellularCallRegister>::GetInstance() == nullptr) {
386        TELEPHONY_LOGE("ReportHangUpInfo return, GetInstance() is nullptr.");
387        return TELEPHONY_ERR_LOCAL_PTR_NULL;
388    }
389    callsReportInfo.slotId = slotId;
390    if (isIgnoredIncomingCall_) {
391        isIgnoredIncomingCall_ = false;
392    } else {
393        DelayedSingleton<CellularCallRegister>::GetInstance()->ReportCallsInfo(callsReportInfo);
394    }
395    ReleaseAllConnection();
396    return TELEPHONY_SUCCESS;
397}
398
399int32_t IMSControl::ReportIncomingInfo(int32_t slotId, const ImsCurrentCallList &imsCurrentCallInfoList)
400{
401    TELEPHONY_LOGI("ReportIncomingInfo entry");
402    CallsReportInfo callsReportInfo;
403    for (int32_t i = 0; i < imsCurrentCallInfoList.callSize; ++i) {
404        CallReportInfo reportInfo = EncapsulationCallReportInfo(slotId, imsCurrentCallInfoList.calls[i]);
405
406        CellularCallConnectionIMS connection;
407        connection.SetStatus(static_cast<TelCallState>(imsCurrentCallInfoList.calls[i].state));
408        connection.SetIndex(imsCurrentCallInfoList.calls[i].index);
409        connection.SetNumber(imsCurrentCallInfoList.calls[i].number);
410        connection.SetOrUpdateCallReportInfo(reportInfo);
411        SetConnectionData(connectionMap_, imsCurrentCallInfoList.calls[i].index, connection);
412
413        callsReportInfo.callVec.push_back(reportInfo);
414    }
415    if (DelayedSingleton<CellularCallRegister>::GetInstance() == nullptr) {
416        TELEPHONY_LOGE("ReportIncomingInfo return, GetInstance() is nullptr.");
417        return TELEPHONY_ERR_ARGUMENT_INVALID;
418    }
419    callsReportInfo.slotId = slotId;
420    if (!DelayedSingleton<CellularCallRegister>::GetInstance()->IsCallManagerCallBackRegistered() &&
421        callsReportInfo.callVec.size() != 0 && callsReportInfo.callVec[0].state == TelCallState::CALL_STATUS_INCOMING) {
422        isIgnoredIncomingCall_ = true;
423    } else {
424        DelayedSingleton<CellularCallRegister>::GetInstance()->ReportCallsInfo(callsReportInfo);
425    }
426    return TELEPHONY_SUCCESS;
427}
428
429int32_t IMSControl::ReportUpdateInfo(int32_t slotId, const ImsCurrentCallList &callInfoList)
430{
431    TELEPHONY_LOGD("ReportUpdateInfo entry");
432    CallsReportInfo callsReportInfo;
433    for (int32_t i = 0; i < callInfoList.callSize; ++i) {
434        CallReportInfo reportInfo = EncapsulationCallReportInfo(slotId, callInfoList.calls[i]);
435        if (callInfoList.callSize == 1 && reportInfo.state == TelCallState::CALL_STATUS_WAITING) {
436            TELEPHONY_LOGI("only one call, report incoming state instead of waiting state");
437            reportInfo.state = TelCallState::CALL_STATUS_INCOMING;
438        }
439        auto pConnection = FindConnectionByIndex<ImsConnectionMap &, CellularCallConnectionIMS *>(
440            connectionMap_, callInfoList.calls[i].index);
441        if (pConnection == nullptr) {
442            CellularCallConnectionIMS connection;
443            connection.SetOrUpdateCallReportInfo(reportInfo);
444            connection.SetFlag(true);
445            connection.SetIndex(callInfoList.calls[i].index);
446            connection.SetNumber(callInfoList.calls[i].number);
447            SetConnectionData(connectionMap_, callInfoList.calls[i].index, connection);
448        } else {
449            TelCallState preCallState = pConnection->GetStatus();
450            pConnection->SetFlag(true);
451            pConnection->SetIndex(callInfoList.calls[i].index);
452            pConnection->SetNumber(callInfoList.calls[i].number);
453            pConnection->SetOrUpdateCallReportInfo(reportInfo);
454            TelCallState curCallState = pConnection->GetStatus();
455            if (IsConnectedOut(preCallState, curCallState)) {
456                pConnection->UpdateCallNumber(pendingPhoneNumber_);
457                pendingPhoneNumber_.clear();
458                ExecutePostDial(slotId, pConnection->GetIndex());
459            }
460        }
461        callsReportInfo.callVec.push_back(reportInfo);
462    }
463    callsReportInfo.slotId = slotId;
464    DeleteConnection(callsReportInfo, callInfoList);
465    if (DelayedSingleton<CellularCallRegister>::GetInstance() == nullptr) {
466        TELEPHONY_LOGE("ReportUpdateInfo return, GetInstance() is nullptr.");
467        return TELEPHONY_ERR_LOCAL_PTR_NULL;
468    }
469    if (isIgnoredIncomingCall_) {
470        isIgnoredIncomingCall_ = false;
471    } else {
472        DelayedSingleton<CellularCallRegister>::GetInstance()->ReportCallsInfo(callsReportInfo);
473    }
474    return TELEPHONY_SUCCESS;
475}
476
477CallReportInfo IMSControl::EncapsulationCallReportInfo(int32_t slotId, const ImsCurrentCall &callInfo)
478{
479    TELEPHONY_LOGD("EncapsulationCallReportInfo entry");
480    CallReportInfo callReportInfo;
481    if (memset_s(&callReportInfo, sizeof(callReportInfo), 0, sizeof(callReportInfo)) != EOK) {
482        TELEPHONY_LOGE("EncapsulationCallReportInfo return, memset_s fail.");
483        return callReportInfo;
484    }
485
486    StandardizeUtils standardizeUtils;
487    std::string phoneNumber = callInfo.number.empty() ? callInfo.name : callInfo.number;
488    std::string newString = standardizeUtils.FormatNumberAndToa(phoneNumber, callInfo.toa);
489    size_t cpyLen = strlen(newString.c_str()) + 1;
490    if (cpyLen > static_cast<size_t>(kMaxNumberLen + 1)) {
491        TELEPHONY_LOGE("EncapsulationCallReportInfo return, strcpy_s fail.");
492        return callReportInfo;
493    }
494    if (strcpy_s(callReportInfo.accountNum, cpyLen, newString.c_str()) != EOK) {
495        TELEPHONY_LOGE("EncapsulationCallReportInfo return, strcpy_s fail.");
496        return callReportInfo;
497    }
498    callReportInfo.index = callInfo.index;
499    callReportInfo.accountId = slotId;
500    callReportInfo.state = static_cast<TelCallState>(callInfo.state);
501    callReportInfo.voiceDomain = callInfo.voiceDomain;
502    callReportInfo.callType = CallType::TYPE_IMS;
503    switch (callInfo.callType) {
504        case ImsCallType::TEL_IMS_CALL_TYPE_VOICE:
505            callReportInfo.callMode = VideoStateType::TYPE_VOICE;
506            break;
507        case ImsCallType::TEL_IMS_CALL_TYPE_VT_TX:
508            callReportInfo.callMode = VideoStateType::TYPE_SEND_ONLY;
509            break;
510        case ImsCallType::TEL_IMS_CALL_TYPE_VT_RX:
511            callReportInfo.callMode = VideoStateType::TYPE_RECEIVE_ONLY;
512            break;
513        case ImsCallType::TEL_IMS_CALL_TYPE_VT:
514            callReportInfo.callMode = VideoStateType::TYPE_VIDEO;
515            break;
516        default:
517            callReportInfo.callMode = VideoStateType::TYPE_VOICE;
518            break;
519    }
520    callReportInfo.mpty = callInfo.mpty;
521    callReportInfo.crsType = callInfo.toneType;
522    callReportInfo.originalCallType = callInfo.callInitialType;
523    return callReportInfo;
524}
525
526void IMSControl::DeleteConnection(CallsReportInfo &callsReportInfo, const ImsCurrentCallList &callInfoList)
527{
528    TELEPHONY_LOGI("DeleteConnection entry");
529    auto it = connectionMap_.begin();
530    while (it != connectionMap_.end()) {
531        if (!it->second.GetFlag()) {
532            CallReportInfo callReportInfo = it->second.GetCallReportInfo();
533            callReportInfo.state = TelCallState::CALL_STATUS_DISCONNECTED;
534            callsReportInfo.callVec.push_back(callReportInfo);
535            it = connectionMap_.erase(it);
536            GetCallFailReason(callsReportInfo.slotId, connectionMap_);
537        } else {
538            it->second.SetFlag(false);
539            ++it;
540        }
541    }
542}
543
544int32_t IMSControl::ExecutePostDial(int32_t slotId, int64_t callId)
545{
546    TELEPHONY_LOGI("ExecutePostDial entry");
547    std::lock_guard<std::recursive_mutex> lock(connectionMapMutex_);
548    if (connectionMap_.empty()) {
549        TELEPHONY_LOGE("connectionMap_ is empty.");
550        return TELEPHONY_ERROR;
551    }
552    auto pConnection = FindConnectionByIndex<ImsConnectionMap &, CellularCallConnectionIMS *>(connectionMap_, callId);
553    if (pConnection == nullptr) {
554        return TELEPHONY_ERR_LOCAL_PTR_NULL;
555    }
556    char currentChar;
557    PostDialCallState state = pConnection->ProcessNextChar(slotId, currentChar);
558    switch (state) {
559        case PostDialCallState::POST_DIAL_CALL_STARTED:
560            DelayedSingleton<CellularCallRegister>::GetInstance()->ReportPostDialChar(currentChar);
561            break;
562        case PostDialCallState::POST_DIAL_CALL_DELAY:
563            DelayedSingleton<CellularCallRegister>::GetInstance()->ReportPostDialDelay(
564                pConnection->GetLeftPostDialCallString());
565            break;
566        default:
567            break;
568    }
569    return TELEPHONY_SUCCESS;
570}
571
572int32_t IMSControl::PostDialProceed(const CellularCallInfo &callInfo, const bool proceed)
573{
574    TELEPHONY_LOGI("PostDialProceed entry");
575    std::string networkAddress;
576    std::string postDialString;
577    StandardizeUtils standardizeUtils;
578    standardizeUtils.ExtractAddressAndPostDial(callInfo.phoneNum, networkAddress, postDialString);
579    std::lock_guard<std::recursive_mutex> lock(connectionMapMutex_);
580    auto pConnection = FindConnectionByIndex<ImsConnectionMap &, CellularCallConnectionIMS *>(connectionMap_,
581        callInfo.index);
582    if (pConnection == nullptr) {
583        TELEPHONY_LOGE("ims pConnection is nullptr!");
584        return TELEPHONY_ERR_LOCAL_PTR_NULL;
585    }
586    if (proceed) {
587        ExecutePostDial(callInfo.slotId, pConnection->GetIndex());
588    } else {
589        pConnection->SetPostDialCallState(PostDialCallState::POST_DIAL_CALL_CANCELED);
590    }
591    return TELEPHONY_SUCCESS;
592}
593
594int32_t IMSControl::RestoreConnection(const std::vector<CellularCallInfo> &infos, int32_t slotId)
595{
596    TELEPHONY_LOGI("RestoreConnection entry");
597    std::lock_guard<std::recursive_mutex> lock(connectionMapMutex_);
598    for (auto &info : infos) {
599        if (info.callType == CallType::TYPE_IMS && info.slotId == slotId) {
600            CellularCallConnectionIMS connectionIMS;
601            connectionIMS.SetIndex(info.index);
602            connectionIMS.SetNumber(info.phoneNum);
603            SetConnectionData(connectionMap_, info.index, connectionIMS);
604        }
605    }
606    return TELEPHONY_SUCCESS;
607}
608
609int32_t IMSControl::ReportHangUp(const std::vector<CellularCallInfo> &infos, int32_t slotId)
610{
611    CallsReportInfo callsReportInfo;
612    callsReportInfo.slotId = slotId;
613    for (const auto &info : infos) {
614        if (info.callType == CallType::TYPE_IMS && info.slotId == slotId) {
615            CallReportInfo imsCallReportInfo;
616            if (memset_s(imsCallReportInfo.accountNum, kMaxNumberLen + 1, 0, kMaxNumberLen + 1) != EOK) {
617                TELEPHONY_LOGE("memset_s fail");
618                return TELEPHONY_ERR_MEMSET_FAIL;
619            }
620            if (memcpy_s(imsCallReportInfo.accountNum, kMaxNumberLen, info.phoneNum, kMaxNumberLen) != EOK) {
621                TELEPHONY_LOGE("memcpy_s fail");
622                return TELEPHONY_ERR_MEMCPY_FAIL;
623            }
624            imsCallReportInfo.index = info.index;
625            imsCallReportInfo.accountId = info.slotId;
626            imsCallReportInfo.callType = CallType::TYPE_IMS;
627            imsCallReportInfo.callMode =
628                static_cast<bool>(info.callType) ? VideoStateType::TYPE_VIDEO : VideoStateType::TYPE_VOICE;
629            imsCallReportInfo.state = TelCallState::CALL_STATUS_DISCONNECTED;
630            callsReportInfo.callVec.push_back(imsCallReportInfo);
631        }
632    }
633    if (DelayedSingleton<CellularCallRegister>::GetInstance() == nullptr) {
634        TELEPHONY_LOGE("CellularCallRegister instance is nullptr");
635        return TELEPHONY_ERR_LOCAL_PTR_NULL;
636    }
637    DelayedSingleton<CellularCallRegister>::GetInstance()->ReportCallsInfo(callsReportInfo);
638    return TELEPHONY_SUCCESS;
639}
640
641void IMSControl::DialAfterHold(int32_t slotId)
642{
643    TELEPHONY_LOGI("DialAfterHold entry");
644    std::lock_guard<std::recursive_mutex> lock(connectionMapMutex_);
645    for (auto &connection : connectionMap_) {
646        if (connection.second.IsNeedToDial()) {
647            ImsDialInfoStruct holdToDialInfo = connection.second.GetHoldToDialInfo();
648            CellularCallConnectionIMS cellularCallConnectionIms;
649            cellularCallConnectionIms.DialRequest(slotId, holdToDialInfo);
650            connection.second.SetDialFlag(false);
651            break;
652        }
653    }
654}
655
656void IMSControl::RecoverPendingHold()
657{
658    TELEPHONY_LOGI("RecoverPendingHold entry");
659    std::lock_guard<std::recursive_mutex> lock(connectionMapMutex_);
660    for (auto &connection : connectionMap_) {
661        if (connection.second.IsPendingHold()) {
662            connection.second.UpdatePendingHoldFlag(false);
663            break;
664        }
665    }
666}
667} // namespace Telephony
668} // namespace OHOS
669