/** * Processes the session initiation {@link SessionIQ} that we were created with, passing its * content to the media handler and then sends either a "session-info/ringing" or a "terminate" * response. * * @param sessionInitIQ The {@link SessionIQ} that created the session that we are handling here. */ protected synchronized void processSessionInitiate(SessionIQ sessionInitIQ) { // Do initiate the session. this.sessionInitIQ = sessionInitIQ; this.initiator = true; RtpDescriptionPacketExtension description = null; for (PacketExtension ext : sessionInitIQ.getExtensions()) { if (ext.getElementName().equals(RtpDescriptionPacketExtension.ELEMENT_NAME)) { description = (RtpDescriptionPacketExtension) ext; break; } } if (description == null) { logger.info("No description in incoming session initiate"); // send an error response; String reasonText = "Error: no description"; SessionIQ errResp = GTalkPacketFactory.createSessionTerminate( sessionInitIQ.getTo(), sessionInitIQ.getFrom(), sessionInitIQ.getID(), Reason.INCOMPATIBLE_PARAMETERS, reasonText); setState(CallPeerState.FAILED, reasonText); getProtocolProvider().getConnection().sendPacket(errResp); return; } try { getMediaHandler().processOffer(description); } catch (Exception ex) { logger.info("Failed to process an incoming session initiate", ex); // send an error response; String reasonText = "Error: " + ex.getMessage(); SessionIQ errResp = GTalkPacketFactory.createSessionTerminate( sessionInitIQ.getTo(), sessionInitIQ.getFrom(), sessionInitIQ.getID(), Reason.INCOMPATIBLE_PARAMETERS, reasonText); setState(CallPeerState.FAILED, reasonText); getProtocolProvider().getConnection().sendPacket(errResp); return; } // If we do not get the info about the remote peer yet. Get it right // now. if (this.getDiscoveryInfo() == null) { String calleeURI = sessionInitIQ.getFrom(); retrieveDiscoveryInfo(calleeURI); } }
/** * Ends the call with for this <tt>CallPeer</tt>. Depending on the state of the peer the method * would send a CANCEL, BYE, or BUSY_HERE message and set the new state to DISCONNECTED. * * @param failed indicates if the hangup is following to a call failure or simply a disconnect * @param reasonText the text, if any, to be set on the <tt>ReasonPacketExtension</tt> as the * value of its * @param reasonOtherExtension the <tt>PacketExtension</tt>, if any, to be set on the * <tt>ReasonPacketExtension</tt> as the value of its <tt>otherExtension</tt> property */ public void hangup(boolean failed, String reasonText, PacketExtension reasonOtherExtension) { // do nothing if the call is already ended if (CallPeerState.DISCONNECTED.equals(getState()) || CallPeerState.FAILED.equals(getState())) { if (logger.isDebugEnabled()) logger.debug("Ignoring a request to hangup a call peer " + "that is already DISCONNECTED"); return; } CallPeerState prevPeerState = getState(); getMediaHandler().getTransportManager().close(); if (failed) setState(CallPeerState.FAILED, reasonText); else setState(CallPeerState.DISCONNECTED, reasonText); SessionIQ responseIQ = null; if (prevPeerState.equals(CallPeerState.CONNECTED) || CallPeerState.isOnHold(prevPeerState)) { responseIQ = GTalkPacketFactory.createBye(getProtocolProvider().getOurJID(), peerJID, getSID()); responseIQ.setInitiator(isInitiator() ? getAddress() : getProtocolProvider().getOurJID()); } else if (CallPeerState.CONNECTING.equals(prevPeerState) || CallPeerState.CONNECTING_WITH_EARLY_MEDIA.equals(prevPeerState) || CallPeerState.ALERTING_REMOTE_SIDE.equals(prevPeerState)) { responseIQ = GTalkPacketFactory.createCancel(getProtocolProvider().getOurJID(), peerJID, getSID()); responseIQ.setInitiator(isInitiator() ? getAddress() : getProtocolProvider().getOurJID()); } else if (prevPeerState.equals(CallPeerState.INCOMING_CALL)) { responseIQ = GTalkPacketFactory.createBusy(getProtocolProvider().getOurJID(), peerJID, getSID()); responseIQ.setInitiator(isInitiator() ? getAddress() : getProtocolProvider().getOurJID()); } else if (prevPeerState.equals(CallPeerState.BUSY) || prevPeerState.equals(CallPeerState.FAILED)) { // For FAILED and BUSY we only need to update CALL_STATUS // as everything else has been done already. } else { logger.info("Could not determine call peer state!"); } if (responseIQ != null) { if (reasonOtherExtension != null) { ReasonPacketExtension reason = (ReasonPacketExtension) responseIQ.getExtension( ReasonPacketExtension.ELEMENT_NAME, ReasonPacketExtension.NAMESPACE); if (reason == null) { if (reasonOtherExtension instanceof ReasonPacketExtension) { responseIQ.setReason((ReasonPacketExtension) reasonOtherExtension); } } else reason.setOtherExtension(reasonOtherExtension); } getProtocolProvider().getConnection().sendPacket(responseIQ); } }
/** * Process candidates received. * * @param sessionInitIQ The {@link SessionIQ} that created the session we are handling here */ public void processCandidates(SessionIQ sessionInitIQ) { Collection<PacketExtension> extensions = sessionInitIQ.getExtensions(); List<GTalkCandidatePacketExtension> candidates = new ArrayList<GTalkCandidatePacketExtension>(); for (PacketExtension ext : extensions) { if (ext.getElementName().equalsIgnoreCase(GTalkCandidatePacketExtension.ELEMENT_NAME)) { GTalkCandidatePacketExtension cand = (GTalkCandidatePacketExtension) ext; candidates.add(cand); } } try { getMediaHandler().processCandidates(candidates); } catch (OperationFailedException ofe) { logger.warn("Failed to process an incoming candidates", ofe); // send an error response String reasonText = "Error: " + ofe.getMessage(); SessionIQ errResp = GTalkPacketFactory.createSessionTerminate( sessionInitIQ.getTo(), sessionInitIQ.getFrom(), sessionInitIQ.getID(), Reason.GENERAL_ERROR, reasonText); getMediaHandler().getTransportManager().close(); setState(CallPeerState.FAILED, reasonText); getProtocolProvider().getConnection().sendPacket(errResp); return; } // HACK for FreeSwitch that send accept message before sending // candidates if (sessAcceptedWithNoCands != null) { if (isInitiator()) { try { answer(); } catch (OperationFailedException e) { logger.info("Failed to answer call (FreeSwitch hack)"); } } else { final SessionIQ sess = sessAcceptedWithNoCands; sessAcceptedWithNoCands = null; // run in another thread to not block smack receive thread and // possibly delay others candidates messages. new Thread() { @Override public void run() { processSessionAccept(sess); } }.start(); } sessAcceptedWithNoCands = null; } }
/** * Processes the session initiation {@link SessionIQ} that we were created with, passing its * content to the media handler. * * @param sessionInitIQ The {@link SessionIQ} that created the session that we are handling here. */ public void processSessionAccept(SessionIQ sessionInitIQ) { this.sessionInitIQ = sessionInitIQ; CallPeerMediaHandlerGTalkImpl mediaHandler = getMediaHandler(); Collection<PacketExtension> extensions = sessionInitIQ.getExtensions(); RtpDescriptionPacketExtension answer = null; for (PacketExtension ext : extensions) { if (ext.getElementName().equalsIgnoreCase(RtpDescriptionPacketExtension.ELEMENT_NAME)) { answer = (RtpDescriptionPacketExtension) ext; break; } } try { mediaHandler.getTransportManager().wrapupConnectivityEstablishment(); mediaHandler.processAnswer(answer); } catch (IllegalArgumentException e) { // HACK for FreeSwitch that send accept message before sending // candidates sessAcceptedWithNoCands = sessionInitIQ; return; } catch (Exception exc) { if (logger.isInfoEnabled()) logger.info("Failed to process a session-accept", exc); // send an error response String reasonText = "Error: " + exc.getMessage(); SessionIQ errResp = GTalkPacketFactory.createSessionTerminate( sessionInitIQ.getTo(), sessionInitIQ.getFrom(), sessionInitIQ.getID(), Reason.GENERAL_ERROR, reasonText); getMediaHandler().getTransportManager().close(); setState(CallPeerState.FAILED, reasonText); getProtocolProvider().getConnection().sendPacket(errResp); return; } // tell everyone we are connecting so that the audio notifications would // stop setState(CallPeerState.CONNECTED); mediaHandler.start(); }
/** * Initiate a Google Talk session {@link SessionIQ}. * * @param sessionInitiateExtensions a collection of additional and optional * <tt>PacketExtension</tt>s to be added to the <tt>initiate</tt> {@link SessionIQ} which is * to initiate the session with this <tt>CallPeerGTalkImpl</tt> * @throws OperationFailedException exception */ protected synchronized void initiateSession(Iterable<PacketExtension> sessionInitiateExtensions) throws OperationFailedException { sid = SessionIQ.generateSID(); initiator = false; // Create the media description that we'd like to send to the other side. RtpDescriptionPacketExtension offer = getMediaHandler().createDescription(); ProtocolProviderServiceJabberImpl protocolProvider = getProtocolProvider(); sessionInitIQ = GTalkPacketFactory.createSessionInitiate( protocolProvider.getOurJID(), this.peerJID, sid, offer); if (sessionInitiateExtensions != null) { for (PacketExtension sessionInitiateExtension : sessionInitiateExtensions) { sessionInitIQ.addExtension(sessionInitiateExtension); } } protocolProvider.getConnection().sendPacket(sessionInitIQ); // for Google Voice JID without resource we do not harvest and send // candidates if (getAddress().endsWith(ProtocolProviderServiceJabberImpl.GOOGLE_VOICE_DOMAIN)) { return; } getMediaHandler() .harvestCandidates( offer.getPayloadTypes(), new CandidatesSender() { public void sendCandidates(Iterable<GTalkCandidatePacketExtension> candidates) { CallPeerGTalkImpl.this.sendCandidates(candidates); } }); }
/** * Indicates a user request to answer an incoming call from this <tt>CallPeer</tt>. * * <p>Sends an OK response to <tt>callPeer</tt>. Make sure that the call peer contains an SDP * description when you call this method. * * @throws OperationFailedException if we fail to create or send the response. */ public synchronized void answer() throws OperationFailedException { RtpDescriptionPacketExtension answer = null; try { getMediaHandler().getTransportManager().wrapupConnectivityEstablishment(); answer = getMediaHandler().generateSessionAccept(true); } catch (IllegalArgumentException e) { sessAcceptedWithNoCands = new SessionIQ(); // HACK apparently FreeSwitch need to have accept before answer = getMediaHandler().generateSessionAccept(false); SessionIQ response = GTalkPacketFactory.createSessionAccept( sessionInitIQ.getTo(), sessionInitIQ.getFrom(), getSID(), answer); getProtocolProvider().getConnection().sendPacket(response); return; } catch (Exception exc) { logger.info("Failed to answer an incoming call", exc); // send an error response String reasonText = "Error: " + exc.getMessage(); SessionIQ errResp = GTalkPacketFactory.createSessionTerminate( sessionInitIQ.getTo(), sessionInitIQ.getFrom(), sessionInitIQ.getID(), Reason.FAILED_APPLICATION, reasonText); setState(CallPeerState.FAILED, reasonText); getProtocolProvider().getConnection().sendPacket(errResp); return; } SessionIQ response = GTalkPacketFactory.createSessionAccept( sessionInitIQ.getTo(), sessionInitIQ.getFrom(), getSID(), answer); // send the packet first and start the stream later in case the media // relay needs to see it before letting hole punching techniques through. if (sessAcceptedWithNoCands == null) getProtocolProvider().getConnection().sendPacket(response); try { getMediaHandler().start(); } catch (UndeclaredThrowableException e) { Throwable exc = e.getUndeclaredThrowable(); logger.info("Failed to establish a connection", exc); // send an error response String reasonText = "Error: " + exc.getMessage(); SessionIQ errResp = GTalkPacketFactory.createSessionTerminate( sessionInitIQ.getTo(), sessionInitIQ.getFrom(), sessionInitIQ.getID(), Reason.GENERAL_ERROR, reasonText); getMediaHandler().getTransportManager().close(); setState(CallPeerState.FAILED, reasonText); getProtocolProvider().getConnection().sendPacket(errResp); return; } // tell everyone we are connecting so that the audio notifications would // stop setState(CallPeerState.CONNECTED); }