1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/media/webrtc/signaling/src/callcontrol/CallControlManagerImpl.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,535 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +#include <errno.h> 1.9 +#include <string> 1.10 +#include <prcvar.h> 1.11 +#include <prlock.h> 1.12 + 1.13 +#include "CSFLog.h" 1.14 + 1.15 +#include "CC_SIPCCDevice.h" 1.16 +#include "CC_SIPCCDeviceInfo.h" 1.17 +#include "CC_SIPCCFeatureInfo.h" 1.18 +#include "CC_SIPCCLine.h" 1.19 +#include "CC_SIPCCLineInfo.h" 1.20 +#include "CC_SIPCCCallInfo.h" 1.21 +#include "CallControlManagerImpl.h" 1.22 +#include "csf_common.h" 1.23 + 1.24 +extern "C" 1.25 +{ 1.26 +#include "config_api.h" 1.27 +} 1.28 + 1.29 + 1.30 +static const char logTag[] = "CallControlManager"; 1.31 + 1.32 +using namespace std; 1.33 +using namespace CSFUnified; 1.34 + 1.35 +namespace CSF 1.36 +{ 1.37 + 1.38 + 1.39 +CallControlManagerImpl::CallControlManagerImpl() 1.40 +: m_lock("CallControlManagerImpl"), 1.41 + multiClusterMode(false), 1.42 + sipccLoggingMask(0xFFFFFFFF), 1.43 + authenticationStatus(AuthenticationStatusEnum::eNotAuthenticated), 1.44 + connectionState(ConnectionStatusEnum::eIdle) 1.45 +{ 1.46 + CSFLogInfo(logTag, "CallControlManagerImpl()"); 1.47 +} 1.48 + 1.49 +CallControlManagerImpl::~CallControlManagerImpl() 1.50 +{ 1.51 + CSFLogInfo(logTag, "~CallControlManagerImpl()"); 1.52 + destroy(); 1.53 +} 1.54 + 1.55 +bool CallControlManagerImpl::destroy() 1.56 +{ 1.57 + CSFLogInfo(logTag, "destroy()"); 1.58 + bool retval = disconnect(); 1.59 + if(retval == false) 1.60 + { 1.61 + return retval; 1.62 + } 1.63 + return retval; 1.64 +} 1.65 + 1.66 +// Observers 1.67 +void CallControlManagerImpl::addCCObserver ( CC_Observer * observer ) 1.68 +{ 1.69 + mozilla::MutexAutoLock lock(m_lock); 1.70 + if (observer == nullptr) 1.71 + { 1.72 + CSFLogError(logTag, "NULL value for \"observer\" passed to addCCObserver()."); 1.73 + return; 1.74 + } 1.75 + 1.76 + ccObservers.insert(observer); 1.77 +} 1.78 + 1.79 +void CallControlManagerImpl::removeCCObserver ( CC_Observer * observer ) 1.80 +{ 1.81 + mozilla::MutexAutoLock lock(m_lock); 1.82 + ccObservers.erase(observer); 1.83 +} 1.84 + 1.85 +void CallControlManagerImpl::addECCObserver ( ECC_Observer * observer ) 1.86 +{ 1.87 + mozilla::MutexAutoLock lock(m_lock); 1.88 + if (observer == nullptr) 1.89 + { 1.90 + CSFLogError(logTag, "NULL value for \"observer\" passed to addECCObserver()."); 1.91 + return; 1.92 + } 1.93 + 1.94 + eccObservers.insert(observer); 1.95 +} 1.96 + 1.97 +void CallControlManagerImpl::removeECCObserver ( ECC_Observer * observer ) 1.98 +{ 1.99 + mozilla::MutexAutoLock lock(m_lock); 1.100 + eccObservers.erase(observer); 1.101 +} 1.102 + 1.103 +void CallControlManagerImpl::setMultiClusterMode(bool allowMultipleClusters) 1.104 +{ 1.105 + CSFLogInfo(logTag, "setMultiClusterMode(%s)", 1.106 + allowMultipleClusters ? "TRUE" : "FALSE"); 1.107 + multiClusterMode = allowMultipleClusters; 1.108 +} 1.109 + 1.110 +void CallControlManagerImpl::setSIPCCLoggingMask(const cc_int32_t mask) 1.111 +{ 1.112 + CSFLogInfo(logTag, "setSIPCCLoggingMask(%u)", mask); 1.113 + sipccLoggingMask = mask; 1.114 +} 1.115 + 1.116 +void CallControlManagerImpl::setAuthenticationString(const std::string &authString) 1.117 +{ 1.118 + CSFLogInfo(logTag, "setAuthenticationString()"); 1.119 + this->authString = authString; 1.120 +} 1.121 + 1.122 +void CallControlManagerImpl::setSecureCachePath(const std::string &secureCachePath) 1.123 +{ 1.124 + CSFLogInfo(logTag, "setSecureCachePath(%s)", secureCachePath.c_str()); 1.125 + this->secureCachePath = secureCachePath; 1.126 +} 1.127 + 1.128 +// Add local codecs 1.129 +void CallControlManagerImpl::setAudioCodecs(int codecMask) 1.130 +{ 1.131 + CSFLogDebug(logTag, "setAudioCodecs %X", codecMask); 1.132 + 1.133 + VcmSIPCCBinding::setAudioCodecs(codecMask); 1.134 +} 1.135 + 1.136 +void CallControlManagerImpl::setVideoCodecs(int codecMask) 1.137 +{ 1.138 + CSFLogDebug(logTag, "setVideoCodecs %X", codecMask); 1.139 + 1.140 + VcmSIPCCBinding::setVideoCodecs(codecMask); 1.141 +} 1.142 + 1.143 +AuthenticationStatusEnum::AuthenticationStatus CallControlManagerImpl::getAuthenticationStatus() 1.144 +{ 1.145 + return authenticationStatus; 1.146 +} 1.147 + 1.148 +bool CallControlManagerImpl::registerUser( const std::string& deviceName, const std::string& user, const std::string& password, const std::string& domain ) 1.149 +{ 1.150 + setConnectionState(ConnectionStatusEnum::eRegistering); 1.151 + 1.152 + CSFLogInfo(logTag, "registerUser(%s, %s )", user.c_str(), domain.c_str()); 1.153 + if(phone != nullptr) 1.154 + { 1.155 + setConnectionState(ConnectionStatusEnum::eReady); 1.156 + 1.157 + CSFLogError(logTag, "registerUser() failed - already connected!"); 1.158 + return false; 1.159 + } 1.160 + 1.161 + softPhone = CC_SIPCCServicePtr(new CC_SIPCCService()); 1.162 + phone = softPhone; 1.163 + phone->init(user, password, domain, deviceName); 1.164 + softPhone->setLoggingMask(sipccLoggingMask); 1.165 + phone->addCCObserver(this); 1.166 + 1.167 + phone->setP2PMode(false); 1.168 + 1.169 + bool bStarted = phone->startService(); 1.170 + if (!bStarted) { 1.171 + setConnectionState(ConnectionStatusEnum::eFailed); 1.172 + } else { 1.173 + setConnectionState(ConnectionStatusEnum::eReady); 1.174 + } 1.175 + 1.176 + return bStarted; 1.177 +} 1.178 + 1.179 +bool CallControlManagerImpl::startP2PMode(const std::string& user) 1.180 +{ 1.181 + setConnectionState(ConnectionStatusEnum::eRegistering); 1.182 + 1.183 + CSFLogInfo(logTag, "startP2PMode(%s)", user.c_str()); 1.184 + if(phone != nullptr) 1.185 + { 1.186 + setConnectionState(ConnectionStatusEnum::eReady); 1.187 + 1.188 + CSFLogError(logTag, "startP2PMode() failed - already started in p2p mode!"); 1.189 + return false; 1.190 + } 1.191 + 1.192 + softPhone = CC_SIPCCServicePtr(new CC_SIPCCService()); 1.193 + phone = softPhone; 1.194 + phone->init(user, "", "127.0.0.1", "sipdevice"); 1.195 + softPhone->setLoggingMask(sipccLoggingMask); 1.196 + phone->addCCObserver(this); 1.197 + 1.198 + phone->setP2PMode(true); 1.199 + 1.200 + bool bStarted = phone->startService(); 1.201 + if (!bStarted) { 1.202 + setConnectionState(ConnectionStatusEnum::eFailed); 1.203 + } else { 1.204 + setConnectionState(ConnectionStatusEnum::eReady); 1.205 + } 1.206 + 1.207 + return bStarted; 1.208 +} 1.209 + 1.210 +bool CallControlManagerImpl::startSDPMode() 1.211 +{ 1.212 + CSFLogInfo(logTag, "startSDPMode"); 1.213 + if(phone != nullptr) 1.214 + { 1.215 + CSFLogError(logTag, "%s failed - already started in SDP mode!",__FUNCTION__); 1.216 + return false; 1.217 + } 1.218 + softPhone = CC_SIPCCServicePtr(new CC_SIPCCService()); 1.219 + phone = softPhone; 1.220 + phone->init("JSEP", "", "127.0.0.1", "sipdevice"); 1.221 + softPhone->setLoggingMask(sipccLoggingMask); 1.222 + phone->addCCObserver(this); 1.223 + phone->setSDPMode(true); 1.224 + 1.225 + return phone->startService(); 1.226 +} 1.227 + 1.228 +bool CallControlManagerImpl::disconnect() 1.229 +{ 1.230 + CSFLogInfo(logTag, "disconnect()"); 1.231 + if(phone == nullptr) 1.232 + return true; 1.233 + 1.234 + connectionState = ConnectionStatusEnum::eIdle; 1.235 + phone->removeCCObserver(this); 1.236 + phone->stop(); 1.237 + phone->destroy(); 1.238 + phone = nullptr; 1.239 + softPhone = nullptr; 1.240 + 1.241 + return true; 1.242 +} 1.243 + 1.244 +std::string CallControlManagerImpl::getPreferredDeviceName() 1.245 +{ 1.246 + return preferredDevice; 1.247 +} 1.248 + 1.249 +std::string CallControlManagerImpl::getPreferredLineDN() 1.250 +{ 1.251 + return preferredLineDN; 1.252 +} 1.253 + 1.254 +ConnectionStatusEnum::ConnectionStatus CallControlManagerImpl::getConnectionStatus() 1.255 +{ 1.256 + return connectionState; 1.257 +} 1.258 + 1.259 +std::string CallControlManagerImpl::getCurrentServer() 1.260 +{ 1.261 + return ""; 1.262 +} 1.263 + 1.264 +// Currently controlled device 1.265 +CC_DevicePtr CallControlManagerImpl::getActiveDevice() 1.266 +{ 1.267 + if(phone != nullptr) 1.268 + return phone->getActiveDevice(); 1.269 + 1.270 + return CC_DevicePtr(); 1.271 +} 1.272 + 1.273 +// All known devices 1.274 +PhoneDetailsVtrPtr CallControlManagerImpl::getAvailablePhoneDetails() 1.275 +{ 1.276 + PhoneDetailsVtrPtr result = PhoneDetailsVtrPtr(new PhoneDetailsVtr()); 1.277 + for(PhoneDetailsMap::iterator it = phoneDetailsMap.begin(); it != phoneDetailsMap.end(); it++) 1.278 + { 1.279 + PhoneDetailsPtr details = it->second.get(); 1.280 + result->push_back(details); 1.281 + } 1.282 + return result; 1.283 +} 1.284 + 1.285 +PhoneDetailsPtr CallControlManagerImpl::getAvailablePhoneDetails(const std::string& deviceName) 1.286 +{ 1.287 + PhoneDetailsMap::iterator it = phoneDetailsMap.find(deviceName); 1.288 + if(it != phoneDetailsMap.end()) 1.289 + { 1.290 + return it->second.get(); 1.291 + } 1.292 + return PhoneDetailsPtr(); 1.293 +} 1.294 +// Media setup 1.295 +VideoControlPtr CallControlManagerImpl::getVideoControl() 1.296 +{ 1.297 + if(phone != nullptr) 1.298 + return phone->getVideoControl(); 1.299 + 1.300 + return VideoControlPtr(); 1.301 +} 1.302 + 1.303 +AudioControlPtr CallControlManagerImpl::getAudioControl() 1.304 +{ 1.305 + if(phone != nullptr) 1.306 + return phone->getAudioControl(); 1.307 + 1.308 + return AudioControlPtr(); 1.309 +} 1.310 + 1.311 +bool CallControlManagerImpl::setProperty(ConfigPropertyKeysEnum::ConfigPropertyKeys key, std::string& value) 1.312 +{ 1.313 + unsigned long strtoul_result; 1.314 + char *strtoul_end; 1.315 + 1.316 + CSFLogInfo(logTag, "setProperty( %s )", value.c_str()); 1.317 + 1.318 + if (key == ConfigPropertyKeysEnum::eLocalVoipPort) { 1.319 + errno = 0; 1.320 + strtoul_result = strtoul(value.c_str(), &strtoul_end, 10); 1.321 + 1.322 + if (errno || value.c_str() == strtoul_end || strtoul_result > USHRT_MAX) { 1.323 + return false; 1.324 + } 1.325 + 1.326 + CCAPI_Config_set_local_voip_port((int) strtoul_result); 1.327 + } else if (key == ConfigPropertyKeysEnum::eRemoteVoipPort) { 1.328 + errno = 0; 1.329 + strtoul_result = strtoul(value.c_str(), &strtoul_end, 10); 1.330 + 1.331 + if (errno || value.c_str() == strtoul_end || strtoul_result > USHRT_MAX) { 1.332 + return false; 1.333 + } 1.334 + 1.335 + CCAPI_Config_set_remote_voip_port((int) strtoul_result); 1.336 + } else if (key == ConfigPropertyKeysEnum::eTransport) { 1.337 + if (value == "tcp") 1.338 + CCAPI_Config_set_transport_udp(false); 1.339 + else 1.340 + CCAPI_Config_set_transport_udp(true); 1.341 + } 1.342 + 1.343 + return true; 1.344 +} 1.345 + 1.346 +std::string CallControlManagerImpl::getProperty(ConfigPropertyKeysEnum::ConfigPropertyKeys key) 1.347 +{ 1.348 + std::string retValue = "NONESET"; 1.349 + char tmpString[11]; 1.350 + 1.351 + CSFLogInfo(logTag, "getProperty()"); 1.352 + 1.353 + if (key == ConfigPropertyKeysEnum::eLocalVoipPort) { 1.354 + csf_sprintf(tmpString, sizeof(tmpString), "%u", CCAPI_Config_get_local_voip_port()); 1.355 + retValue = tmpString; 1.356 + } else if (key == ConfigPropertyKeysEnum::eRemoteVoipPort) { 1.357 + csf_sprintf(tmpString, sizeof(tmpString), "%u", CCAPI_Config_get_remote_voip_port()); 1.358 + retValue = tmpString; 1.359 + } else if (key == ConfigPropertyKeysEnum::eVersion) { 1.360 + const char* version = CCAPI_Config_get_version(); 1.361 + retValue = version; 1.362 + } 1.363 + 1.364 + return retValue; 1.365 +} 1.366 +/* 1.367 + There are a number of factors that determine PhoneAvailabilityType::PhoneAvailability. The supported states for this enum are: 1.368 + { eUnknown, eAvailable, eUnAvailable, eNotAllowed }. eUnknown is the default value, which is set when there is no information 1.369 + available that would otherwise determine the availability value. The factors that can influence PhoneAvailability are: 1.370 + phone mode, and for a given device (described by DeviceInfo) the model, and the name of the device. For phone control mode, the 1.371 + device registration and whether CUCM says the device is CTI controllable (or not) is a factor. 1.372 + 1.373 + For Phone Control mode the state machine is: 1.374 + 1.375 + is blacklisted model name? -> Yes -> NOT_ALLOWED 1.376 + (see Note1 below) 1.377 + || 1.378 + \/ 1.379 + No 1.380 + || 1.381 + \/ 1.382 + is CTI Controllable? 1.383 + (determined from CUCM) -> No -> NOT_ALLOWED 1.384 + || 1.385 + \/ 1.386 + Yes 1.387 + || 1.388 + \/ 1.389 + Can we tell if it's registered? -> No -> ?????? TODO: Seems to depends on other factors (look at suggestedAvailability parameter 1.390 + || in DeviceSubProviderImpl.addOrUpdateDevice() in CSF1G Java code. 1.391 + \/ 1.392 + Yes 1.393 + || 1.394 + \/ 1.395 + is Registered? 1.396 + (determined from CUCM) -> No -> NOT_AVAILABLE 1.397 + || 1.398 + \/ 1.399 + Yes 1.400 + || 1.401 + \/ 1.402 + AVAILABLE 1.403 + 1.404 + ======== 1.405 + 1.406 + For Softphone mode the state machine is: 1.407 + 1.408 + is device excluded? 1.409 + (based on "ExcludedDevices" -> Yes -> NOT_ALLOWED 1.410 + config settings 1.411 + (see Note2 below)) 1.412 + || 1.413 + \/ 1.414 + No 1.415 + || 1.416 + \/ 1.417 + isSoftphone? 1.418 + 1.419 + 1.420 + 1.421 + Note1: model name has to match completely, ie it's not a sub-string match, but we are ignoring case. So, if the blacklist 1.422 + contains a string "Cisco Unified Personal Communicator" then the model has to match this completely (but can be a 1.423 + different case) to be a match. In CSF1G the blacklist is hard-wired to: 1.424 + { "Cisco Unified Personal Communicator", 1.425 + "Cisco Unified Client Services Framework", 1.426 + "Client Services Framework", 1.427 + "Client Services Core" } 1.428 + 1.429 + Note2: The "ExcludedDevices" is a comma-separated list of device name prefixes (not model name). Unlike the above, this is 1.430 + a sub-string match, but only a "starts with" sub-string match, not anywhere in the string. If the device name 1.431 + is a complete match then this is also excluded, ie doesn't have to be a sub-string. For example, if the 1.432 + ExcludeDevices list contains { "ECP", "UPC" } then assuming we're in softphone mode, then any device whose 1.433 + name starts with the strings ECP or UPC, or whose complete name is either of these will be deemed to be excluded 1.434 + and will be marked as NOT_ALLOWED straightaway. In Phone Control mode the "ExcludedDevices" list i not taken into 1.435 + account at all in the determination of availability. 1.436 + 1.437 + Note3: isSoftphone() function 1.438 + 1.439 + The config service provides a list of "blacklisted" device name prefixes, that is, if the name of the device starts with a 1.440 + sub-string that matches an entry in the blacklist, then it is straightaway removed from the list? marked as NOT_ALLOWED. 1.441 + */ 1.442 + 1.443 +// CC_Observers 1.444 +void CallControlManagerImpl::onDeviceEvent(ccapi_device_event_e deviceEvent, CC_DevicePtr devicePtr, CC_DeviceInfoPtr info) 1.445 +{ 1.446 + notifyDeviceEventObservers(deviceEvent, devicePtr, info); 1.447 +} 1.448 +void CallControlManagerImpl::onFeatureEvent(ccapi_device_event_e deviceEvent, CC_DevicePtr devicePtr, CC_FeatureInfoPtr info) 1.449 +{ 1.450 + notifyFeatureEventObservers(deviceEvent, devicePtr, info); 1.451 +} 1.452 +void CallControlManagerImpl::onLineEvent(ccapi_line_event_e lineEvent, CC_LinePtr linePtr, CC_LineInfoPtr info) 1.453 +{ 1.454 + notifyLineEventObservers(lineEvent, linePtr, info); 1.455 +} 1.456 +void CallControlManagerImpl::onCallEvent(ccapi_call_event_e callEvent, CC_CallPtr callPtr, CC_CallInfoPtr info) 1.457 +{ 1.458 + notifyCallEventObservers(callEvent, callPtr, info); 1.459 +} 1.460 + 1.461 + 1.462 +void CallControlManagerImpl::notifyDeviceEventObservers (ccapi_device_event_e deviceEvent, CC_DevicePtr devicePtr, CC_DeviceInfoPtr info) 1.463 +{ 1.464 + mozilla::MutexAutoLock lock(m_lock); 1.465 + set<CC_Observer*>::const_iterator it = ccObservers.begin(); 1.466 + for ( ; it != ccObservers.end(); it++ ) 1.467 + { 1.468 + (*it)->onDeviceEvent(deviceEvent, devicePtr, info); 1.469 + } 1.470 +} 1.471 + 1.472 +void CallControlManagerImpl::notifyFeatureEventObservers (ccapi_device_event_e deviceEvent, CC_DevicePtr devicePtr, CC_FeatureInfoPtr info) 1.473 +{ 1.474 + mozilla::MutexAutoLock lock(m_lock); 1.475 + set<CC_Observer*>::const_iterator it = ccObservers.begin(); 1.476 + for ( ; it != ccObservers.end(); it++ ) 1.477 + { 1.478 + (*it)->onFeatureEvent(deviceEvent, devicePtr, info); 1.479 + } 1.480 +} 1.481 + 1.482 +void CallControlManagerImpl::notifyLineEventObservers (ccapi_line_event_e lineEvent, CC_LinePtr linePtr, CC_LineInfoPtr info) 1.483 +{ 1.484 + mozilla::MutexAutoLock lock(m_lock); 1.485 + set<CC_Observer*>::const_iterator it = ccObservers.begin(); 1.486 + for ( ; it != ccObservers.end(); it++ ) 1.487 + { 1.488 + (*it)->onLineEvent(lineEvent, linePtr, info); 1.489 + } 1.490 +} 1.491 + 1.492 +void CallControlManagerImpl::notifyCallEventObservers (ccapi_call_event_e callEvent, CC_CallPtr callPtr, CC_CallInfoPtr info) 1.493 +{ 1.494 + mozilla::MutexAutoLock lock(m_lock); 1.495 + set<CC_Observer*>::const_iterator it = ccObservers.begin(); 1.496 + for ( ; it != ccObservers.end(); it++ ) 1.497 + { 1.498 + (*it)->onCallEvent(callEvent, callPtr, info); 1.499 + } 1.500 +} 1.501 + 1.502 +void CallControlManagerImpl::notifyAvailablePhoneEvent (AvailablePhoneEventType::AvailablePhoneEvent event, 1.503 + const PhoneDetailsPtr availablePhoneDetails) 1.504 +{ 1.505 + mozilla::MutexAutoLock lock(m_lock); 1.506 + set<ECC_Observer*>::const_iterator it = eccObservers.begin(); 1.507 + for ( ; it != eccObservers.end(); it++ ) 1.508 + { 1.509 + (*it)->onAvailablePhoneEvent(event, availablePhoneDetails); 1.510 + } 1.511 +} 1.512 + 1.513 +void CallControlManagerImpl::notifyAuthenticationStatusChange (AuthenticationStatusEnum::AuthenticationStatus status) 1.514 +{ 1.515 + mozilla::MutexAutoLock lock(m_lock); 1.516 + set<ECC_Observer*>::const_iterator it = eccObservers.begin(); 1.517 + for ( ; it != eccObservers.end(); it++ ) 1.518 + { 1.519 + (*it)->onAuthenticationStatusChange(status); 1.520 + } 1.521 +} 1.522 + 1.523 +void CallControlManagerImpl::notifyConnectionStatusChange(ConnectionStatusEnum::ConnectionStatus status) 1.524 +{ 1.525 + mozilla::MutexAutoLock lock(m_lock); 1.526 + set<ECC_Observer*>::const_iterator it = eccObservers.begin(); 1.527 + for ( ; it != eccObservers.end(); it++ ) 1.528 + { 1.529 + (*it)->onConnectionStatusChange(status); 1.530 + } 1.531 +} 1.532 + 1.533 +void CallControlManagerImpl::setConnectionState(ConnectionStatusEnum::ConnectionStatus status) 1.534 +{ 1.535 + connectionState = status; 1.536 + notifyConnectionStatusChange(status); 1.537 +} 1.538 +}