PageRenderTime 116ms CodeModel.GetById 24ms app.highlight 82ms RepoModel.GetById 1ms app.codeStats 1ms

/indra/newview/llvoicechannel.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 968 lines | 711 code | 115 blank | 142 comment | 104 complexity | a71ab9ff14047e4fb01df7588205ac0e MD5 | raw file
  1/** 
  2 * @file llvoicechannel.cpp
  3 * @brief Voice Channel related classes
  4 *
  5 * $LicenseInfo:firstyear=2001&license=viewerlgpl$
  6 * Second Life Viewer Source Code
  7 * Copyright (C) 2010, Linden Research, Inc.
  8 * 
  9 * This library is free software; you can redistribute it and/or
 10 * modify it under the terms of the GNU Lesser General Public
 11 * License as published by the Free Software Foundation;
 12 * version 2.1 of the License only.
 13 * 
 14 * This library is distributed in the hope that it will be useful,
 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 17 * Lesser General Public License for more details.
 18 * 
 19 * You should have received a copy of the GNU Lesser General Public
 20 * License along with this library; if not, write to the Free Software
 21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 22 * 
 23 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 24 * $/LicenseInfo$
 25 */
 26
 27#include "llviewerprecompiledheaders.h"
 28
 29#include "llagent.h"
 30#include "llfloaterreg.h"
 31#include "llimview.h"
 32#include "llnotifications.h"
 33#include "llnotificationsutil.h"
 34#include "llpanel.h"
 35#include "llrecentpeople.h"
 36#include "llviewercontrol.h"
 37#include "llvoicechannel.h"
 38
 39
 40LLVoiceChannel::voice_channel_map_t LLVoiceChannel::sVoiceChannelMap;
 41LLVoiceChannel::voice_channel_map_uri_t LLVoiceChannel::sVoiceChannelURIMap;
 42LLVoiceChannel* LLVoiceChannel::sCurrentVoiceChannel = NULL;
 43LLVoiceChannel* LLVoiceChannel::sSuspendedVoiceChannel = NULL;
 44LLVoiceChannel::channel_changed_signal_t LLVoiceChannel::sCurrentVoiceChannelChangedSignal;
 45
 46BOOL LLVoiceChannel::sSuspended = FALSE;
 47
 48//
 49// Constants
 50//
 51const U32 DEFAULT_RETRIES_COUNT = 3;
 52
 53
 54class LLVoiceCallCapResponder : public LLHTTPClient::Responder
 55{
 56public:
 57	LLVoiceCallCapResponder(const LLUUID& session_id) : mSessionID(session_id) {};
 58
 59	virtual void error(U32 status, const std::string& reason);	// called with bad status codes
 60	virtual void result(const LLSD& content);
 61
 62private:
 63	LLUUID mSessionID;
 64};
 65
 66
 67void LLVoiceCallCapResponder::error(U32 status, const std::string& reason)
 68{
 69	LL_WARNS("Voice") << "LLVoiceCallCapResponder::error("
 70		<< status << ": " << reason << ")"
 71		<< LL_ENDL;
 72	LLVoiceChannel* channelp = LLVoiceChannel::getChannelByID(mSessionID);
 73	if ( channelp )
 74	{
 75		if ( 403 == status )
 76		{
 77			//403 == no ability
 78			LLNotificationsUtil::add(
 79				"VoiceNotAllowed",
 80				channelp->getNotifyArgs());
 81		}
 82		else
 83		{
 84			LLNotificationsUtil::add(
 85				"VoiceCallGenericError",
 86				channelp->getNotifyArgs());
 87		}
 88		channelp->deactivate();
 89	}
 90}
 91
 92void LLVoiceCallCapResponder::result(const LLSD& content)
 93{
 94	LLVoiceChannel* channelp = LLVoiceChannel::getChannelByID(mSessionID);
 95	if (channelp)
 96	{
 97		//*TODO: DEBUG SPAM
 98		LLSD::map_const_iterator iter;
 99		for(iter = content.beginMap(); iter != content.endMap(); ++iter)
100		{
101			LL_DEBUGS("Voice") << "LLVoiceCallCapResponder::result got " 
102				<< iter->first << LL_ENDL;
103		}
104
105		channelp->setChannelInfo(
106			content["voice_credentials"]["channel_uri"].asString(),
107			content["voice_credentials"]["channel_credentials"].asString());
108	}
109}
110
111//
112// LLVoiceChannel
113//
114LLVoiceChannel::LLVoiceChannel(const LLUUID& session_id, const std::string& session_name) : 
115	mSessionID(session_id), 
116	mState(STATE_NO_CHANNEL_INFO), 
117	mSessionName(session_name),
118	mCallDirection(OUTGOING_CALL),
119	mIgnoreNextSessionLeave(FALSE),
120	mCallEndedByAgent(false)
121{
122	mNotifyArgs["VOICE_CHANNEL_NAME"] = mSessionName;
123
124	if (!sVoiceChannelMap.insert(std::make_pair(session_id, this)).second)
125	{
126		// a voice channel already exists for this session id, so this instance will be orphaned
127		// the end result should simply be the failure to make voice calls
128		LL_WARNS("Voice") << "Duplicate voice channels registered for session_id " << session_id << LL_ENDL;
129	}
130}
131
132LLVoiceChannel::~LLVoiceChannel()
133{
134	// Must check instance exists here, the singleton MAY have already been destroyed.
135	if(LLVoiceClient::instanceExists())
136	{
137		LLVoiceClient::getInstance()->removeObserver(this);
138	}
139	
140	sVoiceChannelMap.erase(mSessionID);
141	sVoiceChannelURIMap.erase(mURI);
142}
143
144void LLVoiceChannel::setChannelInfo(
145	const std::string& uri,
146	const std::string& credentials)
147{
148	setURI(uri);
149
150	mCredentials = credentials;
151
152	if (mState == STATE_NO_CHANNEL_INFO)
153	{
154		if (mURI.empty())
155		{
156			LLNotificationsUtil::add("VoiceChannelJoinFailed", mNotifyArgs);
157			LL_WARNS("Voice") << "Received empty URI for channel " << mSessionName << LL_ENDL;
158			deactivate();
159		}
160		else if (mCredentials.empty())
161		{
162			LLNotificationsUtil::add("VoiceChannelJoinFailed", mNotifyArgs);
163			LL_WARNS("Voice") << "Received empty credentials for channel " << mSessionName << LL_ENDL;
164			deactivate();
165		}
166		else
167		{
168			setState(STATE_READY);
169
170			// if we are supposed to be active, reconnect
171			// this will happen on initial connect, as we request credentials on first use
172			if (sCurrentVoiceChannel == this)
173			{
174				// just in case we got new channel info while active
175				// should move over to new channel
176				activate();
177			}
178		}
179	}
180}
181
182void LLVoiceChannel::onChange(EStatusType type, const std::string &channelURI, bool proximal)
183{
184	if (channelURI != mURI)
185	{
186		return;
187	}
188
189	if (type < BEGIN_ERROR_STATUS)
190	{
191		handleStatusChange(type);
192	}
193	else
194	{
195		handleError(type);
196	}
197}
198
199void LLVoiceChannel::handleStatusChange(EStatusType type)
200{
201	// status updates
202	switch(type)
203	{
204	case STATUS_LOGIN_RETRY:
205		//mLoginNotificationHandle = LLNotifyBox::showXml("VoiceLoginRetry")->getHandle();
206		LLNotificationsUtil::add("VoiceLoginRetry");
207		break;
208	case STATUS_LOGGED_IN:
209		//if (!mLoginNotificationHandle.isDead())
210		//{
211		//	LLNotifyBox* notifyp = (LLNotifyBox*)mLoginNotificationHandle.get();
212		//	if (notifyp)
213		//	{
214		//		notifyp->close();
215		//	}
216		//	mLoginNotificationHandle.markDead();
217		//}
218		break;
219	case STATUS_LEFT_CHANNEL:
220		if (callStarted() && !mIgnoreNextSessionLeave && !sSuspended)
221		{
222			// if forceably removed from channel
223			// update the UI and revert to default channel
224			deactivate();
225		}
226		mIgnoreNextSessionLeave = FALSE;
227		break;
228	case STATUS_JOINING:
229		if (callStarted())
230		{
231			setState(STATE_RINGING);
232		}
233		break;
234	case STATUS_JOINED:
235		if (callStarted())
236		{
237			setState(STATE_CONNECTED);
238		}
239	default:
240		break;
241	}
242}
243
244// default behavior is to just deactivate channel
245// derived classes provide specific error messages
246void LLVoiceChannel::handleError(EStatusType type)
247{
248	deactivate();
249	setState(STATE_ERROR);
250}
251
252BOOL LLVoiceChannel::isActive()
253{ 
254	// only considered active when currently bound channel matches what our channel
255	return callStarted() && LLVoiceClient::getInstance()->getCurrentChannel() == mURI; 
256}
257
258BOOL LLVoiceChannel::callStarted()
259{
260	return mState >= STATE_CALL_STARTED;
261}
262
263void LLVoiceChannel::deactivate()
264{
265	if (mState >= STATE_RINGING)
266	{
267		// ignore session leave event
268		mIgnoreNextSessionLeave = TRUE;
269	}
270
271	if (callStarted())
272	{
273		setState(STATE_HUNG_UP);
274		
275		//Default mic is OFF when leaving voice calls
276		if (gSavedSettings.getBOOL("AutoDisengageMic") && 
277			sCurrentVoiceChannel == this &&
278			LLVoiceClient::getInstance()->getUserPTTState())
279		{
280			gSavedSettings.setBOOL("PTTCurrentlyEnabled", true);
281			LLVoiceClient::getInstance()->inputUserControlState(true);
282		}
283	}
284	LLVoiceClient::getInstance()->removeObserver(this);
285	
286	if (sCurrentVoiceChannel == this)
287	{
288		// default channel is proximal channel
289		sCurrentVoiceChannel = LLVoiceChannelProximal::getInstance();
290		sCurrentVoiceChannel->activate();
291	}
292}
293
294void LLVoiceChannel::activate()
295{
296	if (callStarted())
297	{
298		return;
299	}
300
301	// deactivate old channel and mark ourselves as the active one
302	if (sCurrentVoiceChannel != this)
303	{
304		// mark as current before deactivating the old channel to prevent
305		// activating the proximal channel between IM calls
306		LLVoiceChannel* old_channel = sCurrentVoiceChannel;
307		sCurrentVoiceChannel = this;
308		mCallDialogPayload["old_channel_name"] = "";
309		if (old_channel)
310		{
311			mCallDialogPayload["old_channel_name"] = old_channel->getSessionName();
312			old_channel->deactivate();
313		}
314	}
315
316	if (mState == STATE_NO_CHANNEL_INFO)
317	{
318		// responsible for setting status to active
319		getChannelInfo();
320	}
321	else
322	{
323		setState(STATE_CALL_STARTED);
324	}
325	
326	LLVoiceClient::getInstance()->addObserver(this);
327	
328	//do not send earlier, channel should be initialized, should not be in STATE_NO_CHANNEL_INFO state
329	sCurrentVoiceChannelChangedSignal(this->mSessionID);
330}
331
332void LLVoiceChannel::getChannelInfo()
333{
334	// pretend we have everything we need
335	if (sCurrentVoiceChannel == this)
336	{
337		setState(STATE_CALL_STARTED);
338	}
339}
340
341//static 
342LLVoiceChannel* LLVoiceChannel::getChannelByID(const LLUUID& session_id)
343{
344	voice_channel_map_t::iterator found_it = sVoiceChannelMap.find(session_id);
345	if (found_it == sVoiceChannelMap.end())
346	{
347		return NULL;
348	}
349	else
350	{
351		return found_it->second;
352	}
353}
354
355//static 
356LLVoiceChannel* LLVoiceChannel::getChannelByURI(std::string uri)
357{
358	voice_channel_map_uri_t::iterator found_it = sVoiceChannelURIMap.find(uri);
359	if (found_it == sVoiceChannelURIMap.end())
360	{
361		return NULL;
362	}
363	else
364	{
365		return found_it->second;
366	}
367}
368
369LLVoiceChannel* LLVoiceChannel::getCurrentVoiceChannel()
370{
371	return sCurrentVoiceChannel;
372}
373
374void LLVoiceChannel::updateSessionID(const LLUUID& new_session_id)
375{
376	sVoiceChannelMap.erase(sVoiceChannelMap.find(mSessionID));
377	mSessionID = new_session_id;
378	sVoiceChannelMap.insert(std::make_pair(mSessionID, this));
379}
380
381void LLVoiceChannel::setURI(std::string uri)
382{
383	sVoiceChannelURIMap.erase(mURI);
384	mURI = uri;
385	sVoiceChannelURIMap.insert(std::make_pair(mURI, this));
386}
387
388void LLVoiceChannel::setState(EState state)
389{
390	switch(state)
391	{
392	case STATE_RINGING:
393		//TODO: remove or redirect this call status notification
394//		LLCallInfoDialog::show("ringing", mNotifyArgs);
395		break;
396	case STATE_CONNECTED:
397		//TODO: remove or redirect this call status notification
398//		LLCallInfoDialog::show("connected", mNotifyArgs);
399		break;
400	case STATE_HUNG_UP:
401		//TODO: remove or redirect this call status notification
402//		LLCallInfoDialog::show("hang_up", mNotifyArgs);
403		break;
404	default:
405		break;
406	}
407
408	doSetState(state);
409}
410
411void LLVoiceChannel::doSetState(const EState& new_state)
412{
413	EState old_state = mState;
414	mState = new_state;
415
416	if (!mStateChangedCallback.empty())
417		mStateChangedCallback(old_state, mState, mCallDirection, mCallEndedByAgent);
418}
419
420//static
421void LLVoiceChannel::initClass()
422{
423	sCurrentVoiceChannel = LLVoiceChannelProximal::getInstance();
424}
425
426//static 
427void LLVoiceChannel::suspend()
428{
429	if (!sSuspended)
430	{
431		sSuspendedVoiceChannel = sCurrentVoiceChannel;
432		sSuspended = TRUE;
433	}
434}
435
436//static 
437void LLVoiceChannel::resume()
438{
439	if (sSuspended)
440	{
441		if (LLVoiceClient::getInstance()->voiceEnabled())
442		{
443			if (sSuspendedVoiceChannel)
444			{
445				sSuspendedVoiceChannel->activate();
446			}
447			else
448			{
449				LLVoiceChannelProximal::getInstance()->activate();
450			}
451		}
452		sSuspended = FALSE;
453	}
454}
455
456boost::signals2::connection LLVoiceChannel::setCurrentVoiceChannelChangedCallback(channel_changed_callback_t cb, bool at_front)
457{
458	if (at_front)
459	{
460		return sCurrentVoiceChannelChangedSignal.connect(cb,  boost::signals2::at_front);
461	}
462	else
463	{
464		return sCurrentVoiceChannelChangedSignal.connect(cb);
465	}
466}
467
468//
469// LLVoiceChannelGroup
470//
471
472LLVoiceChannelGroup::LLVoiceChannelGroup(const LLUUID& session_id, const std::string& session_name) : 
473	LLVoiceChannel(session_id, session_name)
474{
475	mRetries = DEFAULT_RETRIES_COUNT;
476	mIsRetrying = FALSE;
477}
478
479void LLVoiceChannelGroup::deactivate()
480{
481	if (callStarted())
482	{
483		LLVoiceClient::getInstance()->leaveNonSpatialChannel();
484	}
485	LLVoiceChannel::deactivate();
486}
487
488void LLVoiceChannelGroup::activate()
489{
490	if (callStarted()) return;
491
492	LLVoiceChannel::activate();
493
494	if (callStarted())
495	{
496		// we have the channel info, just need to use it now
497		LLVoiceClient::getInstance()->setNonSpatialChannel(
498			mURI,
499			mCredentials);
500
501		if (!gAgent.isInGroup(mSessionID)) // ad-hoc channel
502		{
503			LLIMModel::LLIMSession* session = LLIMModel::getInstance()->findIMSession(mSessionID);
504			// Adding ad-hoc call participants to Recent People List.
505			// If it's an outgoing ad-hoc, we can use mInitialTargetIDs that holds IDs of people we
506			// called(both online and offline) as source to get people for recent (STORM-210).
507			if (session->isOutgoingAdHoc())
508			{
509				for (uuid_vec_t::iterator it = session->mInitialTargetIDs.begin();
510					it!=session->mInitialTargetIDs.end();++it)
511				{
512					const LLUUID id = *it;
513					LLRecentPeople::instance().add(id);
514				}
515			}
516			// If this ad-hoc is incoming then trying to get ids of people from mInitialTargetIDs
517			// would lead to EXT-8246. So in this case we get them from speakers list.
518			else
519			{
520				LLIMModel::addSpeakersToRecent(mSessionID);
521			}
522		}
523
524		//Mic default state is OFF on initiating/joining Ad-Hoc/Group calls
525		if (LLVoiceClient::getInstance()->getUserPTTState() && LLVoiceClient::getInstance()->getPTTIsToggle())
526		{
527			LLVoiceClient::getInstance()->inputUserControlState(true);
528		}
529		
530	}
531}
532
533void LLVoiceChannelGroup::getChannelInfo()
534{
535	LLViewerRegion* region = gAgent.getRegion();
536	if (region)
537	{
538		std::string url = region->getCapability("ChatSessionRequest");
539		LLSD data;
540		data["method"] = "call";
541		data["session-id"] = mSessionID;
542		LLHTTPClient::post(url,
543						   data,
544						   new LLVoiceCallCapResponder(mSessionID));
545	}
546}
547
548void LLVoiceChannelGroup::setChannelInfo(
549	const std::string& uri,
550	const std::string& credentials)
551{
552	setURI(uri);
553
554	mCredentials = credentials;
555
556	if (mState == STATE_NO_CHANNEL_INFO)
557	{
558		if(!mURI.empty() && !mCredentials.empty())
559		{
560			setState(STATE_READY);
561
562			// if we are supposed to be active, reconnect
563			// this will happen on initial connect, as we request credentials on first use
564			if (sCurrentVoiceChannel == this)
565			{
566				// just in case we got new channel info while active
567				// should move over to new channel
568				activate();
569			}
570		}
571		else
572		{
573			//*TODO: notify user
574			LL_WARNS("Voice") << "Received invalid credentials for channel " << mSessionName << LL_ENDL;
575			deactivate();
576		}
577	}
578	else if ( mIsRetrying )
579	{
580		// we have the channel info, just need to use it now
581		LLVoiceClient::getInstance()->setNonSpatialChannel(
582			mURI,
583			mCredentials);
584	}
585}
586
587void LLVoiceChannelGroup::handleStatusChange(EStatusType type)
588{
589	// status updates
590	switch(type)
591	{
592	case STATUS_JOINED:
593		mRetries = 3;
594		mIsRetrying = FALSE;
595	default:
596		break;
597	}
598
599	LLVoiceChannel::handleStatusChange(type);
600}
601
602void LLVoiceChannelGroup::handleError(EStatusType status)
603{
604	std::string notify;
605	switch(status)
606	{
607	case ERROR_CHANNEL_LOCKED:
608	case ERROR_CHANNEL_FULL:
609		notify = "VoiceChannelFull";
610		break;
611	case ERROR_NOT_AVAILABLE:
612		//clear URI and credentials
613		//set the state to be no info
614		//and activate
615		if ( mRetries > 0 )
616		{
617			mRetries--;
618			mIsRetrying = TRUE;
619			mIgnoreNextSessionLeave = TRUE;
620
621			getChannelInfo();
622			return;
623		}
624		else
625		{
626			notify = "VoiceChannelJoinFailed";
627			mRetries = DEFAULT_RETRIES_COUNT;
628			mIsRetrying = FALSE;
629		}
630
631		break;
632
633	case ERROR_UNKNOWN:
634	default:
635		break;
636	}
637
638	// notification
639	if (!notify.empty())
640	{
641		LLNotificationPtr notification = LLNotificationsUtil::add(notify, mNotifyArgs);
642		// echo to im window
643		gIMMgr->addMessage(mSessionID, LLUUID::null, SYSTEM_FROM, notification->getMessage());
644	}
645
646	LLVoiceChannel::handleError(status);
647}
648
649void LLVoiceChannelGroup::setState(EState state)
650{
651	switch(state)
652	{
653	case STATE_RINGING:
654		if ( !mIsRetrying )
655		{
656			//TODO: remove or redirect this call status notification
657//			LLCallInfoDialog::show("ringing", mNotifyArgs);
658		}
659
660		doSetState(state);
661		break;
662	default:
663		LLVoiceChannel::setState(state);
664	}
665}
666
667//
668// LLVoiceChannelProximal
669//
670LLVoiceChannelProximal::LLVoiceChannelProximal() : 
671	LLVoiceChannel(LLUUID::null, LLStringUtil::null)
672{
673}
674
675BOOL LLVoiceChannelProximal::isActive()
676{
677	return callStarted() && LLVoiceClient::getInstance()->inProximalChannel(); 
678}
679
680void LLVoiceChannelProximal::activate()
681{
682	if (callStarted()) return;
683
684	if((LLVoiceChannel::sCurrentVoiceChannel != this) && (LLVoiceChannel::getState() == STATE_CONNECTED))
685	{
686		// we're connected to a non-spatial channel, so disconnect.
687		LLVoiceClient::getInstance()->leaveNonSpatialChannel();	
688	}
689	LLVoiceChannel::activate();
690	
691}
692
693void LLVoiceChannelProximal::onChange(EStatusType type, const std::string &channelURI, bool proximal)
694{
695	if (!proximal)
696	{
697		return;
698	}
699
700	if (type < BEGIN_ERROR_STATUS)
701	{
702		handleStatusChange(type);
703	}
704	else
705	{
706		handleError(type);
707	}
708}
709
710void LLVoiceChannelProximal::handleStatusChange(EStatusType status)
711{
712	// status updates
713	switch(status)
714	{
715	case STATUS_LEFT_CHANNEL:
716		// do not notify user when leaving proximal channel
717		return;
718	case STATUS_VOICE_DISABLED:
719		//skip showing "Voice not available at your current location" when agent voice is disabled (EXT-4749)
720		if(LLVoiceClient::getInstance()->voiceEnabled() && LLVoiceClient::getInstance()->isVoiceWorking())
721		{
722			//TODO: remove or redirect this call status notification
723//			LLCallInfoDialog::show("unavailable", mNotifyArgs);
724		}
725		return;
726	default:
727		break;
728	}
729	LLVoiceChannel::handleStatusChange(status);
730}
731
732
733void LLVoiceChannelProximal::handleError(EStatusType status)
734{
735	std::string notify;
736	switch(status)
737	{
738	  case ERROR_CHANNEL_LOCKED:
739	  case ERROR_CHANNEL_FULL:
740		notify = "ProximalVoiceChannelFull";
741		break;
742	  default:
743		 break;
744	}
745
746	// notification
747	if (!notify.empty())
748	{
749		LLNotificationsUtil::add(notify, mNotifyArgs);
750	}
751
752	LLVoiceChannel::handleError(status);
753}
754
755void LLVoiceChannelProximal::deactivate()
756{
757	if (callStarted())
758	{
759		setState(STATE_HUNG_UP);
760	}
761}
762
763
764//
765// LLVoiceChannelP2P
766//
767LLVoiceChannelP2P::LLVoiceChannelP2P(const LLUUID& session_id, const std::string& session_name, const LLUUID& other_user_id) : 
768		LLVoiceChannelGroup(session_id, session_name), 
769		mOtherUserID(other_user_id),
770		mReceivedCall(FALSE)
771{
772	// make sure URI reflects encoded version of other user's agent id
773	// *NOTE: in case of Avaline call generated SIP URL will be incorrect.
774	// But it will be overridden in LLVoiceChannelP2P::setSessionHandle() called when agent accepts call
775	setURI(LLVoiceClient::getInstance()->sipURIFromID(other_user_id));
776}
777
778void LLVoiceChannelP2P::handleStatusChange(EStatusType type)
779{
780	LL_INFOS("Voice") << "P2P CALL CHANNEL STATUS CHANGE: incoming=" << int(mReceivedCall) << " newstatus=" << LLVoiceClientStatusObserver::status2string(type) << " (mState=" << mState << ")" << LL_ENDL;
781
782	// status updates
783	switch(type)
784	{
785	case STATUS_LEFT_CHANNEL:
786		if (callStarted() && !mIgnoreNextSessionLeave && !sSuspended)
787		{
788			// *TODO: use it to show DECLINE voice notification
789			if (mState == STATE_RINGING)
790			{
791				// other user declined call
792				LLNotificationsUtil::add("P2PCallDeclined", mNotifyArgs);
793			}
794			else
795			{
796				// other user hung up, so we didn't end the call				
797				mCallEndedByAgent = false;			
798			}
799			deactivate();
800		}
801		mIgnoreNextSessionLeave = FALSE;
802		return;
803	case STATUS_JOINING:
804		// because we join session we expect to process session leave event in the future. EXT-7371
805		// may be this should be done in the LLVoiceChannel::handleStatusChange.
806		mIgnoreNextSessionLeave = FALSE;
807		break;
808
809	default:
810		break;
811	}
812
813	LLVoiceChannel::handleStatusChange(type);
814}
815
816void LLVoiceChannelP2P::handleError(EStatusType type)
817{
818	switch(type)
819	{
820	case ERROR_NOT_AVAILABLE:
821		LLNotificationsUtil::add("P2PCallNoAnswer", mNotifyArgs);
822		break;
823	default:
824		break;
825	}
826
827	LLVoiceChannel::handleError(type);
828}
829
830void LLVoiceChannelP2P::activate()
831{
832	if (callStarted()) return;
833
834	//call will be counted as ended by user unless this variable is changed in handleStatusChange()
835	mCallEndedByAgent = true;
836
837	LLVoiceChannel::activate();
838
839	if (callStarted())
840	{
841		// no session handle yet, we're starting the call
842		if (mSessionHandle.empty())
843		{
844			mReceivedCall = FALSE;
845			LLVoiceClient::getInstance()->callUser(mOtherUserID);
846		}
847		// otherwise answering the call
848		else
849		{
850			if (!LLVoiceClient::getInstance()->answerInvite(mSessionHandle))
851			{
852				mCallEndedByAgent = false;
853				mSessionHandle.clear();
854				handleError(ERROR_UNKNOWN);
855				return;
856			}
857			// using the session handle invalidates it.  Clear it out here so we can't reuse it by accident.
858			mSessionHandle.clear();
859		}
860
861		// Add the party to the list of people with which we've recently interacted.
862		addToTheRecentPeopleList();
863
864		//Default mic is ON on initiating/joining P2P calls
865		if (!LLVoiceClient::getInstance()->getUserPTTState() && LLVoiceClient::getInstance()->getPTTIsToggle())
866		{
867			LLVoiceClient::getInstance()->inputUserControlState(true);
868		}
869	}
870}
871
872void LLVoiceChannelP2P::getChannelInfo()
873{
874	// pretend we have everything we need, since P2P doesn't use channel info
875	if (sCurrentVoiceChannel == this)
876	{
877		setState(STATE_CALL_STARTED);
878	}
879}
880
881// receiving session from other user who initiated call
882void LLVoiceChannelP2P::setSessionHandle(const std::string& handle, const std::string &inURI)
883{ 
884	BOOL needs_activate = FALSE;
885	if (callStarted())
886	{
887		// defer to lower agent id when already active
888		if (mOtherUserID < gAgent.getID())
889		{
890			// pretend we haven't started the call yet, so we can connect to this session instead
891			deactivate();
892			needs_activate = TRUE;
893		}
894		else
895		{
896			// we are active and have priority, invite the other user again
897			// under the assumption they will join this new session
898			mSessionHandle.clear();
899			LLVoiceClient::getInstance()->callUser(mOtherUserID);
900			return;
901		}
902	}
903
904	mSessionHandle = handle;
905
906	// The URI of a p2p session should always be the other end's SIP URI.
907	if(!inURI.empty())
908	{
909		setURI(inURI);
910	}
911	else
912	{
913		LL_WARNS("Voice") << "incoming SIP URL is not provided. Channel may not work properly." << LL_ENDL;
914		// In the case of an incoming AvaLine call, the generated URI will be different from the
915		// original one. This is because the P2P URI is based on avatar UUID but Avaline is not.
916		// See LLVoiceClient::sessionAddedEvent()
917		setURI(LLVoiceClient::getInstance()->sipURIFromID(mOtherUserID));
918	}
919	
920	mReceivedCall = TRUE;
921
922	if (needs_activate)
923	{
924		activate();
925	}
926}
927
928void LLVoiceChannelP2P::setState(EState state)
929{
930	LL_INFOS("Voice") << "P2P CALL STATE CHANGE: incoming=" << int(mReceivedCall) << " oldstate=" << mState << " newstate=" << state << LL_ENDL;
931
932	if (mReceivedCall) // incoming call
933	{
934		// you only "answer" voice invites in p2p mode
935		// so provide a special purpose message here
936		if (mReceivedCall && state == STATE_RINGING)
937		{
938			//TODO: remove or redirect this call status notification
939//			LLCallInfoDialog::show("answering", mNotifyArgs);
940			doSetState(state);
941			return;
942		}
943	}
944
945	LLVoiceChannel::setState(state);
946}
947
948void LLVoiceChannelP2P::addToTheRecentPeopleList()
949{
950	bool avaline_call = LLIMModel::getInstance()->findIMSession(mSessionID)->isAvalineSessionType();
951	
952	if (avaline_call)
953	{
954		LLSD call_data;
955		std::string call_number = LLVoiceChannel::getSessionName();
956		
957		call_data["avaline_call"]	= true;
958		call_data["session_id"]		= mSessionID;
959		call_data["call_number"]	= call_number;
960		call_data["date"]			= LLDate::now();
961		
962		LLRecentPeople::instance().add(mOtherUserID, call_data);
963	}
964	else
965	{
966		LLRecentPeople::instance().add(mOtherUserID);
967	}
968}