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