Commits work in progress on supporting ICE in Jingle.

cusax-fix
Lyubomir Marinov 16 years ago
parent 0d4643ffa5
commit 549cc87c86

Binary file not shown.

@ -724,6 +724,66 @@ private void deviceSessionSsrcListChanged(PropertyChangeEvent evt)
this.localContributingSourceIDList = csrcArray;
}
/**
* Sets the target of this <tt>MediaStream</tt> to which it is to send and
* from which it is to receive data (e.g. RTP) and control data (e.g. RTCP).
* In contrast to {@link #setTarget(MediaStreamTarget)}, sets the specified
* <tt>target</tt> on this <tt>MediaStreamImpl</tt> even if its current
* <tt>target</tt> is equal to the specified one.
*
* @param target the <tt>MediaStreamTarget</tt> describing the data
* (e.g. RTP) and the control data (e.g. RTCP) locations to which this
* <tt>MediaStream</tt> is to send and from which it is to receive
* @see MediaStreamImpl#setTarget(MediaStreamTarget)
*/
private void doSetTarget(MediaStreamTarget target)
{
rtpConnector.removeTargets();
rtpConnectorTarget = null;
boolean targetIsSet;
if (target != null)
{
InetSocketAddress dataAddr = target.getDataAddress();
InetSocketAddress controlAddr = target.getControlAddress();
try
{
rtpConnector
.addTarget(
new SessionAddress(
dataAddr.getAddress(),
dataAddr.getPort(),
controlAddr.getAddress(),
controlAddr.getPort()));
targetIsSet = true;
}
catch (IOException ioe)
{
// TODO
targetIsSet = false;
logger.error("Failed to set target " + target, ioe);
}
}
else
targetIsSet = true;
if (targetIsSet)
{
rtpConnectorTarget = target;
if (logger.isTraceEnabled())
logger
.trace(
"Set target of "
+ getClass().getSimpleName()
+ " with hashCode "
+ hashCode()
+ " to "
+ target);
}
}
/**
* Gets the <tt>MediaDevice</tt> that this stream uses to play back and
* capture media.
@ -1091,8 +1151,17 @@ protected void rtpConnectorChanged(
{
zrtpControl.setConnector(newValue);
// Register the transform engines that we will be using in this stream.
rtpConnector.setEngine(createTransformEngineChain());
if (newValue != null)
{
/*
* Register the transform engines that we will be using in this
* stream.
*/
newValue.setEngine(createTransformEngineChain());
if (rtpConnectorTarget != null)
doSetTarget(rtpConnectorTarget);
}
}
/**
@ -1354,51 +1423,7 @@ public void setTarget(MediaStreamTarget target)
else if (target.equals(rtpConnectorTarget))
return;
rtpConnector.removeTargets();
rtpConnectorTarget = null;
boolean targetIsSet;
if (target != null)
{
InetSocketAddress dataAddr = target.getDataAddress();
InetSocketAddress controlAddr = target.getControlAddress();
try
{
rtpConnector
.addTarget(
new SessionAddress(
dataAddr.getAddress(),
dataAddr.getPort(),
controlAddr.getAddress(),
controlAddr.getPort()));
targetIsSet = true;
}
catch (IOException ioe)
{
// TODO
targetIsSet = false;
logger.error("Failed to set target " + target, ioe);
}
}
else
targetIsSet = true;
if (targetIsSet)
{
rtpConnectorTarget = target;
if (logger.isTraceEnabled())
logger
.trace(
"Set target of "
+ getClass().getSimpleName()
+ " with hashCode "
+ hashCode()
+ " to "
+ target);
}
doSetTarget(target);
}
/**

@ -15,26 +15,23 @@
import net.java.sip.communicator.util.*;
/**
* A Jabber implementation of the Call abstract class encapsulating Jabber
* jingle sessions.
* A Jabber implementation of the <tt>Call</tt> abstract class encapsulating
* Jabber jingle sessions.
*
* @author Emil Ivov
*/
public class CallJabberImpl extends MediaAwareCall<
CallPeerJabberImpl,
OperationSetBasicTelephonyJabberImpl,
ProtocolProviderServiceJabberImpl>
public class CallJabberImpl
extends MediaAwareCall<
CallPeerJabberImpl,
OperationSetBasicTelephonyJabberImpl,
ProtocolProviderServiceJabberImpl>
{
/**
* Logger of this class
* The <tt>Logger</tt> used by the <tt>CallJabberImpl</tt> class and its
* instances for logging output.
*/
private static final Logger logger = Logger.getLogger(CallJabberImpl.class);
/**
* The operation set that created us.
*/
private final OperationSetBasicTelephonyJabberImpl parentOpSet;
/**
* Indicates if the <tt>CallPeer</tt> will support </tt>inputevt</tt>
* extension (i.e. will be able to be remote-controlled).
@ -42,11 +39,11 @@ public class CallJabberImpl extends MediaAwareCall<
private boolean localInputEvtAware = false;
/**
* Crates a CallJabberImpl instance belonging to <tt>sourceProvider</tt> and
* associated with the jingle session with the specified <tt>jingleSID</tt>.
* If this call corresponds to an incoming jingle session then the jingleSID
* would come from there. Otherwise one could generate one using {@link
* JingleIQ#generateSID()}
* Initializes a new <tt>CallJabberImpl</tt> instance belonging to
* <tt>sourceProvider</tt> and associated with the jingle session with the
* specified <tt>jingleSID</tt>. If the new instance corresponds to an
* incoming jingle session, then the jingleSID would come from there.
* Otherwise, one could generate one using {@link JingleIQ#generateSID()}.
*
* @param parentOpSet the {@link OperationSetBasicTelephonyJabberImpl}
* instance in the context of which this call has been created.
@ -55,7 +52,6 @@ protected CallJabberImpl(
OperationSetBasicTelephonyJabberImpl parentOpSet)
{
super(parentOpSet);
this.parentOpSet = parentOpSet;
//let's add ourselves to the calls repo. we are doing it ourselves just
//to make sure that no one ever forgets.
@ -113,9 +109,7 @@ public CallPeerJabberImpl processSessionInitiate(JingleIQ jingleIQ)
// if this was the first peer we added in this call then the call is
// new and we also need to notify everyone of its creation.
if(this.getCallPeerCount() == 1)
{
parentOpSet.fireCallEvent( CallEvent.CALL_RECEIVED, this);
}
return callPeer;
}
@ -142,6 +136,7 @@ public CallPeerJabberImpl initiateSession(String calleeJID,
{
// create the session-initiate IQ
CallPeerJabberImpl callPeer = new CallPeerJabberImpl(calleeJID, this);
callPeer.setDiscoverInfo(discoverInfo);
addCallPeer(callPeer);
@ -151,9 +146,7 @@ public CallPeerJabberImpl initiateSession(String calleeJID,
// if this was the first peer we added in this call then the call is
// new and we also need to notify everyone of its creation.
if(this.getCallPeerCount() == 1)
{
parentOpSet.fireCallEvent( (CallEvent.CALL_INITIATED), this);
}
/* enable video if it is a videocall */
callPeer.getMediaHandler().setLocalVideoTransmissionEnabled(
@ -163,7 +156,7 @@ public CallPeerJabberImpl initiateSession(String calleeJID,
//set call state to connecting so that the user interface would start
//playing the tones. we do that here because we may be harvesting
//stun/turn addresses in initiateSession() which would take a while..
//STUN/TURN addresses in initiateSession() which would take a while.
callPeer.setState( CallPeerState.CONNECTING);
callPeer.initiateSession();
@ -187,9 +180,7 @@ public void modifyVideoContent(boolean allowed)
"Stop local video streaming");
for(CallPeerJabberImpl peer : getCallPeersVector())
{
peer.sendModifyVideoContent(allowed);
}
}
/**
@ -203,13 +194,7 @@ public void modifyVideoContent(boolean allowed)
*/
public boolean containsJingleSID(String sid)
{
for(CallPeerJabberImpl peer : getCallPeersVector())
{
if (peer.getJingleSID().equals(sid))
return true;
}
return false;
return (getPeer(sid) != null);
}
/**
@ -228,7 +213,6 @@ public CallPeerJabberImpl getPeer(String sid)
if (peer.getJingleSID().equals(sid))
return peer;
}
return null;
}
}

@ -19,7 +19,7 @@
import net.java.sip.communicator.util.*;
/**
* Our Jabber implementation of the default CallPeer;
* Implements a Jabber <tt>CallPeer</tt>.
*
* @author Emil Ivov
*/
@ -29,11 +29,11 @@ public class CallPeerJabberImpl
ProtocolProviderServiceJabberImpl>
{
/**
* The <tt>Logger</tt> used by the <tt>CallPeerJabberImpl</tt>
* class and its instances for logging output.
* The <tt>Logger</tt> used by the <tt>CallPeerJabberImpl</tt> class and its
* instances for logging output.
*/
private static final Logger logger = Logger
.getLogger(CallPeerJabberImpl.class.getName());
private static final Logger logger
= Logger.getLogger(CallPeerJabberImpl.class);
/**
* The jabber address of this peer
@ -99,7 +99,7 @@ public void setAddress(String address)
fireCallPeerChangeEvent(
CallPeerChangeEvent.CALL_PEER_ADDRESS_CHANGE,
oldAddress,
address.toString());
address);
}
/**
@ -111,15 +111,10 @@ public String getDisplayName()
{
if (getCall() != null)
{
ProtocolProviderService pps = getCall().getProtocolProvider();
OperationSetPresence opSetPresence
= pps.getOperationSet(OperationSetPresence.class);
Contact contact = getContact();
Contact cont = opSetPresence.findContactByID(getAddress());
if (cont != null)
{
return cont.getDisplayName();
}
if (contact != null)
return contact.getDisplayName();
}
return peerJID;
}
@ -153,26 +148,30 @@ protected synchronized void processSessionInitiate(JingleIQ sessionInitIQ)
this.sessionInitIQ = sessionInitIQ;
this.isInitiator = true;
// This is the SDP offer that came from the initial session-initiate,
//contrary to sip we we are guaranteed to have content because XEP-0166
//says: "A session consists of at least one content type at a time."
// This is the SDP offer that came from the initial session-initiate.
// Contrary to SIP, we are guaranteed to have content because XEP-0166
// says: "A session consists of at least one content type at a time."
List<ContentPacketExtension> offer = sessionInitIQ.getContentList();
try
{
getMediaHandler().processOffer(offer);
}
catch(Exception exc)
catch(Exception ex)
{
logger.info("Failed to process an incoming session initiate", exc);
logger.info("Failed to process an incoming session initiate", ex);
//send an error response;
JingleIQ errResp = JinglePacketFactory.createSessionTerminate(
sessionInitIQ.getTo(), sessionInitIQ.getFrom(),
sessionInitIQ.getSID(), Reason.INCOMPATIBLE_PARAMETERS,
"Error: " + exc.getMessage());
setState(CallPeerState.FAILED, "Error: " + exc.getMessage());
String reasonText = "Error: " + ex.getMessage();
JingleIQ errResp
= JinglePacketFactory.createSessionTerminate(
sessionInitIQ.getTo(),
sessionInitIQ.getFrom(),
sessionInitIQ.getSID(),
Reason.INCOMPATIBLE_PARAMETERS,
reasonText);
setState(CallPeerState.FAILED, reasonText);
getProtocolProvider().getConnection().sendPacket(errResp);
return;
}
@ -196,19 +195,25 @@ protected synchronized void initiateSession()
throws OperationFailedException
{
isInitiator = false;
//Create the media description that we'd like to send to the other side.
List<ContentPacketExtension> offer = getMediaHandler()
.createContentList();
List<ContentPacketExtension> offer
= getMediaHandler().createContentList();
//send a ringing response
if (logger.isTraceEnabled())
logger.trace("will send ringing response: ");
this.sessionInitIQ = JinglePacketFactory
.createSessionInitiate( getProtocolProvider().getOurJID(),
this.peerJID, JingleIQ.generateSID(), offer);
ProtocolProviderServiceJabberImpl protocolProvider
= getProtocolProvider();
getProtocolProvider().getConnection().sendPacket(sessionInitIQ);
this.sessionInitIQ
= JinglePacketFactory.createSessionInitiate(
protocolProvider.getOurJID(),
this.peerJID,
JingleIQ.generateSID(),
offer);
protocolProvider.getConnection().sendPacket(sessionInitIQ);
}
/**
@ -362,11 +367,11 @@ public void processSessionAccept(JingleIQ sessionInitIQ)
{
this.sessionInitIQ = sessionInitIQ;
List<ContentPacketExtension> offer = sessionInitIQ.getContentList();
List<ContentPacketExtension> answer = sessionInitIQ.getContentList();
try
{
getMediaHandler().processAnswer(offer);
getMediaHandler().processAnswer(answer);
}
catch(Exception exc)
{
@ -495,8 +500,7 @@ private void sendAddVideoContent()
try
{
contents = getMediaHandler().
createContentList(MediaType.VIDEO);
contents = getMediaHandler().createContentList(MediaType.VIDEO);
}
catch(Exception exc)
{
@ -504,11 +508,16 @@ private void sendAddVideoContent()
return;
}
JingleIQ contentIQ = JinglePacketFactory
.createContentAdd(getProtocolProvider().getOurJID(),
this.peerJID, getJingleSID(), contents);
ProtocolProviderServiceJabberImpl protocolProvider
= getProtocolProvider();
JingleIQ contentIQ
= JinglePacketFactory.createContentAdd(
protocolProvider.getOurJID(),
this.peerJID,
getJingleSID(),
contents);
getProtocolProvider().getConnection().sendPacket(contentIQ);
protocolProvider.getConnection().sendPacket(contentIQ);
}
/**
@ -730,9 +739,7 @@ public void processContentRemove(JingleIQ content)
List<ContentPacketExtension> contents = content.getContentList();
for(ContentPacketExtension ext : contents)
{
getMediaHandler().removeRemoteContent(ext.getName());
}
}
/**

@ -6,8 +6,11 @@
*/
package net.java.sip.communicator.impl.protocol.jabber;
import java.lang.reflect.*;
import java.util.*;
import org.jivesoftware.smackx.packet.*;
import net.java.sip.communicator.impl.protocol.jabber.extensions.jingle.*;
import net.java.sip.communicator.impl.protocol.jabber.extensions.jingle.ContentPacketExtension.*;
import net.java.sip.communicator.impl.protocol.jabber.jinglesdp.*;
@ -22,6 +25,7 @@
* An XMPP specific extension of the generic media handler.
*
* @author Emil Ivov
* @author Lyubomir Marinov
*/
public class CallPeerMediaHandlerJabberImpl
extends CallPeerMediaHandler<CallPeerJabberImpl>
@ -34,10 +38,10 @@ public class CallPeerMediaHandlerJabberImpl
= Logger.getLogger(CallPeerMediaHandlerJabberImpl.class);
/**
* A temporarily single transport manager that we use for generating
* addresses until we properly implement both ICE and Raw UDP managers.
* The <tt>TransportManager</tt> implementation handling our address
* management.
*/
private final TransportManagerJabberImpl transportManager;
private TransportManagerJabberImpl transportManager;
/**
* The current description of the streams that we have going toward the
@ -76,8 +80,6 @@ public class CallPeerMediaHandlerJabberImpl
public CallPeerMediaHandlerJabberImpl(CallPeerJabberImpl peer)
{
super(peer, peer);
transportManager = new IceUdpTransportManager(peer);
}
/**
@ -94,12 +96,17 @@ public CallPeerMediaHandlerJabberImpl(CallPeerJabberImpl peer)
* @throws OperationFailedException the exception that we wanted this method
* to throw.
*/
@Override
protected void throwOperationFailedException(String message, int errorCode,
Throwable cause) throws OperationFailedException
protected void throwOperationFailedException(
String message,
int errorCode,
Throwable cause)
throws OperationFailedException
{
ProtocolProviderServiceJabberImpl.throwOperationFailedException(
message, errorCode, cause, logger);
message,
errorCode,
cause,
logger);
}
/**
@ -188,8 +195,14 @@ protected MediaStream initStream(String streamName,
List<RTPExtension> rtpExtensions)
throws OperationFailedException
{
MediaStream stream = super.initStream(connector, device, format,
target, direction, rtpExtensions);
MediaStream stream
= super.initStream(
connector,
device,
format,
target,
direction,
rtpExtensions);
if(stream != null)
stream.setName(streamName);
@ -218,8 +231,7 @@ public void processOffer(List<ContentPacketExtension> offer)
{
// prepare to generate answers to all the incoming descriptions
List<ContentPacketExtension> answerContentList
= new ArrayList<ContentPacketExtension>(offer.size());
= new ArrayList<ContentPacketExtension>(offer.size());
boolean atLeastOneValidDescription = false;
for (ContentPacketExtension content : offer)
@ -227,18 +239,19 @@ public void processOffer(List<ContentPacketExtension> offer)
remoteContentMap.put(content.getName(), content);
RtpDescriptionPacketExtension description
= JingleUtils.getRtpDescription(content);
= JingleUtils.getRtpDescription(content);
MediaType mediaType
= MediaType.parseString( description.getMedia() );
= MediaType.parseString( description.getMedia() );
List<MediaFormat> remoteFormats = JingleUtils.extractFormats(
description, getDynamicPayloadTypes());
List<MediaFormat> remoteFormats
= JingleUtils.extractFormats(
description,
getDynamicPayloadTypes());
MediaDevice dev = getDefaultDevice(mediaType);
MediaDirection devDirection = (dev == null)
? MediaDirection.INACTIVE
: dev.getDirection();
MediaDirection devDirection
= (dev == null) ? MediaDirection.INACTIVE : dev.getDirection();
// Take the preference of the user with respect to streaming
// mediaType into account.
@ -272,6 +285,22 @@ public void processOffer(List<ContentPacketExtension> offer)
int targetDataPort = target.getDataAddress().getPort();
// transport
/*
* RawUdpTransportPacketExtension extends
* IceUdpTransportPacketExtension so getting
* IceUdpTransportPacketExtension should suffice.
*/
IceUdpTransportPacketExtension transport
= content.getFirstChildOfType(
IceUdpTransportPacketExtension.class);
/*
* TODO If the offered transport is not supported, attempt to
* fall back to a supported one using transport-replace.
*/
setTransportManager(transport.getNamespace());
if (mutuallySupportedFormats.isEmpty()
|| (devDirection == MediaDirection.INACTIVE)
|| (targetDataPort == 0))
@ -331,10 +360,14 @@ public void processOffer(List<ContentPacketExtension> offer)
}
if (!atLeastOneValidDescription)
ProtocolProviderServiceJabberImpl
.throwOperationFailedException("Offer contained no media "
+ " formats or no valid media descriptions.",
OperationFailedException.ILLEGAL_ARGUMENT, null, logger);
{
ProtocolProviderServiceJabberImpl.throwOperationFailedException(
"Offer contained no media formats"
+ " or no valid media descriptions.",
OperationFailedException.ILLEGAL_ARGUMENT,
null,
logger);
}
//now, before we go, tell the transport manager to start our candidate
//harvest
@ -354,20 +387,22 @@ public void processOffer(List<ContentPacketExtension> offer)
protected List<ContentPacketExtension> generateSessionAccept()
throws OperationFailedException
{
TransportManagerJabberImpl transportManager = getTransportManager();
List<ContentPacketExtension> sessAccept
= getTransportManager().wrapupHarvest();
= transportManager.wrapupHarvest();
CallPeerJabberImpl peer = getPeer();
//user answered an incoming call so we go through whatever content
//entries we are initializing and init their corresponding streams
for(ContentPacketExtension ourContent : sessAccept)
{
RtpDescriptionPacketExtension description
= JingleUtils.getRtpDescription(ourContent);
= JingleUtils.getRtpDescription(ourContent);
MediaType type = MediaType.parseString(description.getMedia());
//
StreamConnector connector
= getTransportManager().getStreamConnector(type);
= transportManager.getStreamConnector(type);
//the device this stream would be reading from and writing to.
MediaDevice dev = getDefaultDevice(type);
@ -382,7 +417,7 @@ protected List<ContentPacketExtension> generateSessionAccept()
//stream direction
MediaDirection direction = JingleUtils.getDirection(
ourContent, !getPeer().isInitiator());
ourContent, !peer.isInitiator());
//let's now see what was the format we announced as first and
//configure the stream with it.
@ -425,15 +460,13 @@ protected List<ContentPacketExtension> generateSessionAccept()
if(ourContent.getChildExtensionsOfType(
InputEvtPacketExtension.class) != null)
{
OperationSetDesktopSharingClientJabberImpl client =
(OperationSetDesktopSharingClientJabberImpl)
this.getPeer().getProtocolProvider().getOperationSet(
OperationSetDesktopSharingClient.class);
OperationSetDesktopSharingClientJabberImpl client
= (OperationSetDesktopSharingClientJabberImpl)
peer.getProtocolProvider().getOperationSet(
OperationSetDesktopSharingClient.class);
if(client != null)
{
if (client != null)
client.fireRemoteControlGranted();
}
}
}
return sessAccept;
@ -447,14 +480,14 @@ protected List<ContentPacketExtension> generateSessionAccept()
* @return the {@link ContentPacketExtension}s of stream that this
* handler is prepared to initiate.
* @throws OperationFailedException if we fail to create the descriptions
* for reasons like - problems with device interaction, allocating ports,
* etc.
* for reasons like problems with device interaction, allocating ports, etc.
*/
private ContentPacketExtension createContent(MediaDevice dev)
throws OperationFailedException
{
MediaDirection direction = dev.getDirection().and(
getDirectionUserPreference(
dev.getMediaType()));
MediaDirection direction
= dev.getDirection().and(
getDirectionUserPreference(dev.getMediaType()));
if(isLocallyOnHold())
direction = direction.and(MediaDirection.SENDONLY);
@ -469,10 +502,11 @@ private ContentPacketExtension createContent(MediaDevice dev)
if(getPeer().getCall().isSipZrtpAttribute())
{
ZrtpControl control = getZrtpControls().get(dev.getMediaType());
if(control == null)
{
control = JabberActivator.getMediaService()
.createZrtpControl();
control
= JabberActivator.getMediaService().createZrtpControl();
getZrtpControls().put(dev.getMediaType(), control);
}
@ -482,6 +516,7 @@ private ContentPacketExtension createContent(MediaDevice dev)
{
ZrtpHashPacketExtension hash
= new ZrtpHashPacketExtension();
hash.setVersion(helloHash[0]);
hash.setValue(helloHash[1]);
@ -534,10 +569,15 @@ public List<ContentPacketExtension> createContentList(MediaType mediaType)
}
//now add the transport elements
getTransportManager().startCandidateHarvest(mediaDescs);
TransportManagerJabberImpl transportManager = getTransportManager();
transportManager.startCandidateHarvest(mediaDescs);
//XXX ideally we wouldn't wrapup that quickly. we need to revisit this
return getTransportManager().wrapupHarvest();
/*
* XXX Ideally, we wouldn't wrap up that quickly. We need to revisit
* this.
*/
return transportManager.wrapupHarvest();
}
/**
@ -550,15 +590,14 @@ public List<ContentPacketExtension> createContentList(MediaType mediaType)
* streams that this handler is prepared to initiate.
*
* @throws OperationFailedException if we fail to create the descriptions
* for reasons like - problems with device interaction, allocating ports,
* etc.
* for reasons like problems with device interaction, allocating ports, etc.
*/
public List<ContentPacketExtension> createContentList()
throws OperationFailedException
{
//Audio Media Description
List<ContentPacketExtension> mediaDescs
= new ArrayList<ContentPacketExtension>();
= new ArrayList<ContentPacketExtension>();
for (MediaType mediaType : MediaType.values())
{
@ -574,9 +613,11 @@ public List<ContentPacketExtension> createContentList()
if(direction != MediaDirection.INACTIVE)
{
ContentPacketExtension content = createContentForOffer(
dev.getSupportedFormats(), direction,
dev.getSupportedExtensions());
ContentPacketExtension content
= createContentForOffer(
dev.getSupportedFormats(),
direction,
dev.getSupportedExtensions());
//ZRTP
if(getPeer().getCall().isSipZrtpAttribute())
@ -607,8 +648,8 @@ public List<ContentPacketExtension> createContentList()
*/
RtpDescriptionPacketExtension description
= JingleUtils.getRtpDescription(content);
if(description.getMedia().equals(
MediaType.VIDEO.toString()) && localInputEvtAware)
if(description.getMedia().equals(MediaType.VIDEO.toString())
&& localInputEvtAware)
{
content.addChildExtension(
new InputEvtPacketExtension());
@ -622,18 +663,24 @@ public List<ContentPacketExtension> createContentList()
//fail if all devices were inactive
if(mediaDescs.isEmpty())
{
ProtocolProviderServiceJabberImpl
.throwOperationFailedException(
"We couldn't find any active Audio/Video devices and "
+ "couldn't create a call",
OperationFailedException.GENERAL_ERROR, null, logger);
ProtocolProviderServiceJabberImpl.throwOperationFailedException(
"We couldn't find any active Audio/Video devices"
+ " and couldn't create a call",
OperationFailedException.GENERAL_ERROR,
null,
logger);
}
//now add the transport elements
getTransportManager().startCandidateHarvest(mediaDescs);
TransportManagerJabberImpl transportManager = getTransportManager();
transportManager.startCandidateHarvest(mediaDescs);
//XXX ideally we wouldn't wrapup that quickly. we need to revisit this
return getTransportManager().wrapupHarvest();
/*
* XXX Ideally, we wouldn't wrap up that quickly. We need to revisit
* this.
*/
return transportManager.wrapupHarvest();
}
/**
@ -656,14 +703,15 @@ private ContentPacketExtension createContentForOffer(
MediaDirection direction,
List<RTPExtension> supportedExtensions)
{
ContentPacketExtension content = JingleUtils.createDescription(
CreatorEnum.initiator,
supportedFormats.get(0).getMediaType().toString(),
JingleUtils.getSenders(direction, !getPeer().isInitiator()),
supportedFormats,
supportedExtensions,
getDynamicPayloadTypes(),
getRtpExtensionsRegistry());
ContentPacketExtension content
= JingleUtils.createDescription(
CreatorEnum.initiator,
supportedFormats.get(0).getMediaType().toString(),
JingleUtils.getSenders(direction, !getPeer().isInitiator()),
supportedFormats,
supportedExtensions,
getDynamicPayloadTypes(),
getRtpExtensionsRegistry());
this.localContentMap.put(content.getName(), content);
return content;
@ -708,10 +756,11 @@ public void reinitAllContents()
* in this operation can synchronize to the mediaHandler instance to wait
* processing to stop (method setState in CallPeer).
*/
public void reinitContent(String name,
public void reinitContent(
String name,
ContentPacketExtension.SendersEnum senders)
throws OperationFailedException,
IllegalArgumentException
throws OperationFailedException,
IllegalArgumentException
{
ContentPacketExtension ext = remoteContentMap.get(name);
@ -790,13 +839,13 @@ public void removeRemoteContent(String name)
*/
private void processContent(ContentPacketExtension content)
throws OperationFailedException,
IllegalArgumentException
IllegalArgumentException
{
RtpDescriptionPacketExtension description
= JingleUtils.getRtpDescription(content);
= JingleUtils.getRtpDescription(content);
MediaType mediaType
= MediaType.parseString( description.getMedia() );
= MediaType.parseString( description.getMedia() );
//stream target
MediaStreamTarget target
@ -882,24 +931,130 @@ public void processAnswer(List<ContentPacketExtension> answer)
throws OperationFailedException,
IllegalArgumentException
{
for ( ContentPacketExtension content : answer)
for (ContentPacketExtension content : answer)
{
remoteContentMap.put(content.getName(), content);
processContent(content);
}
/*
* Since we've received (the) remote candidates from the peer, we can
* start checking them for connectivity.
*/
startConnectivityEstablishment(answer);
}
/**
* Returns the transport manager that is handling our address management.
* Gets the <tt>TransportManager</tt> implementation handling our address
* management.
*
* @return the transport manager that is handling our address management.
* @return the <tt>TransportManager</tt> implementation handling our address
* management
* @see CallPeerMediaHandler#getTransportManager()
*/
public TransportManagerJabberImpl getTransportManager()
{
if (transportManager == null)
{
CallPeerJabberImpl peer = getPeer();
if (peer.isInitiator())
{
throw new IllegalStateException(
"The initiator is expected to specify the transport"
+ " in their offer.");
}
else
{
ScServiceDiscoveryManager discoveryManager
= peer.getProtocolProvider().getDiscoveryManager();
DiscoverInfo peerDiscoverInfo = peer.getDiscoverInfo();
if (discoveryManager.includesFeature(
ProtocolProviderServiceJabberImpl
.URN_XMPP_JINGLE_ICE_UDP_1)
&& ((peerDiscoverInfo == null)
|| peerDiscoverInfo.containsFeature(
ProtocolProviderServiceJabberImpl
.URN_XMPP_JINGLE_ICE_UDP_1)))
{
transportManager = new IceUdpTransportManager(peer);
}
else if (discoveryManager.includesFeature(
ProtocolProviderServiceJabberImpl
.URN_XMPP_JINGLE_RAW_UDP_0)
&& ((peerDiscoverInfo == null)
|| peerDiscoverInfo.containsFeature(
ProtocolProviderServiceJabberImpl
.URN_XMPP_JINGLE_RAW_UDP_0)))
{
transportManager = new RawUdpTransportManager(peer);
}
else if (logger.isDebugEnabled())
{
logger.debug(
"No known Jingle transport supported"
+ " by Jabber call peer "
+ peer);
}
}
}
return transportManager;
}
/**
* Sets the <tt>TransportManager</tt> implementation to handle our address
* management by Jingle transport XML namespace.
*
* @param xmlns the Jingle transport XML namespace specifying the
* <tt>TransportManager</tt> implementation type to be set on this instance
* to handle our address management
* @throws IllegalArgumentException if the specified <tt>xmlns</tt> does not
* specify a (supported) <tt>TransportManager</tt> implementation type
*/
private void setTransportManager(String xmlns)
throws IllegalArgumentException
{
// Is this really going to be an actual change?
if ((transportManager != null)
&& transportManager.getXmlNamespace().equals(xmlns))
{
return;
}
CallPeerJabberImpl peer = getPeer();
if (!peer
.getProtocolProvider()
.getDiscoveryManager().includesFeature(xmlns))
{
throw new IllegalArgumentException(
"Unsupported Jingle transport " + xmlns);
}
/*
* TODO The transportManager is going to be changed so it may need to be
* disposed prior to the change.
*/
if (xmlns.equals(
ProtocolProviderServiceJabberImpl.URN_XMPP_JINGLE_ICE_UDP_1))
{
transportManager = new IceUdpTransportManager(peer);
}
else if (xmlns.equals(
ProtocolProviderServiceJabberImpl.URN_XMPP_JINGLE_RAW_UDP_0))
{
transportManager = new RawUdpTransportManager(peer);
}
else
{
throw new IllegalArgumentException(
"Unsupported Jingle transport " + xmlns);
}
}
/**
* Acts upon a notification received from the remote party indicating that
* they've put us on/off hold.
@ -910,6 +1065,7 @@ public TransportManagerJabberImpl getTransportManager()
public void setRemotelyOnHold(boolean onHold)
{
this.remotelyOnHold = onHold;
MediaStream audioStream = getStream(MediaType.AUDIO);
MediaStream videoStream = getStream(MediaType.VIDEO);
@ -969,8 +1125,9 @@ private MediaDirection calculatePostHoldDirection(MediaStream stream)
//2. check the user preference.
MediaDevice device = stream.getDevice();
postHoldDir = postHoldDir
.and(getDirectionUserPreference(device.getMediaType()));
postHoldDir
= postHoldDir.and(
getDirectionUserPreference(device.getMediaType()));
//3. check our local hold status.
if(isLocallyOnHold())
@ -983,4 +1140,69 @@ private MediaDirection calculatePostHoldDirection(MediaStream stream)
return postHoldDir;
}
/**
* Overrides {@link CallPeerMediaHandler#start()}. Prior to starting this
* <tt>CallPeerMediaHandler</tt>, makes sure connectivity establishment
* through the associated <tt>TransportManager</tt> has been started in
* order to determine the <tt>StreamConnector</tt>s and the
* <tt>MediaStreamTarget</tt>s of the <tt>MediaStream</tt>s managed by this
* instance.
*
* @throws IllegalStateException if this <tt>CallPeerMediaHandler</tt> has
* not first seen a media description or has not generated an offer
* @see CallPeerMediaHandler#start()
*/
@Override
public void start()
throws IllegalStateException
{
if (getPeer().isInitiator())
{
try
{
startConnectivityEstablishment(remoteContentMap.values());
}
catch (OperationFailedException ofe)
{
throw new UndeclaredThrowableException(ofe);
}
}
super.start();
}
/**
* Starts the connectivity establishment of the associated
* <tt>TransportManagerJabberImpl</tt> i.e. checks the connectivity between
* the local and the remote peers given the remote counterpart of the
* negotiation between them and sets the respective <tt>connector</tt>s and
* <tt>target</tt>s of the associated <tt>MediaStream</tt>s.
*
* @param remote the collection of <tt>ContentPacketExtension</tt>s which
* represents the remote counterpart of the negotiation between the local
* and the remote peers
* @throws OperationFailedException if anything goes wrong while starting
* the connectivity establishment or setting the <tt>connector</tt>s or
* <tt>target</tt>s of the associated <tt>MediaStream</tt>s
*/
private void startConnectivityEstablishment(
Collection<ContentPacketExtension> remote)
throws OperationFailedException
{
TransportManagerJabberImpl transportManager = getTransportManager();
transportManager.startConnectivityEstablishment(remote);
for (MediaType mediaType : MediaType.values())
{
MediaStream stream = getStream(mediaType);
if (stream != null)
{
stream.setConnector(
transportManager.getStreamConnector(mediaType));
stream.setTarget(transportManager.getStreamTarget(mediaType));
}
}
}
}

@ -6,10 +6,13 @@
*/
package net.java.sip.communicator.impl.protocol.jabber;
import java.beans.*;
import java.net.*;
import java.util.*;
import net.java.sip.communicator.impl.protocol.jabber.extensions.jingle.*;
import net.java.sip.communicator.impl.protocol.jabber.jinglesdp.*;
import net.java.sip.communicator.service.neomedia.*;
import net.java.sip.communicator.service.netaddr.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.util.*;
@ -24,6 +27,7 @@
* candidate management.
*
* @author Emil Ivov
* @author Lyubomir Marinov
*/
public class IceUdpTransportManager
extends TransportManagerJabberImpl
@ -32,12 +36,19 @@ public class IceUdpTransportManager
* The <tt>Logger</tt> used by the <tt>IceUdpTransportManager</tt>
* class and its instances for logging output.
*/
private static final Logger logger = Logger
.getLogger(IceUdpTransportManager.class.getName());
private static final Logger logger
= Logger.getLogger(IceUdpTransportManager.class);
/**
* The ICE <tt>Component</tt> IDs in their common order used, for example,
* by <tt>DefaultStreamConnector</tt>, <tt>MediaStreamTarget</tt>.
*/
private static final int[] COMPONENT_IDS
= new int[] { Component.RTP, Component.RTCP };
/**
* This is where we keep our answer between the time we get the offer and
* are ready with the answer;
* are ready with the answer.
*/
private List<ContentPacketExtension> cpeList;
@ -54,7 +65,7 @@ public class IceUdpTransportManager
* @param callPeer the {@link CallPeer} whose traffic we will be taking
* care of.
*/
protected IceUdpTransportManager(CallPeerJabberImpl callPeer)
public IceUdpTransportManager(CallPeerJabberImpl callPeer)
{
super(callPeer);
@ -70,12 +81,18 @@ protected IceUdpTransportManager(CallPeerJabberImpl callPeer)
*/
private Agent createIceAgent()
{
ProtocolProviderServiceJabberImpl provider
= getCallPeer().getProtocolProvider();
CallPeerJabberImpl peer = getCallPeer();
ProtocolProviderServiceJabberImpl provider = peer.getProtocolProvider();
NetworkAddressManagerService namSer = getNetAddrMgr();
Agent agent = namSer.createIceAgent();
/*
* XEP-0176: the initiator MUST include the ICE-CONTROLLING attribute,
* the responder MUST include the ICE-CONTROLLED attribute.
*/
agent.setControlling(!peer.isInitiator());
//we will now create the harvesters
JabberAccountID accID = (JabberAccountID)provider.getAccountID();
@ -84,15 +101,18 @@ private Agent createIceAgent()
//the default server is supposed to use the same user name and
//password as the account itself.
String username = provider.getOurJID();
String password = JabberActivator
.getProtocolProviderFactory().loadPassword(accID);
String password
= JabberActivator.getProtocolProviderFactory().loadPassword(
accID);
StunCandidateHarvester autoHarvester = namSer.discoverStunServer(
accID.getService(),
StringUtils.getUTF8Bytes(username),
StringUtils.getUTF8Bytes(password) );
StunCandidateHarvester autoHarvester
= namSer.discoverStunServer(
accID.getService(),
StringUtils.getUTF8Bytes(username),
StringUtils.getUTF8Bytes(password));
logger.info("Auto discovered harvester is " + autoHarvester);
if (logger.isInfoEnabled())
logger.info("Auto discovered harvester is " + autoHarvester);
if (autoHarvester != null)
agent.addCandidateHarvester(autoHarvester);
@ -110,9 +130,12 @@ private Agent createIceAgent()
if(desc.isTurnSupported())
{
//Yay! a TURN server
harvester = new TurnCandidateHarvester(
addr, new LongTermCredential(
desc.getUsername(), desc.getPassword()));
harvester
= new TurnCandidateHarvester(
addr,
new LongTermCredential(
desc.getUsername(),
desc.getPassword()));
}
else
{
@ -120,7 +143,8 @@ addr, new LongTermCredential(
harvester = new StunCandidateHarvester(addr);
}
logger.info("Adding pre-conficugred harvester " + harvester);
if (logger.isInfoEnabled())
logger.info("Adding pre-configured harvester " + harvester);
agent.addCandidateHarvester(harvester);
}
@ -128,6 +152,206 @@ addr, new LongTermCredential(
return agent;
}
/**
* Initializes a new <tt>StreamConnector</tt> to be used as the
* <tt>connector</tt> of the <tt>MediaStream</tt> with a specific
* <tt>MediaType</tt>.
*
* @param mediaType the <tt>MediaType</tt> of the <tt>MediaStream</tt> which
* is to have its <tt>connector</tt> set to the returned
* <tt>StreamConnector</tt>
* @return a new <tt>StreamConnector</tt> to be used as the
* <tt>connector</tt> of the <tt>MediaStream</tt> with the specified
* <tt>MediaType</tt>
* @throws OperationFailedException if anything goes wrong while
* initializing the new <tt>StreamConnector</tt>
*/
@Override
protected StreamConnector createStreamConnector(MediaType mediaType)
throws OperationFailedException
{
DatagramSocket[] streamConnectorSockets
= getStreamConnectorSockets(mediaType);
/*
* XXX If the iceAgent has not completed (yet), go with a default
* StreamConnector (until it completes).
*/
return
(streamConnectorSockets == null)
? super.createStreamConnector(mediaType)
: new DefaultStreamConnector(
streamConnectorSockets[0 /* RTP */],
streamConnectorSockets[1 /* RTCP */]);
}
/**
* Gets the <tt>StreamConnector</tt> to be used as the <tt>connector</tt> of
* the <tt>MediaStream</tt> with a specific <tt>MediaType</tt>.
*
* @param mediaType the <tt>MediaType</tt> of the <tt>MediaStream</tt> which
* is to have its <tt>connector</tt> set to the returned
* <tt>StreamConnector</tt>
* @return the <tt>StreamConnector</tt> to be used as the <tt>connector</tt>
* of the <tt>MediaStream</tt> with the specified <tt>MediaType</tt>
* @throws OperationFailedException if anything goes wrong while
* initializing the requested <tt>StreamConnector</tt>
* @see net.java.sip.communicator.service.protocol.media.TransportManager#getStreamConnector(MediaType)
*/
@Override
public StreamConnector getStreamConnector(MediaType mediaType)
throws OperationFailedException
{
StreamConnector streamConnector = super.getStreamConnector(mediaType);
/*
* Since the super caches the StreamConnectors, make sure that the
* returned one is up-to-date with the iceAgent.
*/
if (streamConnector != null)
{
DatagramSocket[] streamConnectorSockets
= getStreamConnectorSockets(mediaType);
/*
* XXX If the iceAgent has not completed (yet), go with the default
* StreamConnector (until it completes).
*/
if ((streamConnectorSockets != null)
&& ((streamConnector.getDataSocket()
!= streamConnectorSockets[0 /* RTP */])
|| (streamConnector.getControlSocket()
!= streamConnectorSockets[1 /* RTCP */])))
{
// Recreate the StreamConnector for the specified mediaType.
closeStreamConnector(mediaType);
streamConnector = super.getStreamConnector(mediaType);
}
}
return streamConnector;
}
/**
* Gets an array of <tt>DatagramSocket</tt>s which represents the sockets to
* be used by the <tt>StreamConnector</tt> with the specified
* <tt>MediaType</tt> in the order of {@link #COMPONENT_IDS} if
* {@link #iceAgent} has completed.
*
* @param mediaType the <tt>MediaType</tt> of the <tt>StreamConnector</tt>
* for which the <tt>DatagramSocket</tt>s are to be returned
* @return an array of <tt>DatagramSocket</tt>s which represents the sockets
* to be used by the <tt>StreamConnector</tt> which the specified
* <tt>MediaType</tt> in the order of {@link #COMPONENT_IDS} if
* {@link #iceAgent} has completed; otherwise, <tt>null</tt>
*/
private DatagramSocket[] getStreamConnectorSockets(MediaType mediaType)
{
IceMediaStream stream = iceAgent.getStream(mediaType.toString());
if (stream != null)
{
DatagramSocket[] streamConnectorSockets
= new DatagramSocket[COMPONENT_IDS.length];
int streamConnectorSocketCount = 0;
for (int i = 0; i < COMPONENT_IDS.length; i++)
{
Component component = stream.getComponent(COMPONENT_IDS[i]);
if (component != null)
{
CandidatePair selectedPair = component.getSelectedPair();
if (selectedPair != null)
{
DatagramSocket streamConnectorSocket
= selectedPair.getLocalCandidate().getSocket();
if (streamConnectorSocket != null)
{
streamConnectorSockets[i] = streamConnectorSocket;
streamConnectorSocketCount++;
}
}
}
}
if (streamConnectorSocketCount > 0)
return streamConnectorSockets;
}
return null;
}
/**
* Implements {@link TransportManagerJabberImpl#getStreamTarget(MediaType)}.
* Gets the <tt>MediaStreamTarget</tt> to be used as the <tt>target</tt> of
* the <tt>MediaStream</tt> with a specific <tt>MediaType</tt>.
*
* @param mediaType the <tt>MediaType</tt> of the <tt>MediaStream</tt> which
* is to have its <tt>target</tt> set to the returned
* <tt>MediaStreamTarget</tt>
* @return the <tt>MediaStreamTarget</tt> to be used as the <tt>target</tt>
* of the <tt>MediaStream</tt> with the specified <tt>MediaType</tt>
* @see TransportManagerJabberImpl#getStreamTarget(MediaType)
*/
public MediaStreamTarget getStreamTarget(MediaType mediaType)
{
IceMediaStream stream = iceAgent.getStream(mediaType.toString());
MediaStreamTarget streamTarget = null;
if (stream != null)
{
InetSocketAddress[] streamTargetAddresses
= new InetSocketAddress[COMPONENT_IDS.length];
int streamTargetAddressCount = 0;
for (int i = 0; i < COMPONENT_IDS.length; i++)
{
Component component = stream.getComponent(COMPONENT_IDS[i]);
if (component != null)
{
CandidatePair selectedPair = component.getSelectedPair();
if (selectedPair != null)
{
InetSocketAddress streamTargetAddress
= selectedPair
.getRemoteCandidate()
.getTransportAddress();
if (streamTargetAddress != null)
{
streamTargetAddresses[i] = streamTargetAddress;
streamTargetAddressCount++;
}
}
}
}
if (streamTargetAddressCount > 0)
{
streamTarget
= new MediaStreamTarget(
streamTargetAddresses[0 /* RTP */],
streamTargetAddresses[1 /* RTCP */]);
}
}
return streamTarget;
}
/**
* Implements {@link TransportManagerJabberImpl#getXmlNamespace()}. Gets the
* XML namespace of the Jingle transport implemented by this
* <tt>TransportManagerJabberImpl</tt>.
*
* @return the XML namespace of the Jingle transport implemented by this
* <tt>TransportManagerJabberImpl</tt>
* @see TransportManagerJabberImpl#getXmlNamespace()
*/
public String getXmlNamespace()
{
return ProtocolProviderServiceJabberImpl.URN_XMPP_JINGLE_ICE_UDP_1;
}
/**
* Starts transport candidate harvest. This method should complete rapidly
* and, in case of lengthy procedures like STUN/TURN/UPnP candidate harvests
@ -158,15 +382,13 @@ public void startCandidateHarvest(List<ContentPacketExtension> theirOffer,
continue;
RtpDescriptionPacketExtension rtpDesc
= (RtpDescriptionPacketExtension)ourContent
.getFirstChildOfType(RtpDescriptionPacketExtension.class);
= ourContent.getFirstChildOfType(
RtpDescriptionPacketExtension.class);
IceMediaStream stream = createIceStream(rtpDesc.getMedia());
//we now generate the XMPP code containing the candidates.
ourContent.addChildExtension(JingleUtils.createTransport(stream));
}
this.cpeList = ourAnswer;
@ -191,12 +413,11 @@ public void startCandidateHarvest(
for(ContentPacketExtension ourContent : ourOffer)
{
RtpDescriptionPacketExtension rtpDesc
= (RtpDescriptionPacketExtension)ourContent
.getFirstChildOfType(RtpDescriptionPacketExtension.class);
= ourContent.getFirstChildOfType(
RtpDescriptionPacketExtension.class);
IceMediaStream stream = createIceStream(rtpDesc.getMedia());
//we now generate the XMPP code containing the candidates.
ourContent.addChildExtension(JingleUtils.createTransport(stream));
}
@ -225,11 +446,12 @@ private IceMediaStream createIceStream(String media)
stream = getNetAddrMgr().createIceStream(
nextMediaPortToTry, media, iceAgent);
}
catch (Exception exc)
catch (Exception ex)
{
throw new OperationFailedException(
"Failed to initialize stream " + media,
OperationFailedException.INTERNAL_ERROR);
OperationFailedException.INTERNAL_ERROR,
ex);
}
//let's now update the next port var as best we can: we would assume
@ -252,8 +474,6 @@ private IceMediaStream createIceStream(String media)
return stream;
}
/**
* Simply returns the list of local candidates that we gathered during the
* harvest. This is a raw udp transport manager so there's no real wrapping
@ -267,29 +487,6 @@ public List<ContentPacketExtension> wrapupHarvest()
return cpeList;
}
/**
* Looks through the <tt>cpExtList</tt> and returns the {@link
* ContentPacketExtension} with the specified name.
*
* @param cpExtList the list that we will be searching for a specific
* content.
* @param name the name of the content element we are looking for.
* @return the {@link ContentPacketExtension} with the specified name or
* <tt>null</tt> if no such content element exists.
*/
private ContentPacketExtension findContentByName(
List<ContentPacketExtension> cpExtList,
String name)
{
for(ContentPacketExtension cpExt : cpExtList)
{
if(cpExt.getName().equals(name))
return cpExt;
}
return null;
}
/**
* Returns a reference to the {@link NetworkAddressManagerService}. The only
* reason this method exists is that {@link JabberActivator
@ -302,4 +499,127 @@ private static NetworkAddressManagerService getNetAddrMgr()
{
return JabberActivator.getNetworkAddressManagerService();
}
/**
* Implements
* {@link TransportManagerJabberImpl#startConnectivityEstablishment(Collection)}.
* Starts the connectivity establishment of the associated ICE
* <tt>Agent</tt> and waits for it to complete.
*
* @param remote the collection of <tt>ContentPacketExtension</tt>s which
* represents the remote counterpart of the negotiation between the local
* and the remote peers
* @see TransportManagerJabberImpl#startConnectivityEstablishment(Collection)
*/
public void startConnectivityEstablishment(
Collection<ContentPacketExtension> remote)
{
int generation = iceAgent.getGeneration();
for (ContentPacketExtension content : remote)
{
IceUdpTransportPacketExtension transport
= content.getFirstChildOfType(
IceUdpTransportPacketExtension.class);
String ufrag = transport.getUfrag();
if (ufrag != null)
iceAgent.setRemoteUfrag(ufrag);
String password = transport.getPassword();
if (password != null)
iceAgent.setRemotePassword(password);
List<CandidatePacketExtension> candidates
= transport.getChildExtensionsOfType(
CandidatePacketExtension.class);
RtpDescriptionPacketExtension description
= content.getFirstChildOfType(
RtpDescriptionPacketExtension.class);
IceMediaStream stream = iceAgent.getStream(description.getMedia());
for (CandidatePacketExtension candidate : candidates)
{
/*
* Is the remote candidate from the current generation of the
* iceAgent?
*/
if (candidate.getGeneration() != generation)
continue;
Component component
= stream.getComponent(candidate.getComponent());
component.addRemoteCandidate(
new RemoteCandidate(
new TransportAddress(
candidate.getIP(),
candidate.getPort(),
Transport.parse(
candidate.getProtocol())),
component,
org.ice4j.ice.CandidateType.parse(
candidate.getType().toString()),
Integer.toString(candidate.getFoundation()),
candidate.getPriority()));
}
}
final Object[] iceProcessingState = new Object[1];
PropertyChangeListener stateChangeListener
= new PropertyChangeListener()
{
public void propertyChange(PropertyChangeEvent evt)
{
Object newValue = evt.getNewValue();
if (IceProcessingState.COMPLETED.equals(newValue)
|| IceProcessingState.FAILED.equals(newValue)
|| IceProcessingState.TERMINATED.equals(newValue))
{
if (logger.isTraceEnabled())
logger.trace("ICE " + newValue);
Agent iceAgent = (Agent) evt.getSource();
iceAgent.removeStateChangeListener(this);
if (iceAgent == IceUdpTransportManager.this.iceAgent)
{
synchronized (iceProcessingState)
{
iceProcessingState[0] = newValue;
iceProcessingState.notify();
}
}
}
}
};
iceAgent.addStateChangeListener(stateChangeListener);
iceAgent.startConnectivityEstablishment();
// Wait for the connectivity checks to finish.
boolean interrupted = false;
synchronized (iceProcessingState)
{
while (iceProcessingState[0] == null)
{
try
{
iceProcessingState.wait();
}
catch (InterruptedException ie)
{
interrupted = true;
}
}
}
if (interrupted)
Thread.currentThread().interrupt();
}
}

@ -9,6 +9,7 @@
import java.util.*;
import net.java.sip.communicator.impl.protocol.jabber.extensions.jingle.*;
import net.java.sip.communicator.impl.protocol.jabber.jinglesdp.*;
import net.java.sip.communicator.service.neomedia.*;
import net.java.sip.communicator.service.protocol.*;
@ -17,15 +18,23 @@
* single candidate pair (i.e. RTP and RTCP).
*
* @author Emil Ivov
* @author Lyubomir Marinov
*/
public class RawUdpTransportManager
extends TransportManagerJabberImpl
{
/**
* This is where we keep our answer between the time we get the offer and
* are ready with the answer;
* The list of <tt>ContentPacketExtension</tt>s which represents the local
* counterpart of the negotiation between the local and the remote peers.
*/
private List<ContentPacketExtension> cpeList;
private List<ContentPacketExtension> local;
/**
* The collection of <tt>ContentPacketExtension</tt>s which represents the
* remote counterpart of the negotiation between the local and the remote
* peers.
*/
private Collection<ContentPacketExtension> remote;
/**
* Creates a new instance of this transport manager, binding it to the
@ -34,11 +43,61 @@ public class RawUdpTransportManager
* @param callPeer the {@link CallPeer} whose traffic we will be taking
* care of.
*/
protected RawUdpTransportManager(CallPeerJabberImpl callPeer)
public RawUdpTransportManager(CallPeerJabberImpl callPeer)
{
super(callPeer);
}
/**
* Implements {@link TransportManagerJabberImpl#getStreamTarget(MediaType)}.
* Gets the <tt>MediaStreamTarget</tt> to be used as the <tt>target</tt> of
* the <tt>MediaStream</tt> with a specific <tt>MediaType</tt>.
*
* @param mediaType the <tt>MediaType</tt> of the <tt>MediaStream</tt> which
* is to have its <tt>target</tt> set to the returned
* <tt>MediaStreamTarget</tt>
* @return the <tt>MediaStreamTarget</tt> to be used as the <tt>target</tt>
* of the <tt>MediaStream</tt> with the specified <tt>MediaType</tt>
* @see TransportManagerJabberImpl#getStreamTarget(MediaType)
*/
public MediaStreamTarget getStreamTarget(MediaType mediaType)
{
MediaStreamTarget streamTarget = null;
if (remote != null)
{
for (ContentPacketExtension content : remote)
{
RtpDescriptionPacketExtension rtpDescription
= content.getFirstChildOfType(
RtpDescriptionPacketExtension.class);
MediaType contentMediaType
= MediaType.parseString(rtpDescription.getMedia());
if (mediaType.equals(contentMediaType))
{
streamTarget = JingleUtils.extractDefaultTarget(content);
break;
}
}
}
return streamTarget;
}
/**
* Implements {@link TransportManagerJabberImpl#getXmlNamespace()}. Gets the
* XML namespace of the Jingle transport implemented by this
* <tt>TransportManagerJabberImpl</tt>.
*
* @return the XML namespace of the Jingle transport implemented by this
* <tt>TransportManagerJabberImpl</tt>
* @see TransportManagerJabberImpl#getXmlNamespace()
*/
public String getXmlNamespace()
{
return ProtocolProviderServiceJabberImpl.URN_XMPP_JINGLE_RAW_UDP_0;
}
/**
* Starts transport candidate harvest. This method should complete rapidly
* and, in case of lengthy procedures like STUN/TURN/UPnP candidate harvests
@ -61,8 +120,8 @@ public void startCandidateHarvest(List<ContentPacketExtension> theirOffer,
for(ContentPacketExtension content : theirOffer)
{
RtpDescriptionPacketExtension rtpDesc
= (RtpDescriptionPacketExtension)content
.getFirstChildOfType(RtpDescriptionPacketExtension.class);
= content.getFirstChildOfType(
RtpDescriptionPacketExtension.class);
StreamConnector connector = getStreamConnector(
MediaType.parseString( rtpDesc.getMedia()));
@ -81,7 +140,7 @@ public void startCandidateHarvest(List<ContentPacketExtension> theirOffer,
cpExt.addChildExtension(ourTransport);
}
this.cpeList = ourAnswer;
this.local = ourAnswer;
}
/**
@ -91,39 +150,40 @@ public void startCandidateHarvest(List<ContentPacketExtension> theirOffer,
* harvest would then need to be concluded in the {@link #wrapupHarvest()}
* method which would be called once we absolutely need the candidates.
*
* @param ourAnswer the content list that should tell us how many stream
* @param ourOffer the content list that should tell us how many stream
* connectors we actually need.
*
* @throws OperationFailedException in case we fail allocating ports
*/
public void startCandidateHarvest(
List<ContentPacketExtension> ourAnswer)
public void startCandidateHarvest(List<ContentPacketExtension> ourOffer)
throws OperationFailedException
{
for(ContentPacketExtension content : ourAnswer)
for(ContentPacketExtension content : ourOffer)
{
RtpDescriptionPacketExtension rtpDesc
= (RtpDescriptionPacketExtension)content
.getFirstChildOfType(RtpDescriptionPacketExtension.class);
= content.getFirstChildOfType(
RtpDescriptionPacketExtension.class);
StreamConnector connector = getStreamConnector(
MediaType.parseString( rtpDesc.getMedia()));
StreamConnector connector
= getStreamConnector(
MediaType.parseString( rtpDesc.getMedia()));
RawUdpTransportPacketExtension ourTransport
= createTransport(connector);
= createTransport(connector);
//now add our transport to our answer
//now add our transport to our offer
ContentPacketExtension cpExt
= findContentByName(ourAnswer, content.getName());
= findContentByName(ourOffer, content.getName());
cpExt.addChildExtension(ourTransport);
}
this.cpeList = ourAnswer;
this.local = ourOffer;
}
/**
* Creates a raw udp transport element according to the specified stream
* <tt>connector</tt>
* Creates a raw UDP transport element according to the specified stream
* <tt>connector</tt>.
*
* @param connector the connector that we'd like to describe within the
* transport element.
@ -138,7 +198,7 @@ private RawUdpTransportPacketExtension createTransport(
= new RawUdpTransportPacketExtension();
// create and add candidates that correspond to the stream connector
// rtp
// RTP
CandidatePacketExtension rtpCand = new CandidatePacketExtension();
rtpCand.setComponent(CandidatePacketExtension.RTP_COMPONENT_ID);
rtpCand.setGeneration(getCurrentGeneration());
@ -150,7 +210,7 @@ private RawUdpTransportPacketExtension createTransport(
ourTransport.addCandidate(rtpCand);
// rtcp
// RTCP
CandidatePacketExtension rtcpCand = new CandidatePacketExtension();
rtcpCand.setComponent(CandidatePacketExtension.RTCP_COMPONENT_ID);
rtcpCand.setGeneration(getCurrentGeneration());
@ -167,7 +227,7 @@ private RawUdpTransportPacketExtension createTransport(
/**
* Simply returns the list of local candidates that we gathered during the
* harvest. This is a raw udp transport manager so there's no real wraping
* harvest. This is a raw UDP transport manager so there's no real wrapping
* up to do.
*
* @return the list of local candidates that we gathered during the
@ -175,29 +235,25 @@ private RawUdpTransportPacketExtension createTransport(
*/
public List<ContentPacketExtension> wrapupHarvest()
{
return cpeList;
return local;
}
/**
* Looks through the <tt>cpExtList</tt> and returns the {@link
* ContentPacketExtension} with the specified name.
* Implements
* {@link TransportManagerJabberImpl#startConnectivityEstablishment(Collection)}.
* Since this represents a raw UDP transport, performs no connectivity
* checks and just remembers the remote counterpart of the negotiation
* between the local and the remote peers in order to be able to report the
* <tt>MediaStreamTarget</tt>s upon request.
*
* @param cpExtList the list that we will be searching for a specific
* content.
* @param name the name of the content element we are looking for.
* @return the {@link ContentPacketExtension} with the specified name or
* <tt>null</tt> if no such content element exists.
* @param remote the collection of <tt>ContentPacketExtension</tt>s which
* represent the remote counterpart of the negotiation between the local and
* the remote peers
* @see TransportManagerJabberImpl#startConnectivityEstablishment(Collection)
*/
private ContentPacketExtension findContentByName(
List<ContentPacketExtension> cpExtList,
String name)
public void startConnectivityEstablishment(
Collection<ContentPacketExtension> remote)
{
for(ContentPacketExtension cpExt : cpExtList)
{
if(cpExt.getName().equals(name))
return cpExt;
}
return null;
this.remote = remote;
}
}

@ -10,6 +10,7 @@
import java.util.*;
import net.java.sip.communicator.impl.protocol.jabber.extensions.jingle.*;
import net.java.sip.communicator.service.neomedia.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.service.protocol.media.*;
@ -21,6 +22,7 @@
* it has not yet completed.
*
* @author Emil Ivov
* @author Lyubomir Marinov
*/
public abstract class TransportManagerJabberImpl
extends TransportManager<CallPeerJabberImpl>
@ -82,6 +84,27 @@ protected String getNextID()
return Integer.toString(nextID++);
}
/**
* Gets the <tt>MediaStreamTarget</tt> to be used as the <tt>target</tt> of
* the <tt>MediaStream</tt> with a specific <tt>MediaType</tt>.
*
* @param mediaType the <tt>MediaType</tt> of the <tt>MediaStream</tt> which
* is to have its <tt>target</tt> set to the returned
* <tt>MediaStreamTarget</tt>
* @return the <tt>MediaStreamTarget</tt> to be used as the <tt>target</tt>
* of the <tt>MediaStream</tt> with the specified <tt>MediaType</tt>
*/
public abstract MediaStreamTarget getStreamTarget(MediaType mediaType);
/**
* Gets the XML namespace of the Jingle transport implemented by this
* <tt>TransportManagerJabberImpl</tt>.
*
* @return the XML namespace of the Jingle transport implemented by this
* <tt>TransportManagerJabberImpl</tt>
*/
public abstract String getXmlNamespace();
/**
* Returns the generation that our current candidates belong to.
*
@ -134,7 +157,7 @@ public abstract void startCandidateHarvest(
* @throws OperationFailedException if we fail to allocate a port number.
*/
public abstract void startCandidateHarvest(
List<ContentPacketExtension> ourOffer)
List<ContentPacketExtension> ourOffer)
throws OperationFailedException;
/**
@ -146,4 +169,39 @@ public abstract void startCandidateHarvest(
* a new instance) and that we have updated with transport lists.
*/
public abstract List<ContentPacketExtension> wrapupHarvest();
/**
* Looks through the <tt>cpExtList</tt> and returns the {@link
* ContentPacketExtension} with the specified name.
*
* @param cpExtList the list that we will be searching for a specific
* content.
* @param name the name of the content element we are looking for.
* @return the {@link ContentPacketExtension} with the specified name or
* <tt>null</tt> if no such content element exists.
*/
protected ContentPacketExtension findContentByName(
List<ContentPacketExtension> cpExtList,
String name)
{
for(ContentPacketExtension cpExt : cpExtList)
{
if(cpExt.getName().equals(name))
return cpExt;
}
return null;
}
/**
* Starts the connectivity establishment of this
* <tt>TransportManagerJabberImpl</tt> i.e. checks the connectivity between
* the local and the remote peers given the remote counterpart of the
* negotiation between them.
*
* @param remote the collection of <tt>ContentPacketExtension</tt>s which
* represents the remote counterpart of the negotiation between the local
* and the remote peer
*/
public abstract void startConnectivityEstablishment(
Collection<ContentPacketExtension> remote);
}

@ -173,7 +173,7 @@ public abstract class CallPeerMediaHandler<
* Indicates whether this handler has already started at least one of its
* streams, at least once.
*/
private boolean isStarted = false;
private boolean started = false;
/**
* Contains all dynamic payload type mappings that have been made for this
@ -387,19 +387,15 @@ public synchronized void close()
protected void closeStream(MediaType type)
{
if( type == MediaType.AUDIO)
{
setAudioStream(null);
}
else
{
setVideoStream(null);
}
this.getTransportManager().closeStreamConnector(type);
getTransportManager().closeStreamConnector(type);
// clears the zrtp controls used for current call.
// Clear the ZRTP controls used for the associated Call.
ZrtpControl zrtpCtrl = zrtpControls.get(type);
if (zrtpCtrl != null)
{
zrtpCtrl.cleanup();
@ -1126,34 +1122,35 @@ protected MediaStream initStream(StreamConnector connector,
List<RTPExtension> rtpExtensions)
throws OperationFailedException
{
MediaStream stream = null;
if (device.getMediaType() == MediaType.AUDIO)
stream = this.audioStream;
else
{
stream = this.videoStream;
}
MediaType mediaType = device.getMediaType();
MediaStream stream = getStream(mediaType);
if (stream == null)
{
if (logger.isTraceEnabled() && (mediaType != format.getMediaType()))
logger.trace("The media types of device and format differ.");
// check whether a control already exists
ZrtpControl control = zrtpControls.get(format.getMediaType());
ZrtpControl control = zrtpControls.get(mediaType);
MediaService mediaService
= ProtocolMediaActivator.getMediaService();
= ProtocolMediaActivator.getMediaService();
if(control == null)
stream = mediaService.createMediaStream(connector, device);
else
stream = mediaService.createMediaStream(
connector, device, control);
{
stream
= mediaService.createMediaStream(
connector, device, control);
}
}
else
{
//this is a reinit
}
return configureStream(
return
configureStream(
device, format, target, direction, rtpExtensions, stream);
}
@ -1209,7 +1206,7 @@ protected MediaStream configureStream( MediaDevice device,
if(peer.getCall().isDefaultEncrypted())
{
// we use the audio stream for master stream
// when using zrtp multistreams
// when using ZRTP multistreams.
ZrtpControl zrtpControl = stream.getZrtpControl();
zrtpControl.setZrtpListener(zrtpController);
@ -1220,9 +1217,9 @@ protected MediaStream configureStream( MediaDevice device,
}
/**
* Send empty UDP packet to target destination data/control ports
* in order to open port on NAT or RTP proxy if any. In order to be really
* efficient, this method should be called after we send our offer or answer
* Sends empty UDP packets to target destination data/control ports in order
* to open port on NAT or RTP proxy if any. In order to be really efficient,
* this method should be called after we send our offer or answer.
*
* @param target <tt>MediaStreamTarget</tt>
*/
@ -1536,16 +1533,15 @@ protected DynamicRTPExtensionsRegistry getRtpExtensionsRegistry()
*/
public boolean isStarted()
{
return isStarted;
return started;
}
/**
* Returns <tt>true</tt> if this handler has already started at least one
* of its streams, at least once, and <tt>false</tt> otherwise. If the
* handler is already started, this method has no effect.
* Starts this <tt>CallPeerMediaHandler</tt>. If it has already been
* started, does nothing.
*
* @throws IllegalStateException if this method is called without this
* handler having first seen a media description or having generate an
* handler having first seen a media description or having generated an
* offer.
*/
public void start()
@ -1555,21 +1551,22 @@ public void start()
return;
MediaStream stream = getStream(MediaType.AUDIO);
if ( stream != null && !stream.isStarted()
&& isLocalAudioTransmissionEnabled())
if ((stream != null)
&& !stream.isStarted()
&& isLocalAudioTransmissionEnabled())
{
stream.start();
}
stream = getStream(MediaType.VIDEO);
if ( stream != null && !stream.isStarted())
if ((stream != null) && !stream.isStarted())
{
stream.start();
// send empty packet to deblock some kind of RTP proxy to let just
// one user sends its video
if(stream instanceof VideoMediaStream
&& !isLocalVideoTransmissionEnabled())
if ((stream instanceof VideoMediaStream)
&& !isLocalVideoTransmissionEnabled())
{
sendHolePunchPacket(stream.getTarget());
}
@ -1606,9 +1603,11 @@ protected abstract void throwOperationFailedException( String message,
throws OperationFailedException;
/**
* Returns the transport manager that is handling our address management.
* Gets the <tt>TransportManager</tt> implementation handling our address
* management.
*
* @return the transport manager that is handling our address management.
* @return the <tt>TransportManager</tt> implementation handling our address
* management
*/
public abstract TransportManager<T> getTransportManager();
}

@ -8,6 +8,7 @@
import java.net.*;
import net.java.sip.communicator.service.configuration.*;
import net.java.sip.communicator.service.neomedia.*;
import net.java.sip.communicator.service.netaddr.*;
import net.java.sip.communicator.service.protocol.*;
@ -21,6 +22,7 @@
* or <tt>CallPeerJabberImpl</tt>
*
* @author Emil Ivov
* @author Lyubomir Marinov
*/
public abstract class TransportManager<U extends MediaAwareCallPeer<?, ?, ?>>
{
@ -28,8 +30,8 @@ public abstract class TransportManager<U extends MediaAwareCallPeer<?, ?, ?>>
* The <tt>Logger</tt> used by the <tt>TransportManager</tt>
* class and its instances for logging output.
*/
private static final Logger logger = Logger
.getLogger(TransportManager.class.getName());
private static final Logger logger
= Logger.getLogger(TransportManager.class);
/**
* The minimum port number that we'd like our RTP sockets to bind upon.
@ -48,21 +50,17 @@ public abstract class TransportManager<U extends MediaAwareCallPeer<?, ?, ?>>
protected static int nextMediaPortToTry = minMediaPort;
/**
* The RTP/RTCP socket couple that this media handler should use to send
* and receive audio flows through.
*/
private StreamConnector audioStreamConnector = null;
/**
* The RTP/RTCP socket couple that this media handler should use to send
* and receive video flows through.
* The {@link MediaAwareCallPeer} whose traffic we will be taking care of.
*/
private StreamConnector videoStreamConnector = null;
private U callPeer;
/**
* The {@link MediaAwareCallPeer} whose traffic we will be taking care of.
* The RTP/RTCP socket couples that this <tt>TransportManager</tt> uses to
* send and receive media flows through indexed by <tt>MediaType</tt>
* (ordinal).
*/
private U callPeer;
private final StreamConnector[] streamConnectors
= new StreamConnector[MediaType.values().length];
/**
* Creates a new instance of this transport manager, binding it to the
@ -83,8 +81,8 @@ protected TransportManager(U callPeer)
* been initialized for this <tt>mediaType</tt> yet or in case one
* of its underlying sockets has been closed.
*
* @param mediaType the MediaType that we'd like to create a connector for.
*
* @param mediaType the <tt>MediaType</tt> that we'd like to create a
* connector for.
* @return this media handler's <tt>StreamConnector</tt> for the specified
* <tt>mediaType</tt>.
*
@ -94,28 +92,18 @@ protected TransportManager(U callPeer)
public StreamConnector getStreamConnector(MediaType mediaType)
throws OperationFailedException
{
if (mediaType == MediaType.AUDIO)
{
if ( audioStreamConnector == null
|| audioStreamConnector.getDataSocket().isClosed()
|| audioStreamConnector.getControlSocket().isClosed())
{
audioStreamConnector = createStreamConnector();
}
StreamConnector streamConnector = streamConnectors[mediaType.ordinal()];
return audioStreamConnector;
}
else
if ((streamConnector == null)
|| streamConnector.getDataSocket().isClosed()
|| streamConnector.getControlSocket().isClosed())
{
if ( videoStreamConnector == null
|| videoStreamConnector.getDataSocket().isClosed()
|| videoStreamConnector.getControlSocket().isClosed())
{
videoStreamConnector = createStreamConnector();
}
return videoStreamConnector;
streamConnectors[mediaType.ordinal()]
= streamConnector
= createStreamConnector(mediaType);
}
return streamConnector;
}
/**
@ -127,31 +115,30 @@ public StreamConnector getStreamConnector(MediaType mediaType)
public void closeStreamConnector(MediaType mediaType)
{
StreamConnector connector
= (mediaType == MediaType.VIDEO)
? videoStreamConnector
: audioStreamConnector;
if(connector == null)
return;
= streamConnectors[mediaType.ordinal()];
synchronized(connector)
if(connector != null)
{
connector.getDataSocket().close();
connector.getControlSocket().close();
connector = null;
synchronized(connector)
{
connector.getDataSocket().close();
connector.getControlSocket().close();
streamConnectors[mediaType.ordinal()] = null;
}
}
}
/**
* Creates a media <tt>StreamConnector</tt>. The method takes into account
* the minimum and maximum media port boundaries.
*
* @param mediaType the <tt>MediaType</tt> of the stream for which a new
* <tt>StreamConnector</tt> is to be created
* @return a new <tt>StreamConnector</tt>.
*
* @throws OperationFailedException if we fail binding the the sockets.
*/
protected StreamConnector createStreamConnector()
protected StreamConnector createStreamConnector(MediaType mediaType)
throws OperationFailedException
{
NetworkAddressManagerService nam
@ -192,7 +179,8 @@ protected StreamConnector createStreamConnector()
{
throw new OperationFailedException(
"Failed to allocate the network ports necessary for the call.",
OperationFailedException.INTERNAL_ERROR, exc);
OperationFailedException.INTERNAL_ERROR,
exc);
}
//make sure that next time we don't try to bind on occupied ports
@ -201,11 +189,7 @@ protected StreamConnector createStreamConnector()
if (nextMediaPortToTry > maxMediaPort -1)// take RTCP into account.
nextMediaPortToTry = minMediaPort;
//create the RTCP socket
DefaultStreamConnector connector = new DefaultStreamConnector(
rtpSocket, rtcpSocket);
return connector;
return new DefaultStreamConnector(rtpSocket, rtcpSocket);
}
/**
@ -219,10 +203,12 @@ protected void initializePortNumbers()
maxMediaPort = 6000;
//then set to anything the user might have specified.
ConfigurationService configuration
= ProtocolMediaActivator.getConfigurationService();
String minPortNumberStr
= ProtocolMediaActivator.getConfigurationService()
.getString(OperationSetBasicTelephony
.MIN_MEDIA_PORT_NUMBER_PROPERTY_NAME);
= configuration.getString(
OperationSetBasicTelephony
.MIN_MEDIA_PORT_NUMBER_PROPERTY_NAME);
if (minPortNumberStr != null)
{
@ -239,8 +225,8 @@ protected void initializePortNumbers()
}
String maxPortNumberStr
= ProtocolMediaActivator.getConfigurationService()
.getString(OperationSetBasicTelephony
= configuration.getString(
OperationSetBasicTelephony
.MAX_MEDIA_PORT_NUMBER_PROPERTY_NAME);
if (maxPortNumberStr != null)
@ -270,7 +256,7 @@ protected void initializePortNumbers()
* descriptions so we already have a specific local address assigned to them
* at the time we get ready to create the c= and o= fields. It is therefore
* better to try and return one of these addresses before trying the net
* address manager again ang running the slight risk of getting a different
* address manager again and running the slight risk of getting a different
* address.
*
* @return an <tt>InetAddress</tt> that we use in one of the
@ -278,14 +264,17 @@ protected void initializePortNumbers()
*/
public InetAddress getLastUsedLocalHost()
{
if (audioStreamConnector != null)
return audioStreamConnector.getDataSocket().getLocalAddress();
for (MediaType mediaType : MediaType.values())
{
StreamConnector streamConnector
= streamConnectors[mediaType.ordinal()];
if (videoStreamConnector != null)
return videoStreamConnector.getDataSocket().getLocalAddress();
if (streamConnector != null)
return streamConnector.getDataSocket().getLocalAddress();
}
NetworkAddressManagerService nam
= ProtocolMediaActivator.getNetworkAddressManagerService();
= ProtocolMediaActivator.getNetworkAddressManagerService();
InetAddress intendedDestination = getIntendedDestination(getCallPeer());
return nam.getLocalHost(intendedDestination);

Loading…
Cancel
Save