You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
jitsi/src/net/java/sip/communicator/service/protocol/AbstractCallPeer.java

907 lines
31 KiB

/*
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package net.java.sip.communicator.service.protocol;
import java.net.*;
import java.util.*;
import net.java.sip.communicator.service.protocol.event.*;
import net.java.sip.communicator.util.*;
import org.jitsi.service.protocol.event.*;
import org.jitsi.util.event.*;
/**
* Provides a default implementation for most of the <tt>CallPeer</tt> methods
* with the purpose of only leaving custom protocol development to clients using
* the PhoneUI service.
*
* @param <T> the call extension class like for example <tt>CallSipImpl</tt>
* or <tt>CallJabberImpl</tt>
* @param <U> the provider extension class like for example
* <tt>ProtocolProviderServiceSipImpl</tt> or
* <tt>ProtocolProviderServiceJabberImpl</tt>
*
* @author Emil Ivov
* @author Lyubomir Marinov
* @author Yana Stamcheva
*/
public abstract class AbstractCallPeer<T extends Call,
U extends ProtocolProviderService>
extends PropertyChangeNotifier
implements CallPeer
{
/**
* Our class logger.
*/
private static final Logger logger
= Logger.getLogger(AbstractCallPeer.class);
/**
* The constant which describes an empty set of <tt>ConferenceMember</tt>s
* (and which can be used to reduce allocations).
*/
public static final ConferenceMember[] NO_CONFERENCE_MEMBERS
= new ConferenceMember[0];
/**
* The time this call started at.
*/
private long callDurationStartTime = CALL_DURATION_START_TIME_UNKNOWN;
/**
* The list of <tt>CallPeerConferenceListener</tt>s interested in and to be
* notified about changes in conference-related information such as this
* peer acting or not acting as a conference focus and conference membership
* details.
*/
protected final List<CallPeerConferenceListener>
callPeerConferenceListeners
= new ArrayList<CallPeerConferenceListener>();
/**
* All the CallPeer listeners registered with this CallPeer.
*/
protected final List<CallPeerListener> callPeerListeners
= new ArrayList<CallPeerListener>();
/**
* All the CallPeerSecurityListener-s registered with this CallPeer.
*/
protected final List<CallPeerSecurityListener>
callPeerSecurityListeners
= new ArrayList<CallPeerSecurityListener>();
/**
* The indicator which determines whether this peer is acting as a
* conference focus and thus may provide information about
* <tt>ConferenceMember</tt> such as {@link #getConferenceMembers()} and
* {@link #getConferenceMemberCount()}.
*/
private boolean conferenceFocus;
/**
* The list of <tt>ConferenceMember</tt>s currently known to and managed in
* a conference by this <tt>CallPeer</tt>. It is implemented as a
* copy-on-write storage in order to optimize the implementation of
* {@link #getConferenceMembers()} which is used more often than
* {@link #addConferenceMember(ConferenceMember)} and
* {@link #removeConferenceMember(ConferenceMember)}.
*/
private List<ConferenceMember> conferenceMembers;
/**
* The <tt>Object</tt> which synchronizes the access to
* {@link #conferenceMembers} and {@link #unmodifiableConferenceMembers}.
*/
private final Object conferenceMembersSyncRoot = new Object();
/**
* The flag that determines whether our audio stream to this call peer is
* currently muted.
*/
private boolean isMute = false;
/**
* The last fired security event.
*/
private CallPeerSecurityStatusEvent lastSecurityEvent;
/**
* The state of the call peer.
*/
private CallPeerState state = CallPeerState.UNKNOWN;
/**
* An unmodifiable view of {@link #conferenceMembers}. The list of
* <tt>ConferenceMember</tt>s participating in the conference managed by
* this instance is implemented as a copy-on-write storage in order to
* optimize the implementation of {@link #getConferenceMembers()} which is
* used more often than {@link #addConferenceMember(ConferenceMember)} and
* {@link #removeConferenceMember(ConferenceMember)}.
*/
private List<ConferenceMember> unmodifiableConferenceMembers;
/**
* Initializes a new <tt>AbstractCallPeer</tt> instance.
*/
protected AbstractCallPeer()
{
conferenceMembers = Collections.emptyList();
unmodifiableConferenceMembers
= Collections.unmodifiableList(conferenceMembers);
}
/**
* Implements
* <tt>CallPeer#addCallPeerConferenceListener(
* CallPeerConferenceListener)</tt>. In the fashion of the addition of the
* other listeners, does not throw an exception on attempting to add a
* <tt>null</tt> listeners and just ignores the call.
*
* @param listener the <tt>CallPeerConferenceListener</tt> to add
*/
public void addCallPeerConferenceListener(
CallPeerConferenceListener listener)
{
if (listener != null)
synchronized (callPeerConferenceListeners)
{
if (!callPeerConferenceListeners.contains(listener))
callPeerConferenceListeners.add(listener);
}
}
/**
* Registers the <tt>listener</tt> to the list of listeners that would be
* receiving CallPeerEvents.
*
* @param listener a listener instance to register with this peer.
*/
public void addCallPeerListener(CallPeerListener listener)
{
if (listener == null)
return;
synchronized(callPeerListeners)
{
if (!callPeerListeners.contains(listener))
callPeerListeners.add(listener);
}
}
/**
* Registers the <tt>listener</tt> to the list of listeners that would be
* receiving CallPeerSecurityEvents.
*
* @param listener a listener instance to register with this peer.
*/
public void addCallPeerSecurityListener(
CallPeerSecurityListener listener)
{
if (listener == null)
return;
synchronized(callPeerSecurityListeners)
{
if (!callPeerSecurityListeners.contains(listener))
callPeerSecurityListeners.add(listener);
}
}
/**
* Adds a specific <tt>ConferenceMember</tt> to the list of
* <tt>ConferenceMember</tt>s reported by this peer through
* {@link #getConferenceMembers()} and {@link #getConferenceMemberCount()}
* and fires
* <tt>CallPeerConferenceEvent#CONFERENCE_MEMBER_ADDED</tt> to
* the currently registered <tt>CallPeerConferenceListener</tt>s.
*
* @param conferenceMember a <tt>ConferenceMember</tt> to be added to the
* list of <tt>ConferenceMember</tt> reported by this peer. If the specified
* <tt>ConferenceMember</tt> is already contained in the list, it is not
* added again and no event is fired.
*/
public void addConferenceMember(ConferenceMember conferenceMember)
{
if (conferenceMember == null)
throw new NullPointerException("conferenceMember");
else
{
synchronized (conferenceMembersSyncRoot)
{
if (conferenceMembers.contains(conferenceMember))
return;
else
{
List<ConferenceMember> newConferenceMembers
= new ArrayList<ConferenceMember>(conferenceMembers);
if (newConferenceMembers.add(conferenceMember))
{
conferenceMembers = newConferenceMembers;
unmodifiableConferenceMembers
= Collections.unmodifiableList(conferenceMembers);
}
else
return;
}
}
fireCallPeerConferenceEvent(
new CallPeerConferenceEvent(
this,
CallPeerConferenceEvent.CONFERENCE_MEMBER_ADDED,
conferenceMember));
}
}
/**
* Finds the first <tt>ConferenceMember</tt> whose <tt>audioSsrc</tt> is
* equals to a specific value. The method is meant for very frequent use so
* it iterates over the <tt>List</tt> of <tt>ConferenceMember</tt>s without
* creating an <tt>Iterator</tt>.
*
* @param ssrc the SSRC identifier of the audio RTP streams transmitted by
* the <tt>ConferenceMember</tt> that we are looking for.
* @return the first <tt>ConferenceMember</tt> whose <tt>audioSsrc</tt> is
* equal to <tt>ssrc</tt> or <tt>null</tt> if no such
* <tt>ConferenceMember</tt> was found
*/
protected ConferenceMember findConferenceMember(long ssrc)
{
List<ConferenceMember> members = getConferenceMembers();
for (int i = 0, memberCount = members.size(); i < memberCount; i++)
{
ConferenceMember member = members.get(i);
if (member.getAudioSsrc() == ssrc)
return member;
}
return null;
}
/**
* Constructs a <tt>CallPeerChangeEvent</tt> using this call peer as source,
* setting it to be of type <tt>eventType</tt> and the corresponding
* <tt>oldValue</tt> and <tt>newValue</tt>,
*
* @param eventType the type of the event to create and dispatch.
* @param oldValue the value of the source property before it changed.
* @param newValue the current value of the source property.
*/
protected void fireCallPeerChangeEvent(String eventType,
Object oldValue,
Object newValue)
{
this.fireCallPeerChangeEvent( eventType, oldValue, newValue, null);
}
/**
* Constructs a <tt>CallPeerChangeEvent</tt> using this call peer as source,
* setting it to be of type <tt>eventType</tt> and the corresponding
* <tt>oldValue</tt> and <tt>newValue</tt>.
*
* @param eventType the type of the event to create and dispatch.
* @param oldValue the value of the source property before it changed.
* @param newValue the current value of the source property.
* @param reason a string that could be set to contain a human readable
* explanation for the transition (particularly handy when moving into a
* FAILED state).
*/
protected void fireCallPeerChangeEvent(String eventType,
Object oldValue,
Object newValue,
String reason)
{
this.fireCallPeerChangeEvent(eventType, oldValue, newValue, reason, -1);
}
/**
* Constructs a <tt>CallPeerChangeEvent</tt> using this call peer as source,
* setting it to be of type <tt>eventType</tt> and the corresponding
* <tt>oldValue</tt> and <tt>newValue</tt>.
*
* @param eventType the type of the event to create and dispatch.
* @param oldValue the value of the source property before it changed.
* @param newValue the current value of the source property.
* @param reason a string that could be set to contain a human readable
* explanation for the transition (particularly handy when moving into a
* FAILED state).
* @param reasonCode the reason code for the reason of this event.
*/
protected void fireCallPeerChangeEvent(String eventType,
Object oldValue,
Object newValue,
String reason,
int reasonCode)
{
CallPeerChangeEvent evt = new CallPeerChangeEvent(
this, eventType, oldValue, newValue, reason, reasonCode);
if (logger.isDebugEnabled())
logger.debug("Dispatching a CallPeerChangeEvent event to "
+ callPeerListeners.size()
+" listeners. event is: " + evt.toString());
Iterator<CallPeerListener> listeners = null;
synchronized (callPeerListeners)
{
listeners = new ArrayList<CallPeerListener>(
callPeerListeners).iterator();
}
while (listeners.hasNext())
{
CallPeerListener listener = listeners.next();
// catch any possible errors, so we are sure we dispatch events
// to all listeners
try
{
if(eventType.equals(CallPeerChangeEvent
.CALL_PEER_ADDRESS_CHANGE))
{
listener.peerAddressChanged(evt);
} else if(eventType.equals(CallPeerChangeEvent
.CALL_PEER_DISPLAY_NAME_CHANGE))
{
listener.peerDisplayNameChanged(evt);
} else if(eventType.equals(CallPeerChangeEvent
.CALL_PEER_IMAGE_CHANGE))
{
listener.peerImageChanged(evt);
} else if(eventType.equals(CallPeerChangeEvent
.CALL_PEER_STATE_CHANGE))
{
listener.peerStateChanged(evt);
}
}
catch(Throwable t)
{
logger.error("Error dispatching event of type"
+ eventType + " in " + listener, t);
}
}
}
/**
* Fires a specific <tt>CallPeerConferenceEvent</tt> to the
* <tt>CallPeerConferenceListener</tt>s interested in changes in the
* conference-related information provided by this peer.
*
* @param conferenceEvent a <tt>CallPeerConferenceEvent</tt> to be fired and
* carrying the event data
*/
protected void fireCallPeerConferenceEvent(
CallPeerConferenceEvent conferenceEvent)
{
CallPeerConferenceListener[] listeners;
synchronized (callPeerConferenceListeners)
{
listeners
= callPeerConferenceListeners
.toArray(
new CallPeerConferenceListener[
callPeerConferenceListeners.size()]);
}
int eventID = conferenceEvent.getEventID();
if (logger.isDebugEnabled())
{
String eventIDString;
switch (eventID)
{
case CallPeerConferenceEvent.CONFERENCE_FOCUS_CHANGED:
eventIDString = "CONFERENCE_FOCUS_CHANGED";
break;
case CallPeerConferenceEvent.CONFERENCE_MEMBER_ADDED:
eventIDString = "CONFERENCE_MEMBER_ADDED";
break;
case CallPeerConferenceEvent.CONFERENCE_MEMBER_REMOVED:
eventIDString = "CONFERENCE_MEMBER_REMOVED";
break;
default:
eventIDString = "UNKNOWN";
break;
}
logger
.debug(
"Firing CallPeerConferenceEvent with ID "
+ eventIDString
+ " to "
+ listeners.length
+ " listeners");
}
for (CallPeerConferenceListener listener : listeners)
switch (eventID)
{
case CallPeerConferenceEvent.CONFERENCE_FOCUS_CHANGED:
listener.conferenceFocusChanged(conferenceEvent);
break;
case CallPeerConferenceEvent.CONFERENCE_MEMBER_ADDED:
listener.conferenceMemberAdded(conferenceEvent);
break;
case CallPeerConferenceEvent.CONFERENCE_MEMBER_REMOVED:
listener.conferenceMemberRemoved(conferenceEvent);
break;
}
}
/**
* Constructs a <tt>CallPeerSecurityStatusEvent</tt> using this call peer as
* source, setting it to be of type <tt>eventType</tt> and the corresponding
* <tt>oldValue</tt> and <tt>newValue</tt>.
*
* @param messageType the type of the message
* @param i18nMessage message
* @param severity severity level
*/
protected void fireCallPeerSecurityMessageEvent(
String messageType,
String i18nMessage,
int severity)
{
CallPeerSecurityMessageEvent evt
= new CallPeerSecurityMessageEvent( this,
messageType,
i18nMessage,
severity);
if (logger.isDebugEnabled())
logger.debug("Dispatching a CallPeerSecurityFailedEvent event to "
+ callPeerSecurityListeners.size()
+" listeners. event is: " + evt.toString());
Iterator<CallPeerSecurityListener> listeners = null;
synchronized (callPeerSecurityListeners)
{
listeners = new ArrayList<CallPeerSecurityListener>(
callPeerSecurityListeners).iterator();
}
while (listeners.hasNext())
{
CallPeerSecurityListener listener = listeners.next();
listener.securityMessageRecieved(evt);
}
}
/**
* Constructs a <tt>CallPeerSecurityStatusEvent</tt> using this call peer as
* source, setting it to be of type <tt>eventType</tt> and the corresponding
* <tt>oldValue</tt> and <tt>newValue</tt>.
*
* @param evt the event object with details to pass on to the consumers
*/
protected void fireCallPeerSecurityNegotiationStartedEvent(
CallPeerSecurityNegotiationStartedEvent evt)
{
lastSecurityEvent = evt;
if (logger.isDebugEnabled())
logger.debug("Dispatching a CallPeerSecurityStatusEvent event to "
+ callPeerSecurityListeners.size()
+" listeners. event is: " + evt.toString());
List<CallPeerSecurityListener> listeners = null;
synchronized (callPeerSecurityListeners)
{
listeners = new ArrayList<CallPeerSecurityListener>(
callPeerSecurityListeners);
}
for(CallPeerSecurityListener listener : listeners)
{
listener.securityNegotiationStarted(evt);
}
}
/**
* Constructs a <tt>CallPeerSecurityStatusEvent</tt> using this call peer as
* source, setting it to be of type <tt>eventType</tt> and the corresponding
* <tt>oldValue</tt> and <tt>newValue</tt>.
*
* @param evt the event object with details to pass on to the consumers
*/
protected void fireCallPeerSecurityOffEvent(CallPeerSecurityOffEvent evt)
{
lastSecurityEvent = evt;
if (logger.isDebugEnabled())
logger.debug(
"Dispatching a CallPeerSecurityAuthenticationEvent event to "
+ callPeerSecurityListeners.size()
+" listeners. event is: " + evt.toString());
List<CallPeerSecurityListener> listeners = null;
synchronized (callPeerSecurityListeners)
{
listeners = new ArrayList<CallPeerSecurityListener>(
callPeerSecurityListeners);
}
for(CallPeerSecurityListener listener : listeners)
{
listener.securityOff(evt);
}
}
/**
* Constructs a <tt>CallPeerSecurityStatusEvent</tt> using this call peer as
* source, setting it to be of type <tt>eventType</tt> and the corresponding
* <tt>oldValue</tt> and <tt>newValue</tt>.
*
* @param evt the event object with details to pass on to the consumers
*/
protected void fireCallPeerSecurityOnEvent(CallPeerSecurityOnEvent evt)
{
lastSecurityEvent = evt;
if (logger.isDebugEnabled())
logger.debug("Dispatching a CallPeerSecurityStatusEvent event to "
+ callPeerSecurityListeners.size()
+" listeners. event is: " + evt.toString());
List<CallPeerSecurityListener> listeners = null;
synchronized (callPeerSecurityListeners)
{
listeners = new ArrayList<CallPeerSecurityListener>(
callPeerSecurityListeners);
}
for(CallPeerSecurityListener listener : listeners)
{
listener.securityOn(evt);
}
}
/**
* Constructs a <tt>CallPeerSecurityStatusEvent</tt> using this call peer as
* source, setting it to be of type <tt>eventType</tt> and the corresponding
* <tt>oldValue</tt> and <tt>newValue</tt>.
*
* @param evt the event object with details to pass on to the consumers
*/
protected void fireCallPeerSecurityTimeoutEvent(
CallPeerSecurityTimeoutEvent evt)
{
lastSecurityEvent = evt;
if (logger.isDebugEnabled())
logger.debug("Dispatching a CallPeerSecurityStatusEvent event to "
+ callPeerSecurityListeners.size()
+" listeners. event is: " + evt.toString());
List<CallPeerSecurityListener> listeners = null;
synchronized (callPeerSecurityListeners)
{
listeners = new ArrayList<CallPeerSecurityListener>(
callPeerSecurityListeners);
}
for(CallPeerSecurityListener listener : listeners)
{
listener.securityTimeout(evt);
}
}
/**
* Returns a reference to the call that this peer belongs to.
*
* @return a reference to the call containing this peer.
*/
public abstract T getCall();
/**
* Gets the time at which this <tt>CallPeer</tt> transitioned into a state
* (likely {@link CallPeerState#CONNECTED}) marking the start of the
* duration of the participation in a <tt>Call</tt>.
*
* @return the time at which this <tt>CallPeer</tt> transitioned into a
* state marking the start of the duration of the participation in a
* <tt>Call</tt> or {@link CallPeer#CALL_DURATION_START_TIME_UNKNOWN} if
* such a transition has not been performed
*/
public long getCallDurationStartTime()
{
return callDurationStartTime;
}
/**
* Returns a URL pointing ta a location with call control information for
* this peer or <tt>null</tt> if no such URL is available for this call
* peer.
*
* @return a URL link to a location with call information or a call control
* web interface related to this peer or <tt>null</tt> if no such URL is
* available.
*/
public URL getCallInfoURL()
{
//if signaling protocols (such as SIP) know where to get this URL from
//they should override this method
return null;
}
/**
* {@inheritDoc}
*/
public int getConferenceMemberCount()
{
synchronized (conferenceMembersSyncRoot)
{
return isConferenceFocus() ? getConferenceMembers().size() : 0;
}
}
/**
* {@inheritDoc}
*/
public List<ConferenceMember> getConferenceMembers()
{
synchronized (conferenceMembersSyncRoot)
{
return unmodifiableConferenceMembers;
}
}
/**
* Returns the currently used security settings of this <tt>CallPeer</tt>.
*
* @return the <tt>CallPeerSecurityStatusEvent</tt> that contains the
* current security settings.
*/
public CallPeerSecurityStatusEvent getCurrentSecuritySettings()
{
return lastSecurityEvent;
}
/**
* Returns the protocol provider that this peer belongs to.
*
* @return a reference to the ProtocolProviderService that this peer
* belongs to.
*/
public abstract U getProtocolProvider();
/**
* Returns an object representing the current state of that peer.
*
* @return a CallPeerState instance representing the peer's state.
*/
public CallPeerState getState()
{
return state;
}
/**
* Determines whether this call peer is currently a conference focus.
*
* @return <tt>true</tt> if this peer is a conference focus and
* <tt>false</tt> otherwise.
*/
public boolean isConferenceFocus()
{
return conferenceFocus;
}
/**
* Determines whether the audio stream (if any) being sent to this peer is
* mute.
* <p>
* The default implementation returns <tt>false</tt>.
* </p>
*
* @return <tt>true</tt> if an audio stream is being sent to this peer and
* it is currently mute; <tt>false</tt>, otherwise
*/
public boolean isMute()
{
return isMute;
}
/**
* Implements
* <tt>CallPeer#removeCallPeerConferenceListener(
* CallPeerConferenceListener)</tt>.
*
* @param listener the <tt>CallPeerConferenceListener</tt> to remove
*/
public void removeCallPeerConferenceListener(
CallPeerConferenceListener listener)
{
if (listener != null)
synchronized (callPeerConferenceListeners)
{
callPeerConferenceListeners.remove(listener);
}
}
/**
* Unregisters the specified listener.
*
* @param listener the listener to unregister.
*/
public void removeCallPeerListener(CallPeerListener listener)
{
if (listener == null)
return;
synchronized(callPeerListeners)
{
callPeerListeners.remove(listener);
}
}
/**
* Unregisters the specified listener.
*
* @param listener the listener to unregister.
*/
public void removeCallPeerSecurityListener(
CallPeerSecurityListener listener)
{
if (listener == null)
return;
synchronized(callPeerSecurityListeners)
{
callPeerSecurityListeners.remove(listener);
}
}
/**
* Removes a specific <tt>ConferenceMember</tt> from the list of
* <tt>ConferenceMember</tt>s reported by this peer through
* {@link #getConferenceMembers()} and {@link #getConferenceMemberCount()}
* if it is contained and fires
* <tt>CallPeerConferenceEvent#CONFERENCE_MEMBER_REMOVED</tt> to
* the currently registered <tt>CallPeerConferenceListener</tt>s.
*
* @param conferenceMember a <tt>ConferenceMember</tt> to be removed from
* the list of <tt>ConferenceMember</tt> reported by this peer. If the
* specified <tt>ConferenceMember</tt> is no contained in the list, no event
* is fired.
*/
public void removeConferenceMember(ConferenceMember conferenceMember)
{
if (conferenceMember != null)
{
synchronized (conferenceMembersSyncRoot)
{
if (conferenceMembers.contains(conferenceMember))
{
List<ConferenceMember> newConferenceMembers
= new ArrayList<ConferenceMember>(conferenceMembers);
if (newConferenceMembers.remove(conferenceMember))
{
conferenceMembers = newConferenceMembers;
unmodifiableConferenceMembers
= Collections.unmodifiableList(conferenceMembers);
}
else
return;
}
else
return;
}
fireCallPeerConferenceEvent(
new CallPeerConferenceEvent(
this,
CallPeerConferenceEvent.CONFERENCE_MEMBER_REMOVED,
conferenceMember));
}
}
/**
* Specifies whether this peer is a conference focus.
*
* @param conferenceFocus <tt>true</tt> if this peer is to become a
* conference focus and <tt>false</tt> otherwise.
*/
public void setConferenceFocus(boolean conferenceFocus)
{
if (this.conferenceFocus != conferenceFocus)
{
this.conferenceFocus = conferenceFocus;
fireCallPeerConferenceEvent(
new CallPeerConferenceEvent(
this,
CallPeerConferenceEvent.CONFERENCE_FOCUS_CHANGED));
}
}
/**
* Sets the mute property for this call peer.
*
* @param newMuteValue the new value of the mute property for this call peer
*/
public void setMute(boolean newMuteValue)
{
this.isMute = newMuteValue;
firePropertyChange(MUTE_PROPERTY_NAME, isMute, newMuteValue);
}
/**
* Causes this CallPeer to enter the specified state. The method also sets
* the currentStateStartDate field and fires a CallPeerChangeEvent.
*
* @param newState the state this call peer should enter.
*/
public void setState(CallPeerState newState)
{
setState(newState, null);
}
/**
* Causes this CallPeer to enter the specified state. The method also sets
* the currentStateStartDate field and fires a CallPeerChangeEvent.
*
* @param newState the state this call peer should enter.
* @param reason a string that could be set to contain a human readable
* explanation for the transition (particularly handy when moving into a
* FAILED state).
*/
public void setState(CallPeerState newState, String reason)
{
setState(newState, reason, -1);
}
/**
* Causes this CallPeer to enter the specified state. The method also sets
* the currentStateStartDate field and fires a CallPeerChangeEvent.
*
* @param newState the state this call peer should enter.
* @param reason a string that could be set to contain a human readable
* explanation for the transition (particularly handy when moving into a
* FAILED state).
* @param reasonCode the code for the reason of the state change.
*/
public void setState(CallPeerState newState, String reason, int reasonCode)
{
CallPeerState oldState = getState();
if(oldState == newState)
return;
this.state = newState;
if (CallPeerState.CONNECTED.equals(newState)
&& !CallPeerState.isOnHold(oldState))
{
callDurationStartTime = System.currentTimeMillis();
}
fireCallPeerChangeEvent(
CallPeerChangeEvent.CALL_PEER_STATE_CHANGE,
oldState,
newState,
reason,
reasonCode);
}
/**
* Returns a string representation of the peer in the form of
* <br/>
* Display Name &lt;address&gt;;status=CallPeerStatus
*
* @return a string representation of the peer and its state.
*/
@Override
public String toString()
{
return getDisplayName() + " <" + getAddress()
+ ">;status=" + getState().getStateString();
}
}