From 9384b4fbbaf0746ba6e69fb56996148a727b354d Mon Sep 17 00:00:00 2001 From: Emil Ivov Date: Sun, 19 Sep 2010 10:53:11 +0000 Subject: ICE integration for XMPP accounts --- .../netaddr/NetworkAddressManagerServiceImpl.java | 6 +- .../jabber/CallPeerMediaHandlerJabberImpl.java | 2 +- .../protocol/jabber/IceUdpTransportManager.java | 63 ++++++++++++++++-- .../protocol/jabber/jinglesdp/JingleUtils.java | 76 ++++++++++++++++++++++ .../plugin/jabberaccregwizz/IceConfigPanel.java | 20 +++--- .../netaddr/NetworkAddressManagerService.java | 6 +- .../service/protocol/StunServerDescriptor.java | 16 ++--- .../service/protocol/media/TransportManager.java | 2 +- 8 files changed, 162 insertions(+), 29 deletions(-) (limited to 'src/net/java/sip') diff --git a/src/net/java/sip/communicator/impl/netaddr/NetworkAddressManagerServiceImpl.java b/src/net/java/sip/communicator/impl/netaddr/NetworkAddressManagerServiceImpl.java index f2724ea..11cbcab 100644 --- a/src/net/java/sip/communicator/impl/netaddr/NetworkAddressManagerServiceImpl.java +++ b/src/net/java/sip/communicator/impl/netaddr/NetworkAddressManagerServiceImpl.java @@ -603,9 +603,9 @@ public class NetworkAddressManagerServiceImpl * @throws BindException if we couldn't find a free port between within the * default number of retries. */ - public static IceMediaStream createStream( int rtpPort, - String streamName, - Agent agent) + public IceMediaStream createIceStream( int rtpPort, + String streamName, + Agent agent) throws IllegalArgumentException, IOException, BindException diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/CallPeerMediaHandlerJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/CallPeerMediaHandlerJabberImpl.java index 7552f1f..b8ceab5 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/CallPeerMediaHandlerJabberImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/CallPeerMediaHandlerJabberImpl.java @@ -71,7 +71,7 @@ public class CallPeerMediaHandlerJabberImpl { super(peer, peer); - transportManager = new RawUdpTransportManager(peer); + transportManager = new IceUdpTransportManager(peer); } /** diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/IceUdpTransportManager.java b/src/net/java/sip/communicator/impl/protocol/jabber/IceUdpTransportManager.java index f635325..f77336b 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/IceUdpTransportManager.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/IceUdpTransportManager.java @@ -7,15 +7,18 @@ package net.java.sip.communicator.impl.protocol.jabber; import java.io.*; +import java.net.*; import java.nio.charset.*; import java.text.*; import java.util.*; import net.java.sip.communicator.impl.protocol.jabber.extensions.jingle.*; import net.java.sip.communicator.impl.protocol.jabber.extensions.jingle.CandidateType; +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.*; import org.ice4j.*; import org.ice4j.ice.*; @@ -32,6 +35,13 @@ public class IceUdpTransportManager extends TransportManagerJabberImpl { /** + * The Logger used by the IceUdpTransportManager + * class and its instances for logging output. + */ + private static final Logger logger = Logger + .getLogger(IceUdpTransportManager.class.getName()); + + /** * This is where we keep our answer between the time we get the offer and * are ready with the answer; */ @@ -68,8 +78,7 @@ public class IceUdpTransportManager { ProtocolProviderServiceJabberImpl provider = getCallPeer().getProtocolProvider(); - NetworkAddressManagerService namSer - = JabberActivator.getNetworkAddressManagerService(); + NetworkAddressManagerService namSer = getNetAddrMgr(); Agent agent = namSer.createIceAgent(); @@ -190,16 +199,49 @@ public class IceUdpTransportManager = (RtpDescriptionPacketExtension)content .getFirstChildOfType(RtpDescriptionPacketExtension.class); - IceMediaStream stream = content.getName() + IceMediaStream stream; + try + { + //the following call involves STUN calls so it may take a while. + stream = getNetAddrMgr().createIceStream( + nextMediaPortToTry, rtpDesc.getMedia(), iceAgent); + } + catch (Exception exc) + { + throw new OperationFailedException( + "Failed to initialize stream " + rtpDesc.getMedia(), + OperationFailedException.INTERNAL_ERROR); + } + //let's now update the next port var as best we can: we would assume + //that all local candidates are bound on the same port and set it + //to the one just above. if the assumption is wrong the next bind + //would simply include one more bind retry. + try + { + nextMediaPortToTry = stream.getComponent(Component.RTCP) + .getLocalCandidates().get(0) + .getTransportAddress().getPort() + 1; + } + catch(Throwable t) + { + //hey, we were just trying to be nice. if that didn't work for + //some reason we really can't be held responsible! + logger.debug("Determining next port didn't work: ", t); + } + //we now generate the XMPP code containing the candidates. + content.addChildExtension(JingleUtils.createTransport(stream)); } + this.cpeList = ourOffer; } + + /** * 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 @@ -232,4 +274,17 @@ public class IceUdpTransportManager return null; } + + /** + * Returns a reference to the {@link NetworkAddressManagerService}. The only + * reason this method exists is that {@link JabberActivator + * #getNetworkAddressManagerService()} is too long to write and makes code + * look clumsy. + * + * @return a reference to the {@link NetworkAddressManagerService}. + */ + private static NetworkAddressManagerService getNetAddrMgr() + { + return JabberActivator.getNetworkAddressManagerService(); + } } diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/jinglesdp/JingleUtils.java b/src/net/java/sip/communicator/impl/protocol/jabber/jinglesdp/JingleUtils.java index 2099e7f..4513cef 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/jinglesdp/JingleUtils.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/jinglesdp/JingleUtils.java @@ -9,12 +9,17 @@ package net.java.sip.communicator.impl.protocol.jabber.jinglesdp; import java.net.*; import java.util.*; +import org.ice4j.*; +import org.ice4j.ice.*; + import net.java.sip.communicator.impl.protocol.jabber.*; import net.java.sip.communicator.impl.protocol.jabber.extensions.jingle.*; +import net.java.sip.communicator.impl.protocol.jabber.extensions.jingle.CandidateType; import net.java.sip.communicator.service.neomedia.*; import net.java.sip.communicator.service.neomedia.format.*; import net.java.sip.communicator.service.protocol.media.*; import net.java.sip.communicator.util.*; +import net.java.sip.communicator.util.NetworkUtils; //disambiguates with ice4j's network utils. import static net.java.sip.communicator.impl.protocol.jabber.extensions.jingle.ContentPacketExtension.*; /** @@ -502,4 +507,75 @@ public class JingleUtils return ptExt; } + /** + * Converts the ICE media stream and its local candidates into a + * {@link IceUdpTransportPacketExtension}. + * + * @param stream the {@link IceMediaStream} that we'd like to describe in + * XML. + * + * @return the {@link IceUdpTransportPacketExtension} that we + */ + public static IceUdpTransportPacketExtension createTransport( + IceMediaStream stream) + { + IceUdpTransportPacketExtension trans + = new IceUdpTransportPacketExtension(); + + trans.setUfrag(stream.getParentAgent().getLocalUfrag()); + trans.setPassword(stream.getParentAgent().getLocalPassword()); + + for(Component component : stream.getComponents()) + { + for(Candidate cand : component.getLocalCandidates()) + { + trans.addCandidate(createCandidate(cand)); + } + } + + return trans; + } + + + /** + * Creates a {@link CandidatePacketExtension} and initializes it so that it + * would describe the state of candidate + * + * @param candidate the ICE4J {@link Candidate} that we'd like to convert + * into an XMPP packet extension. + * + * @return a new {@link CandidatePacketExtension} corresponding to the state + * of the candidate candidate. + */ + private static CandidatePacketExtension createCandidate(Candidate candidate) + { + CandidatePacketExtension packet = new CandidatePacketExtension(); + + //TODO: XMPP expects int values as foundations. Luckily that's exactly + //what ice4j is using under the hood at this time. still, we'd need to + //make sure that doesn't change ... possibly by setting a property there + packet.setFoundation(Integer.parseInt( candidate.getFoundation())); + + packet.setComponent( candidate.getParentComponent().getComponentID()); + packet.setProtocol(candidate.getTransport().toString()); + packet.setPriority(candidate.getPriority()); + packet.setGeneration(candidate.getParentComponent() + .getParentStream().getParentAgent().getGeneration()); + + packet.setIP(candidate.getTransportAddress().getHostAddress()); + packet.setPort(candidate.getTransportAddress().getPort()); + packet.setType(CandidateType.valueOf(candidate.getType().toString())); + + TransportAddress relAddr = candidate.getRelatedAddress(); + + if(relAddr != null) + { + packet.setRelAddr(relAddr.getHostAddress()); + packet.setRelPort(relAddr.getPort()); + } + + + return packet; + } + } diff --git a/src/net/java/sip/communicator/plugin/jabberaccregwizz/IceConfigPanel.java b/src/net/java/sip/communicator/plugin/jabberaccregwizz/IceConfigPanel.java index 9463c60..64fe4d9 100644 --- a/src/net/java/sip/communicator/plugin/jabberaccregwizz/IceConfigPanel.java +++ b/src/net/java/sip/communicator/plugin/jabberaccregwizz/IceConfigPanel.java @@ -111,12 +111,14 @@ public class IceConfigPanel if (stunServer != null) { - StunConfigDialog dialog - = new StunConfigDialog( stunServer.getAddress(), - stunServer.getPort(), - stunServer.isTurnSupported(), - stunServer.getUsername(), - stunServer.getPassword()); + StunConfigDialog dialog = new StunConfigDialog( + stunServer.getAddress(), + stunServer.getPort(), + stunServer.isTurnSupported(), + StunServerDescriptor.getUTF8String( + stunServer.getUsername()), + StunServerDescriptor.getUTF8String( + stunServer.getPassword())); dialog.setVisible(true); } @@ -212,7 +214,7 @@ public class IceConfigPanel int port, boolean isSupportTurn, String username, - char[] password) + String password) { this(); @@ -220,7 +222,7 @@ public class IceConfigPanel portField.setText(Integer.toString( port )); supportTurnCheckBox.setSelected(isSupportTurn); usernameField.setText(username); - passwordField.setText(password.toString()); + passwordField.setText(password); } /** @@ -304,7 +306,7 @@ public class IceConfigPanel StunServerDescriptor stunServer = new StunServerDescriptor( address, port, supportTurnCheckBox.isSelected(), - username, password); + username, new String( password )); addStunServer(stunServer); diff --git a/src/net/java/sip/communicator/service/netaddr/NetworkAddressManagerService.java b/src/net/java/sip/communicator/service/netaddr/NetworkAddressManagerService.java index fb5b063..0893e33 100644 --- a/src/net/java/sip/communicator/service/netaddr/NetworkAddressManagerService.java +++ b/src/net/java/sip/communicator/service/netaddr/NetworkAddressManagerService.java @@ -191,9 +191,9 @@ public interface NetworkAddressManagerService * @throws BindException if we couldn't find a free port between within the * default number of retries. */ - public IceMediaStream createStream( int rtpPort, - String streamName, - Agent agent) + public IceMediaStream createIceStream( int rtpPort, + String streamName, + Agent agent) throws IllegalArgumentException, IOException, BindException; diff --git a/src/net/java/sip/communicator/service/protocol/StunServerDescriptor.java b/src/net/java/sip/communicator/service/protocol/StunServerDescriptor.java index 96ec91f..f637c16 100644 --- a/src/net/java/sip/communicator/service/protocol/StunServerDescriptor.java +++ b/src/net/java/sip/communicator/service/protocol/StunServerDescriptor.java @@ -67,14 +67,14 @@ public class StunServerDescriptor public StunServerDescriptor( String address, int port, boolean supportTurn, - byte[] username, - byte[] password) + String username, + String password) { this.address = address; this.port = port; this.isTurnSupported = supportTurn; - this.username = username; - this.password = password; + this.username = getUTF8Bytes( username ); + this.password = getUTF8Bytes( password ); } /** @@ -260,8 +260,8 @@ public class StunServerDescriptor new StunServerDescriptor( stunAddress, stunPort, stunIsTurnSupported, - getUTF8Bytes(stunUsername), - getUTF8Bytes(stunPassword)); + stunUsername, + stunPassword); return stunServer; } @@ -274,7 +274,7 @@ public class StunServerDescriptor * * @return string's bytes. */ - private static byte[] getUTF8Bytes(String string) + public static byte[] getUTF8Bytes(String string) { try { @@ -297,7 +297,7 @@ public class StunServerDescriptor * * @return the UTF-8 String. */ - private static String getUTF8String(byte[] bytes) + public static String getUTF8String(byte[] bytes) { try { diff --git a/src/net/java/sip/communicator/service/protocol/media/TransportManager.java b/src/net/java/sip/communicator/service/protocol/media/TransportManager.java index 8734e6c..c6986a3 100644 --- a/src/net/java/sip/communicator/service/protocol/media/TransportManager.java +++ b/src/net/java/sip/communicator/service/protocol/media/TransportManager.java @@ -45,7 +45,7 @@ public abstract class TransportManager> * The port that we should try to bind our next media stream's RTP socket * to. */ - private static int nextMediaPortToTry = minMediaPort; + protected static int nextMediaPortToTry = minMediaPort; /** * The RTP/RTCP socket couple that this media handler should use to send -- cgit v1.1