• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 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 "securec.h"
19 
20 #include "module_service_utils.h"
21 #include "cellular_call_register.h"
22 #include "standardize_utils.h"
23 #include "emergency_utils.h"
24 
25 namespace OHOS {
26 namespace Telephony {
~IMSControl()27 IMSControl::~IMSControl()
28 {
29     TELEPHONY_LOGI("~IMSControl start");
30     ReleaseAllConnection();
31 }
32 
Dial(const CellularCallInfo & callInfo)33 int32_t IMSControl::Dial(const CellularCallInfo &callInfo)
34 {
35     TELEPHONY_LOGI("Dial start");
36     int32_t ret = DialPreJudgment(callInfo);
37     if (ret != TELEPHONY_SUCCESS) {
38         return ret;
39     }
40 
41     // sip uri needs to remove separator
42     std::string newPhoneNum(callInfo.phoneNum);
43     StandardizeUtils standardizeUtils;
44     if (newPhoneNum.find('@') != std::string::npos || newPhoneNum.find("%40") != std::string::npos) {
45         newPhoneNum = standardizeUtils.RemoveSeparatorsPhoneNumber(newPhoneNum);
46     }
47 
48     CLIRMode clirMode = CLIRMode::DEFAULT;
49     if (IsNeedExecuteMMI(callInfo.slotId, newPhoneNum, clirMode)) {
50         TELEPHONY_LOGI("Dial return, mmi code type.");
51         return RETURN_TYPE_MMI;
52     }
53     return DialJudgment(callInfo.slotId, newPhoneNum, clirMode, callInfo.videoState);
54 }
55 
DialJudgment(int32_t slotId,const std::string & phoneNum,CLIRMode & clirMode,int32_t videoState)56 int32_t IMSControl::DialJudgment(int32_t slotId, const std::string &phoneNum, CLIRMode &clirMode, int32_t videoState)
57 {
58     TELEPHONY_LOGI("DialJudgment entry.");
59     if (!CanCall(connectionMap_)) {
60         TELEPHONY_LOGE("DialJudgment return, error type: call state error.");
61         return CALL_ERR_CALL_COUNTS_EXCEED_LIMIT;
62     }
63 
64     // Calls can be put on hold, recovered, released, added to conversation,
65     // and transferred similarly as defined in 3GPP TS 22.030 [19].
66     if (IsInState(connectionMap_, TelCallState::CALL_STATUS_ACTIVE)) {
67         // New calls must be active, so other calls need to be hold
68         TELEPHONY_LOGI("DialJudgment, have connection in active state.");
69         CellularCallConnectionIMS connection;
70         // - a call can be temporarily disconnected from the ME but the connection is retained by the network
71         connection.HoldCallRequest(slotId);
72     }
73     return EncapsulateDial(slotId, phoneNum, clirMode, videoState);
74 }
75 
EncapsulateDial(int32_t slotId,const std::string & phoneNum,CLIRMode & clirMode,int32_t videoState) const76 int32_t IMSControl::EncapsulateDial(
77     int32_t slotId, const std::string &phoneNum, CLIRMode &clirMode, int32_t videoState) const
78 {
79     TELEPHONY_LOGI("EncapsulateDial start");
80 
81     ImsDialInfoStruct dialInfo;
82     dialInfo.videoState = videoState;
83     EmergencyUtils emergencyUtils;
84     dialInfo.bEmergencyCall = emergencyUtils.IsEmergencyCall(slotId, phoneNum);
85 
86     /**
87      * <idx>: integer type;
88      * call identification number as described in 3GPP TS 22.030 [19] subclause 4.5.5.1
89      * this number can be used in +CHLD command operations
90      * <dir>:
91      */
92     dialInfo.phoneNum = phoneNum;
93     /**
94      * <n> (parameter sets the adjustment for outgoing calls):
95      *  0	presentation indicator is used according to the subscription of the CLIR service
96      *  1	CLIR invocation
97      *  2	CLIR suppression
98      */
99     dialInfo.clirMode = clirMode;
100     /**
101      * An example of voice group call service request usage:
102      * ATD*17*753#500; (originate voice group call with the priority level 3)
103      * OK (voice group call setup was successful)
104      */
105 
106     CellularCallConnectionIMS cellularCallConnectionIms;
107     return cellularCallConnectionIms.DialRequest(slotId, dialInfo);
108 }
109 
HangUp(const CellularCallInfo & callInfo,CallSupplementType type)110 int32_t IMSControl::HangUp(const CellularCallInfo &callInfo, CallSupplementType type)
111 {
112     TELEPHONY_LOGI("HangUp start");
113     switch (type) {
114         case CallSupplementType::TYPE_DEFAULT: {
115             auto pConnection =
116                 GetConnectionData<ImsConnectionMap &, CellularCallConnectionIMS *>(connectionMap_, callInfo.phoneNum);
117             if (pConnection == nullptr) {
118                 TELEPHONY_LOGI("HangUp: connection cannot be matched, use index directly");
119                 pConnection = FindConnectionByIndex<ImsConnectionMap &, CellularCallConnectionIMS *>(
120                     connectionMap_, callInfo.index);
121             }
122             if (pConnection == nullptr) {
123                 TELEPHONY_LOGE("HangUp return, error type: connection is null");
124                 return CALL_ERR_CALL_CONNECTION_NOT_EXIST;
125             }
126 
127             if (DelayedSingleton<CellularCallRegister>::GetInstance() != nullptr) {
128                 DelayedSingleton<CellularCallRegister>::GetInstance()->ReportSingleCallInfo(
129                     pConnection->GetCallReportInfo(), TelCallState::CALL_STATUS_DISCONNECTING);
130             }
131             return pConnection->HangUpRequest(callInfo.slotId, callInfo.phoneNum, callInfo.index);
132         }
133         case CallSupplementType::TYPE_HANG_UP_HOLD_WAIT:
134             // release the second (active) call and recover the first (held) call
135         case CallSupplementType::TYPE_HANG_UP_ACTIVE: {
136             CellularCallConnectionIMS connection;
137             return connection.CallSupplementRequest(callInfo.slotId, type);
138         }
139         case CallSupplementType::TYPE_HANG_UP_ALL: {
140             TELEPHONY_LOGI("HangUp, hang up all call");
141             CellularCallConnectionIMS connection;
142             // The AT command for hanging up all calls is the same as the AT command for rejecting calls,
143             // so the reject interface is reused.
144             return connection.RejectRequest(callInfo.slotId, callInfo.phoneNum, callInfo.index);
145         }
146         default: {
147             TELEPHONY_LOGE("HangUp warring, type is invalid");
148             return TELEPHONY_ERR_ARGUMENT_INVALID;
149         }
150     }
151 }
152 
Answer(const CellularCallInfo & callInfo)153 int32_t IMSControl::Answer(const CellularCallInfo &callInfo)
154 {
155     TELEPHONY_LOGI("IMSControl::Answer start");
156     auto pConnection =
157         GetConnectionData<ImsConnectionMap &, CellularCallConnectionIMS *>(connectionMap_, callInfo.phoneNum);
158     if (pConnection == nullptr) {
159         TELEPHONY_LOGE("IMSControl::Answer, error type: connection is null");
160         return CALL_ERR_CALL_CONNECTION_NOT_EXIST;
161     }
162     if (IsInState(connectionMap_, TelCallState::CALL_STATUS_ACTIVE) &&
163         pConnection->GetStatus() == TelCallState::CALL_STATUS_WAITING) {
164         TELEPHONY_LOGI("Answer there is an active call when you call, or third party call waiting");
165         auto con = FindConnectionByState<ImsConnectionMap &, CellularCallConnectionIMS *>(
166             connectionMap_, TelCallState::CALL_STATUS_ACTIVE);
167         if (con == nullptr) {
168             TELEPHONY_LOGE("Answer return, error type: con is null, there are no active calls");
169             return CALL_ERR_CALL_CONNECTION_NOT_EXIST;
170         }
171         return con->SwitchCallRequest(callInfo.slotId);
172     }
173     if (pConnection->GetStatus() == TelCallState::CALL_STATUS_ALERTING ||
174         pConnection->GetStatus() == TelCallState::CALL_STATUS_INCOMING ||
175         pConnection->GetStatus() == TelCallState::CALL_STATUS_WAITING) {
176         return pConnection->AnswerRequest(callInfo.slotId, callInfo.phoneNum, callInfo.videoState, callInfo.index);
177     }
178     TELEPHONY_LOGE("IMSControl::Answer return, error type: call state error, phone not ringing.");
179     return CALL_ERR_CALL_STATE;
180 }
181 
Reject(const CellularCallInfo & callInfo)182 int32_t IMSControl::Reject(const CellularCallInfo &callInfo)
183 {
184     TELEPHONY_LOGI("IMSControl::Reject start");
185     auto pConnection =
186         GetConnectionData<ImsConnectionMap &, CellularCallConnectionIMS *>(connectionMap_, callInfo.phoneNum);
187     if (pConnection == nullptr) {
188         TELEPHONY_LOGI("Reject: connection cannot be matched, use index directly");
189         pConnection =
190             FindConnectionByIndex<ImsConnectionMap &, CellularCallConnectionIMS *>(connectionMap_, callInfo.index);
191     }
192     if (pConnection == nullptr) {
193         TELEPHONY_LOGE("IMSControl::Reject, error type: connection is null");
194         return CALL_ERR_CALL_CONNECTION_NOT_EXIST;
195     }
196     if (!pConnection->IsRingingState()) {
197         TELEPHONY_LOGE("IMSControl::Reject return, error type: call state error, phone not ringing.");
198         return CALL_ERR_CALL_STATE;
199     }
200     if (DelayedSingleton<CellularCallRegister>::GetInstance() != nullptr) {
201         DelayedSingleton<CellularCallRegister>::GetInstance()->ReportSingleCallInfo(
202             pConnection->GetCallReportInfo(), TelCallState::CALL_STATUS_DISCONNECTING);
203     }
204     return pConnection->RejectRequest(callInfo.slotId, callInfo.phoneNum, callInfo.index);
205 }
206 
HoldCall(int32_t slotId)207 int32_t IMSControl::HoldCall(int32_t slotId)
208 {
209     TELEPHONY_LOGI("HoldCall start");
210     if (IsInState(connectionMap_, TelCallState::CALL_STATUS_INCOMING)) {
211         TELEPHONY_LOGE("HoldCall return, error type: call state error.");
212         return CALL_ERR_CALL_STATE;
213     }
214     CellularCallConnectionIMS cellularCallConnectionIms;
215     return cellularCallConnectionIms.HoldCallRequest(slotId);
216 }
217 
UnHoldCall(int32_t slotId)218 int32_t IMSControl::UnHoldCall(int32_t slotId)
219 {
220     TELEPHONY_LOGI("UnHoldCall start");
221     CellularCallConnectionIMS cellularCallConnectionIms;
222     return cellularCallConnectionIms.UnHoldCallRequest(slotId);
223 }
224 
SwitchCall(int32_t slotId)225 int32_t IMSControl::SwitchCall(int32_t slotId)
226 {
227     TELEPHONY_LOGI("SwitchCall start");
228     CellularCallConnectionIMS cellularCallConnectionIms;
229     return cellularCallConnectionIms.SwitchCallRequest(slotId);
230 }
231 
232 /**
233  * Add another remote party, to which a private communication has been established using
234  * the same procedures as in Section 1.3.8.1, if the number of remote parties does not then
235  * exceed the maximum number allowed, which results in an active multiParty call.
236  */
CombineConference(int32_t slotId)237 int32_t IMSControl::CombineConference(int32_t slotId)
238 {
239     TELEPHONY_LOGI("CombineConference entry");
240     CellularCallConnectionIMS connection;
241     int32_t voiceCall = 0;
242     return connection.CombineConferenceRequest(slotId, voiceCall);
243 }
244 
HangUpAllConnection(int32_t slotId)245 int32_t IMSControl::HangUpAllConnection(int32_t slotId)
246 {
247     TELEPHONY_LOGI("HangUpAllConnection entry");
248     CellularCallConnectionIMS connection;
249     int32_t index = connectionMap_.begin()->second.GetIndex();
250     // The AT command for hanging up all calls is the same as the AT command for rejecting calls,
251     // so the reject interface is reused.
252     return connection.RejectRequest(slotId, connectionMap_.begin()->first, index);
253 }
254 
InviteToConference(int32_t slotId,const std::vector<std::string> & numberList)255 int32_t IMSControl::InviteToConference(int32_t slotId, const std::vector<std::string> &numberList)
256 {
257     TELEPHONY_LOGI("InviteToConference entry");
258     CellularCallConnectionIMS connection;
259     return connection.InviteToConferenceRequest(slotId, numberList);
260 }
261 
KickOutFromConference(int32_t slotId,const std::vector<std::string> & numberList)262 int32_t IMSControl::KickOutFromConference(int32_t slotId, const std::vector<std::string> &numberList)
263 {
264     TELEPHONY_LOGI("KickOutFromConference entry");
265     CellularCallConnectionIMS connection;
266     return connection.KickOutFromConferenceRequest(slotId, numberList);
267 }
268 
UpdateImsCallMode(const CellularCallInfo & callInfo,ImsCallMode mode)269 int32_t IMSControl::UpdateImsCallMode(const CellularCallInfo &callInfo, ImsCallMode mode)
270 {
271     TELEPHONY_LOGI("UpdateImsCallMode entry");
272     auto pConnection =
273         GetConnectionData<ImsConnectionMap &, CellularCallConnectionIMS *>(connectionMap_, callInfo.phoneNum);
274     if (pConnection == nullptr) {
275         TELEPHONY_LOGI("UpdateImsCallMode: connection cannot be matched, use index directly");
276         pConnection =
277             FindConnectionByIndex<ImsConnectionMap &, CellularCallConnectionIMS *>(connectionMap_, callInfo.index);
278     }
279     if (pConnection == nullptr) {
280         TELEPHONY_LOGE("IMSControl::UpdateImsCallMode, error type: connection is null");
281         return CALL_ERR_CALL_CONNECTION_NOT_EXIST;
282     }
283     bool bContinue = pConnection->GetStatus() == TelCallState::CALL_STATUS_ALERTING ||
284         pConnection->GetStatus() == TelCallState::CALL_STATUS_ACTIVE;
285     if (!bContinue) {
286         TELEPHONY_LOGE("IMSControl::UpdateImsCallMode return, error type: call state error.");
287         return CALL_ERR_CALL_STATE;
288     }
289     return pConnection->UpdateCallMediaModeRequest(callInfo, mode);
290 }
291 
StartRtt(int32_t slotId,const std::string & msg)292 int32_t IMSControl::StartRtt(int32_t slotId, const std::string &msg)
293 {
294     TELEPHONY_LOGI("StartRtt entry");
295     CellularCallConnectionIMS connection;
296     return connection.StartRttRequest(slotId, msg);
297 }
298 
StopRtt(int32_t slotId)299 int32_t IMSControl::StopRtt(int32_t slotId)
300 {
301     TELEPHONY_LOGI("StopRtt entry");
302     CellularCallConnectionIMS connection;
303     return connection.StopRttRequest(slotId);
304 }
305 
ReleaseAllConnection()306 void IMSControl::ReleaseAllConnection()
307 {
308     connectionMap_.clear();
309 }
310 
GetConnectionMap()311 ImsConnectionMap IMSControl::GetConnectionMap()
312 {
313     return connectionMap_;
314 }
315 
ReportCallsData(int32_t slotId,const CallInfoList & callInfoList)316 int32_t IMSControl::ReportCallsData(int32_t slotId, const CallInfoList &callInfoList)
317 {
318     if (callInfoList.callSize <= 0 && !connectionMap_.empty()) {
319         return ReportHungUpInfo(slotId);
320     } else if (callInfoList.callSize > 0 && connectionMap_.empty()) {
321         return ReportIncomingInfo(slotId, callInfoList);
322     } else if (callInfoList.callSize > 0 && !connectionMap_.empty()) {
323         return ReportUpdateInfo(slotId, callInfoList);
324     }
325     return TELEPHONY_ERROR;
326 }
327 
ReportHungUpInfo(int32_t slotId)328 int32_t IMSControl::ReportHungUpInfo(int32_t slotId)
329 {
330     TELEPHONY_LOGI("ReportHungUpInfo entry");
331     CallsReportInfo callsReportInfo;
332     for (auto &it : connectionMap_) {
333         CallReportInfo reportInfo = it.second.GetCallReportInfo();
334         reportInfo.state = TelCallState::CALL_STATUS_DISCONNECTED;
335         reportInfo.accountId = slotId;
336         callsReportInfo.callVec.push_back(reportInfo);
337         GetCallFailReason(slotId, connectionMap_);
338     }
339     if (DelayedSingleton<CellularCallRegister>::GetInstance() == nullptr) {
340         TELEPHONY_LOGE("ReportHungUpInfo return, GetInstance() is nullptr.");
341         return TELEPHONY_ERR_LOCAL_PTR_NULL;
342     }
343     callsReportInfo.slotId = slotId;
344     DelayedSingleton<CellularCallRegister>::GetInstance()->ReportCallsInfo(callsReportInfo);
345     ReleaseAllConnection();
346     return TELEPHONY_SUCCESS;
347 }
348 
ReportIncomingInfo(int32_t slotId,const CallInfoList & callInfoList)349 int32_t IMSControl::ReportIncomingInfo(int32_t slotId, const CallInfoList &callInfoList)
350 {
351     TELEPHONY_LOGI("ReportIncomingInfo entry");
352     CallsReportInfo callsReportInfo;
353     for (int32_t i = 0; i < callInfoList.callSize; ++i) {
354         CallReportInfo reportInfo = EncapsulationCallReportInfo(slotId, callInfoList.calls[i]);
355 
356         CellularCallConnectionIMS connection;
357         connection.SetStatus(static_cast<TelCallState>(callInfoList.calls[i].state));
358         connection.SetIndex(callInfoList.calls[i].index);
359         connection.SetOrUpdateCallReportInfo(reportInfo);
360         SetConnectionData(connectionMap_, callInfoList.calls[i].number, connection);
361 
362         callsReportInfo.callVec.push_back(reportInfo);
363     }
364     if (DelayedSingleton<CellularCallRegister>::GetInstance() == nullptr) {
365         TELEPHONY_LOGE("ReportIncomingInfo return, GetInstance() is nullptr.");
366         return TELEPHONY_ERR_ARGUMENT_INVALID;
367     }
368     callsReportInfo.slotId = slotId;
369     DelayedSingleton<CellularCallRegister>::GetInstance()->ReportCallsInfo(callsReportInfo);
370     return TELEPHONY_SUCCESS;
371 }
372 
ReportUpdateInfo(int32_t slotId,const CallInfoList & callInfoList)373 int32_t IMSControl::ReportUpdateInfo(int32_t slotId, const CallInfoList &callInfoList)
374 {
375     TELEPHONY_LOGI("ReportUpdateInfo entry");
376     CallsReportInfo callsReportInfo;
377     for (int32_t i = 0; i < callInfoList.callSize; ++i) {
378         CallReportInfo reportInfo = EncapsulationCallReportInfo(slotId, callInfoList.calls[i]);
379 
380         auto pConnection = GetConnectionData<ImsConnectionMap &, CellularCallConnectionIMS *>(
381             connectionMap_, callInfoList.calls[i].number);
382         if (pConnection == nullptr) {
383             CellularCallConnectionIMS connection;
384             connection.SetOrUpdateCallReportInfo(reportInfo);
385             connection.SetFlag(true);
386             connection.SetIndex(callInfoList.calls[i].index);
387             SetConnectionData(connectionMap_, callInfoList.calls[i].number, connection);
388         } else {
389             pConnection->SetFlag(true);
390             pConnection->SetIndex(callInfoList.calls[i].index);
391             pConnection->SetOrUpdateCallReportInfo(reportInfo);
392         }
393         callsReportInfo.callVec.push_back(reportInfo);
394     }
395     callsReportInfo.slotId = slotId;
396     DeleteConnection(callsReportInfo, callInfoList);
397     if (DelayedSingleton<CellularCallRegister>::GetInstance() == nullptr) {
398         TELEPHONY_LOGE("ReportUpdateInfo return, GetInstance() is nullptr.");
399         return TELEPHONY_ERR_LOCAL_PTR_NULL;
400     }
401     DelayedSingleton<CellularCallRegister>::GetInstance()->ReportCallsInfo(callsReportInfo);
402     return TELEPHONY_SUCCESS;
403 }
404 
EncapsulationCallReportInfo(int32_t slotId,const CallInfo & callInfo)405 CallReportInfo IMSControl::EncapsulationCallReportInfo(int32_t slotId, const CallInfo &callInfo)
406 {
407     TELEPHONY_LOGI("EncapsulationCallReportInfo entry");
408     CallReportInfo callReportInfo;
409     if (memset_s(&callReportInfo, sizeof(callReportInfo), 0, sizeof(callReportInfo)) != EOK) {
410         TELEPHONY_LOGE("EncapsulationCallReportInfo return, memset_s fail.");
411         return callReportInfo;
412     }
413 
414     size_t cpyLen = strlen(callInfo.number.c_str()) + 1;
415     if (strcpy_s(callReportInfo.accountNum, cpyLen, callInfo.number.c_str()) != EOK) {
416         TELEPHONY_LOGE("EncapsulationCallReportInfo return, strcpy_s fail.");
417         return callReportInfo;
418     }
419     callReportInfo.index = callInfo.index;
420     callReportInfo.accountId = slotId;
421     callReportInfo.state = static_cast<TelCallState>(callInfo.state);
422     callReportInfo.voiceDomain = callInfo.voiceDomain;
423     callReportInfo.callType = CallType::TYPE_IMS;
424     callReportInfo.callMode = callInfo.callType ? VideoStateType::TYPE_VIDEO : VideoStateType::TYPE_VOICE;
425     return callReportInfo;
426 }
427 
DeleteConnection(CallsReportInfo & callsReportInfo,const CallInfoList & callInfoList)428 void IMSControl::DeleteConnection(CallsReportInfo &callsReportInfo, const CallInfoList &callInfoList)
429 {
430     TELEPHONY_LOGI("DeleteConnection entry");
431     auto it = connectionMap_.begin();
432     while (it != connectionMap_.end()) {
433         if (!it->second.GetFlag()) {
434             CallReportInfo callReportInfo = it->second.GetCallReportInfo();
435             callReportInfo.state = TelCallState::CALL_STATUS_DISCONNECTED;
436             callsReportInfo.callVec.push_back(callReportInfo);
437             connectionMap_.erase(it++);
438             GetCallFailReason(callsReportInfo.slotId, connectionMap_);
439         } else {
440             it->second.SetFlag(false);
441             ++it;
442         }
443     }
444 }
445 } // namespace Telephony
446 } // namespace OHOS