|
1 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
2 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
4 |
|
5 #include <errno.h> |
|
6 #include <string> |
|
7 #include <prcvar.h> |
|
8 #include <prlock.h> |
|
9 |
|
10 #include "CSFLog.h" |
|
11 |
|
12 #include "CC_SIPCCDevice.h" |
|
13 #include "CC_SIPCCDeviceInfo.h" |
|
14 #include "CC_SIPCCFeatureInfo.h" |
|
15 #include "CC_SIPCCLine.h" |
|
16 #include "CC_SIPCCLineInfo.h" |
|
17 #include "CC_SIPCCCallInfo.h" |
|
18 #include "CallControlManagerImpl.h" |
|
19 #include "csf_common.h" |
|
20 |
|
21 extern "C" |
|
22 { |
|
23 #include "config_api.h" |
|
24 } |
|
25 |
|
26 |
|
27 static const char logTag[] = "CallControlManager"; |
|
28 |
|
29 using namespace std; |
|
30 using namespace CSFUnified; |
|
31 |
|
32 namespace CSF |
|
33 { |
|
34 |
|
35 |
|
36 CallControlManagerImpl::CallControlManagerImpl() |
|
37 : m_lock("CallControlManagerImpl"), |
|
38 multiClusterMode(false), |
|
39 sipccLoggingMask(0xFFFFFFFF), |
|
40 authenticationStatus(AuthenticationStatusEnum::eNotAuthenticated), |
|
41 connectionState(ConnectionStatusEnum::eIdle) |
|
42 { |
|
43 CSFLogInfo(logTag, "CallControlManagerImpl()"); |
|
44 } |
|
45 |
|
46 CallControlManagerImpl::~CallControlManagerImpl() |
|
47 { |
|
48 CSFLogInfo(logTag, "~CallControlManagerImpl()"); |
|
49 destroy(); |
|
50 } |
|
51 |
|
52 bool CallControlManagerImpl::destroy() |
|
53 { |
|
54 CSFLogInfo(logTag, "destroy()"); |
|
55 bool retval = disconnect(); |
|
56 if(retval == false) |
|
57 { |
|
58 return retval; |
|
59 } |
|
60 return retval; |
|
61 } |
|
62 |
|
63 // Observers |
|
64 void CallControlManagerImpl::addCCObserver ( CC_Observer * observer ) |
|
65 { |
|
66 mozilla::MutexAutoLock lock(m_lock); |
|
67 if (observer == nullptr) |
|
68 { |
|
69 CSFLogError(logTag, "NULL value for \"observer\" passed to addCCObserver()."); |
|
70 return; |
|
71 } |
|
72 |
|
73 ccObservers.insert(observer); |
|
74 } |
|
75 |
|
76 void CallControlManagerImpl::removeCCObserver ( CC_Observer * observer ) |
|
77 { |
|
78 mozilla::MutexAutoLock lock(m_lock); |
|
79 ccObservers.erase(observer); |
|
80 } |
|
81 |
|
82 void CallControlManagerImpl::addECCObserver ( ECC_Observer * observer ) |
|
83 { |
|
84 mozilla::MutexAutoLock lock(m_lock); |
|
85 if (observer == nullptr) |
|
86 { |
|
87 CSFLogError(logTag, "NULL value for \"observer\" passed to addECCObserver()."); |
|
88 return; |
|
89 } |
|
90 |
|
91 eccObservers.insert(observer); |
|
92 } |
|
93 |
|
94 void CallControlManagerImpl::removeECCObserver ( ECC_Observer * observer ) |
|
95 { |
|
96 mozilla::MutexAutoLock lock(m_lock); |
|
97 eccObservers.erase(observer); |
|
98 } |
|
99 |
|
100 void CallControlManagerImpl::setMultiClusterMode(bool allowMultipleClusters) |
|
101 { |
|
102 CSFLogInfo(logTag, "setMultiClusterMode(%s)", |
|
103 allowMultipleClusters ? "TRUE" : "FALSE"); |
|
104 multiClusterMode = allowMultipleClusters; |
|
105 } |
|
106 |
|
107 void CallControlManagerImpl::setSIPCCLoggingMask(const cc_int32_t mask) |
|
108 { |
|
109 CSFLogInfo(logTag, "setSIPCCLoggingMask(%u)", mask); |
|
110 sipccLoggingMask = mask; |
|
111 } |
|
112 |
|
113 void CallControlManagerImpl::setAuthenticationString(const std::string &authString) |
|
114 { |
|
115 CSFLogInfo(logTag, "setAuthenticationString()"); |
|
116 this->authString = authString; |
|
117 } |
|
118 |
|
119 void CallControlManagerImpl::setSecureCachePath(const std::string &secureCachePath) |
|
120 { |
|
121 CSFLogInfo(logTag, "setSecureCachePath(%s)", secureCachePath.c_str()); |
|
122 this->secureCachePath = secureCachePath; |
|
123 } |
|
124 |
|
125 // Add local codecs |
|
126 void CallControlManagerImpl::setAudioCodecs(int codecMask) |
|
127 { |
|
128 CSFLogDebug(logTag, "setAudioCodecs %X", codecMask); |
|
129 |
|
130 VcmSIPCCBinding::setAudioCodecs(codecMask); |
|
131 } |
|
132 |
|
133 void CallControlManagerImpl::setVideoCodecs(int codecMask) |
|
134 { |
|
135 CSFLogDebug(logTag, "setVideoCodecs %X", codecMask); |
|
136 |
|
137 VcmSIPCCBinding::setVideoCodecs(codecMask); |
|
138 } |
|
139 |
|
140 AuthenticationStatusEnum::AuthenticationStatus CallControlManagerImpl::getAuthenticationStatus() |
|
141 { |
|
142 return authenticationStatus; |
|
143 } |
|
144 |
|
145 bool CallControlManagerImpl::registerUser( const std::string& deviceName, const std::string& user, const std::string& password, const std::string& domain ) |
|
146 { |
|
147 setConnectionState(ConnectionStatusEnum::eRegistering); |
|
148 |
|
149 CSFLogInfo(logTag, "registerUser(%s, %s )", user.c_str(), domain.c_str()); |
|
150 if(phone != nullptr) |
|
151 { |
|
152 setConnectionState(ConnectionStatusEnum::eReady); |
|
153 |
|
154 CSFLogError(logTag, "registerUser() failed - already connected!"); |
|
155 return false; |
|
156 } |
|
157 |
|
158 softPhone = CC_SIPCCServicePtr(new CC_SIPCCService()); |
|
159 phone = softPhone; |
|
160 phone->init(user, password, domain, deviceName); |
|
161 softPhone->setLoggingMask(sipccLoggingMask); |
|
162 phone->addCCObserver(this); |
|
163 |
|
164 phone->setP2PMode(false); |
|
165 |
|
166 bool bStarted = phone->startService(); |
|
167 if (!bStarted) { |
|
168 setConnectionState(ConnectionStatusEnum::eFailed); |
|
169 } else { |
|
170 setConnectionState(ConnectionStatusEnum::eReady); |
|
171 } |
|
172 |
|
173 return bStarted; |
|
174 } |
|
175 |
|
176 bool CallControlManagerImpl::startP2PMode(const std::string& user) |
|
177 { |
|
178 setConnectionState(ConnectionStatusEnum::eRegistering); |
|
179 |
|
180 CSFLogInfo(logTag, "startP2PMode(%s)", user.c_str()); |
|
181 if(phone != nullptr) |
|
182 { |
|
183 setConnectionState(ConnectionStatusEnum::eReady); |
|
184 |
|
185 CSFLogError(logTag, "startP2PMode() failed - already started in p2p mode!"); |
|
186 return false; |
|
187 } |
|
188 |
|
189 softPhone = CC_SIPCCServicePtr(new CC_SIPCCService()); |
|
190 phone = softPhone; |
|
191 phone->init(user, "", "127.0.0.1", "sipdevice"); |
|
192 softPhone->setLoggingMask(sipccLoggingMask); |
|
193 phone->addCCObserver(this); |
|
194 |
|
195 phone->setP2PMode(true); |
|
196 |
|
197 bool bStarted = phone->startService(); |
|
198 if (!bStarted) { |
|
199 setConnectionState(ConnectionStatusEnum::eFailed); |
|
200 } else { |
|
201 setConnectionState(ConnectionStatusEnum::eReady); |
|
202 } |
|
203 |
|
204 return bStarted; |
|
205 } |
|
206 |
|
207 bool CallControlManagerImpl::startSDPMode() |
|
208 { |
|
209 CSFLogInfo(logTag, "startSDPMode"); |
|
210 if(phone != nullptr) |
|
211 { |
|
212 CSFLogError(logTag, "%s failed - already started in SDP mode!",__FUNCTION__); |
|
213 return false; |
|
214 } |
|
215 softPhone = CC_SIPCCServicePtr(new CC_SIPCCService()); |
|
216 phone = softPhone; |
|
217 phone->init("JSEP", "", "127.0.0.1", "sipdevice"); |
|
218 softPhone->setLoggingMask(sipccLoggingMask); |
|
219 phone->addCCObserver(this); |
|
220 phone->setSDPMode(true); |
|
221 |
|
222 return phone->startService(); |
|
223 } |
|
224 |
|
225 bool CallControlManagerImpl::disconnect() |
|
226 { |
|
227 CSFLogInfo(logTag, "disconnect()"); |
|
228 if(phone == nullptr) |
|
229 return true; |
|
230 |
|
231 connectionState = ConnectionStatusEnum::eIdle; |
|
232 phone->removeCCObserver(this); |
|
233 phone->stop(); |
|
234 phone->destroy(); |
|
235 phone = nullptr; |
|
236 softPhone = nullptr; |
|
237 |
|
238 return true; |
|
239 } |
|
240 |
|
241 std::string CallControlManagerImpl::getPreferredDeviceName() |
|
242 { |
|
243 return preferredDevice; |
|
244 } |
|
245 |
|
246 std::string CallControlManagerImpl::getPreferredLineDN() |
|
247 { |
|
248 return preferredLineDN; |
|
249 } |
|
250 |
|
251 ConnectionStatusEnum::ConnectionStatus CallControlManagerImpl::getConnectionStatus() |
|
252 { |
|
253 return connectionState; |
|
254 } |
|
255 |
|
256 std::string CallControlManagerImpl::getCurrentServer() |
|
257 { |
|
258 return ""; |
|
259 } |
|
260 |
|
261 // Currently controlled device |
|
262 CC_DevicePtr CallControlManagerImpl::getActiveDevice() |
|
263 { |
|
264 if(phone != nullptr) |
|
265 return phone->getActiveDevice(); |
|
266 |
|
267 return CC_DevicePtr(); |
|
268 } |
|
269 |
|
270 // All known devices |
|
271 PhoneDetailsVtrPtr CallControlManagerImpl::getAvailablePhoneDetails() |
|
272 { |
|
273 PhoneDetailsVtrPtr result = PhoneDetailsVtrPtr(new PhoneDetailsVtr()); |
|
274 for(PhoneDetailsMap::iterator it = phoneDetailsMap.begin(); it != phoneDetailsMap.end(); it++) |
|
275 { |
|
276 PhoneDetailsPtr details = it->second.get(); |
|
277 result->push_back(details); |
|
278 } |
|
279 return result; |
|
280 } |
|
281 |
|
282 PhoneDetailsPtr CallControlManagerImpl::getAvailablePhoneDetails(const std::string& deviceName) |
|
283 { |
|
284 PhoneDetailsMap::iterator it = phoneDetailsMap.find(deviceName); |
|
285 if(it != phoneDetailsMap.end()) |
|
286 { |
|
287 return it->second.get(); |
|
288 } |
|
289 return PhoneDetailsPtr(); |
|
290 } |
|
291 // Media setup |
|
292 VideoControlPtr CallControlManagerImpl::getVideoControl() |
|
293 { |
|
294 if(phone != nullptr) |
|
295 return phone->getVideoControl(); |
|
296 |
|
297 return VideoControlPtr(); |
|
298 } |
|
299 |
|
300 AudioControlPtr CallControlManagerImpl::getAudioControl() |
|
301 { |
|
302 if(phone != nullptr) |
|
303 return phone->getAudioControl(); |
|
304 |
|
305 return AudioControlPtr(); |
|
306 } |
|
307 |
|
308 bool CallControlManagerImpl::setProperty(ConfigPropertyKeysEnum::ConfigPropertyKeys key, std::string& value) |
|
309 { |
|
310 unsigned long strtoul_result; |
|
311 char *strtoul_end; |
|
312 |
|
313 CSFLogInfo(logTag, "setProperty( %s )", value.c_str()); |
|
314 |
|
315 if (key == ConfigPropertyKeysEnum::eLocalVoipPort) { |
|
316 errno = 0; |
|
317 strtoul_result = strtoul(value.c_str(), &strtoul_end, 10); |
|
318 |
|
319 if (errno || value.c_str() == strtoul_end || strtoul_result > USHRT_MAX) { |
|
320 return false; |
|
321 } |
|
322 |
|
323 CCAPI_Config_set_local_voip_port((int) strtoul_result); |
|
324 } else if (key == ConfigPropertyKeysEnum::eRemoteVoipPort) { |
|
325 errno = 0; |
|
326 strtoul_result = strtoul(value.c_str(), &strtoul_end, 10); |
|
327 |
|
328 if (errno || value.c_str() == strtoul_end || strtoul_result > USHRT_MAX) { |
|
329 return false; |
|
330 } |
|
331 |
|
332 CCAPI_Config_set_remote_voip_port((int) strtoul_result); |
|
333 } else if (key == ConfigPropertyKeysEnum::eTransport) { |
|
334 if (value == "tcp") |
|
335 CCAPI_Config_set_transport_udp(false); |
|
336 else |
|
337 CCAPI_Config_set_transport_udp(true); |
|
338 } |
|
339 |
|
340 return true; |
|
341 } |
|
342 |
|
343 std::string CallControlManagerImpl::getProperty(ConfigPropertyKeysEnum::ConfigPropertyKeys key) |
|
344 { |
|
345 std::string retValue = "NONESET"; |
|
346 char tmpString[11]; |
|
347 |
|
348 CSFLogInfo(logTag, "getProperty()"); |
|
349 |
|
350 if (key == ConfigPropertyKeysEnum::eLocalVoipPort) { |
|
351 csf_sprintf(tmpString, sizeof(tmpString), "%u", CCAPI_Config_get_local_voip_port()); |
|
352 retValue = tmpString; |
|
353 } else if (key == ConfigPropertyKeysEnum::eRemoteVoipPort) { |
|
354 csf_sprintf(tmpString, sizeof(tmpString), "%u", CCAPI_Config_get_remote_voip_port()); |
|
355 retValue = tmpString; |
|
356 } else if (key == ConfigPropertyKeysEnum::eVersion) { |
|
357 const char* version = CCAPI_Config_get_version(); |
|
358 retValue = version; |
|
359 } |
|
360 |
|
361 return retValue; |
|
362 } |
|
363 /* |
|
364 There are a number of factors that determine PhoneAvailabilityType::PhoneAvailability. The supported states for this enum are: |
|
365 { eUnknown, eAvailable, eUnAvailable, eNotAllowed }. eUnknown is the default value, which is set when there is no information |
|
366 available that would otherwise determine the availability value. The factors that can influence PhoneAvailability are: |
|
367 phone mode, and for a given device (described by DeviceInfo) the model, and the name of the device. For phone control mode, the |
|
368 device registration and whether CUCM says the device is CTI controllable (or not) is a factor. |
|
369 |
|
370 For Phone Control mode the state machine is: |
|
371 |
|
372 is blacklisted model name? -> Yes -> NOT_ALLOWED |
|
373 (see Note1 below) |
|
374 || |
|
375 \/ |
|
376 No |
|
377 || |
|
378 \/ |
|
379 is CTI Controllable? |
|
380 (determined from CUCM) -> No -> NOT_ALLOWED |
|
381 || |
|
382 \/ |
|
383 Yes |
|
384 || |
|
385 \/ |
|
386 Can we tell if it's registered? -> No -> ?????? TODO: Seems to depends on other factors (look at suggestedAvailability parameter |
|
387 || in DeviceSubProviderImpl.addOrUpdateDevice() in CSF1G Java code. |
|
388 \/ |
|
389 Yes |
|
390 || |
|
391 \/ |
|
392 is Registered? |
|
393 (determined from CUCM) -> No -> NOT_AVAILABLE |
|
394 || |
|
395 \/ |
|
396 Yes |
|
397 || |
|
398 \/ |
|
399 AVAILABLE |
|
400 |
|
401 ======== |
|
402 |
|
403 For Softphone mode the state machine is: |
|
404 |
|
405 is device excluded? |
|
406 (based on "ExcludedDevices" -> Yes -> NOT_ALLOWED |
|
407 config settings |
|
408 (see Note2 below)) |
|
409 || |
|
410 \/ |
|
411 No |
|
412 || |
|
413 \/ |
|
414 isSoftphone? |
|
415 |
|
416 |
|
417 |
|
418 Note1: model name has to match completely, ie it's not a sub-string match, but we are ignoring case. So, if the blacklist |
|
419 contains a string "Cisco Unified Personal Communicator" then the model has to match this completely (but can be a |
|
420 different case) to be a match. In CSF1G the blacklist is hard-wired to: |
|
421 { "Cisco Unified Personal Communicator", |
|
422 "Cisco Unified Client Services Framework", |
|
423 "Client Services Framework", |
|
424 "Client Services Core" } |
|
425 |
|
426 Note2: The "ExcludedDevices" is a comma-separated list of device name prefixes (not model name). Unlike the above, this is |
|
427 a sub-string match, but only a "starts with" sub-string match, not anywhere in the string. If the device name |
|
428 is a complete match then this is also excluded, ie doesn't have to be a sub-string. For example, if the |
|
429 ExcludeDevices list contains { "ECP", "UPC" } then assuming we're in softphone mode, then any device whose |
|
430 name starts with the strings ECP or UPC, or whose complete name is either of these will be deemed to be excluded |
|
431 and will be marked as NOT_ALLOWED straightaway. In Phone Control mode the "ExcludedDevices" list i not taken into |
|
432 account at all in the determination of availability. |
|
433 |
|
434 Note3: isSoftphone() function |
|
435 |
|
436 The config service provides a list of "blacklisted" device name prefixes, that is, if the name of the device starts with a |
|
437 sub-string that matches an entry in the blacklist, then it is straightaway removed from the list? marked as NOT_ALLOWED. |
|
438 */ |
|
439 |
|
440 // CC_Observers |
|
441 void CallControlManagerImpl::onDeviceEvent(ccapi_device_event_e deviceEvent, CC_DevicePtr devicePtr, CC_DeviceInfoPtr info) |
|
442 { |
|
443 notifyDeviceEventObservers(deviceEvent, devicePtr, info); |
|
444 } |
|
445 void CallControlManagerImpl::onFeatureEvent(ccapi_device_event_e deviceEvent, CC_DevicePtr devicePtr, CC_FeatureInfoPtr info) |
|
446 { |
|
447 notifyFeatureEventObservers(deviceEvent, devicePtr, info); |
|
448 } |
|
449 void CallControlManagerImpl::onLineEvent(ccapi_line_event_e lineEvent, CC_LinePtr linePtr, CC_LineInfoPtr info) |
|
450 { |
|
451 notifyLineEventObservers(lineEvent, linePtr, info); |
|
452 } |
|
453 void CallControlManagerImpl::onCallEvent(ccapi_call_event_e callEvent, CC_CallPtr callPtr, CC_CallInfoPtr info) |
|
454 { |
|
455 notifyCallEventObservers(callEvent, callPtr, info); |
|
456 } |
|
457 |
|
458 |
|
459 void CallControlManagerImpl::notifyDeviceEventObservers (ccapi_device_event_e deviceEvent, CC_DevicePtr devicePtr, CC_DeviceInfoPtr info) |
|
460 { |
|
461 mozilla::MutexAutoLock lock(m_lock); |
|
462 set<CC_Observer*>::const_iterator it = ccObservers.begin(); |
|
463 for ( ; it != ccObservers.end(); it++ ) |
|
464 { |
|
465 (*it)->onDeviceEvent(deviceEvent, devicePtr, info); |
|
466 } |
|
467 } |
|
468 |
|
469 void CallControlManagerImpl::notifyFeatureEventObservers (ccapi_device_event_e deviceEvent, CC_DevicePtr devicePtr, CC_FeatureInfoPtr info) |
|
470 { |
|
471 mozilla::MutexAutoLock lock(m_lock); |
|
472 set<CC_Observer*>::const_iterator it = ccObservers.begin(); |
|
473 for ( ; it != ccObservers.end(); it++ ) |
|
474 { |
|
475 (*it)->onFeatureEvent(deviceEvent, devicePtr, info); |
|
476 } |
|
477 } |
|
478 |
|
479 void CallControlManagerImpl::notifyLineEventObservers (ccapi_line_event_e lineEvent, CC_LinePtr linePtr, CC_LineInfoPtr info) |
|
480 { |
|
481 mozilla::MutexAutoLock lock(m_lock); |
|
482 set<CC_Observer*>::const_iterator it = ccObservers.begin(); |
|
483 for ( ; it != ccObservers.end(); it++ ) |
|
484 { |
|
485 (*it)->onLineEvent(lineEvent, linePtr, info); |
|
486 } |
|
487 } |
|
488 |
|
489 void CallControlManagerImpl::notifyCallEventObservers (ccapi_call_event_e callEvent, CC_CallPtr callPtr, CC_CallInfoPtr info) |
|
490 { |
|
491 mozilla::MutexAutoLock lock(m_lock); |
|
492 set<CC_Observer*>::const_iterator it = ccObservers.begin(); |
|
493 for ( ; it != ccObservers.end(); it++ ) |
|
494 { |
|
495 (*it)->onCallEvent(callEvent, callPtr, info); |
|
496 } |
|
497 } |
|
498 |
|
499 void CallControlManagerImpl::notifyAvailablePhoneEvent (AvailablePhoneEventType::AvailablePhoneEvent event, |
|
500 const PhoneDetailsPtr availablePhoneDetails) |
|
501 { |
|
502 mozilla::MutexAutoLock lock(m_lock); |
|
503 set<ECC_Observer*>::const_iterator it = eccObservers.begin(); |
|
504 for ( ; it != eccObservers.end(); it++ ) |
|
505 { |
|
506 (*it)->onAvailablePhoneEvent(event, availablePhoneDetails); |
|
507 } |
|
508 } |
|
509 |
|
510 void CallControlManagerImpl::notifyAuthenticationStatusChange (AuthenticationStatusEnum::AuthenticationStatus status) |
|
511 { |
|
512 mozilla::MutexAutoLock lock(m_lock); |
|
513 set<ECC_Observer*>::const_iterator it = eccObservers.begin(); |
|
514 for ( ; it != eccObservers.end(); it++ ) |
|
515 { |
|
516 (*it)->onAuthenticationStatusChange(status); |
|
517 } |
|
518 } |
|
519 |
|
520 void CallControlManagerImpl::notifyConnectionStatusChange(ConnectionStatusEnum::ConnectionStatus status) |
|
521 { |
|
522 mozilla::MutexAutoLock lock(m_lock); |
|
523 set<ECC_Observer*>::const_iterator it = eccObservers.begin(); |
|
524 for ( ; it != eccObservers.end(); it++ ) |
|
525 { |
|
526 (*it)->onConnectionStatusChange(status); |
|
527 } |
|
528 } |
|
529 |
|
530 void CallControlManagerImpl::setConnectionState(ConnectionStatusEnum::ConnectionStatus status) |
|
531 { |
|
532 connectionState = status; |
|
533 notifyConnectionStatusChange(status); |
|
534 } |
|
535 } |