package net.sourceforge.gjtapi.raw.mjsip.ua;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Enumeration;
import java.util.Vector;
import java.util.logging.Logger;
import local.media.AudioClipPlayer;
import local.ua.MediaLauncher;
import local.ua.VICLauncher;
import org.zoolu.sdp.AttributeField;
import org.zoolu.sdp.ConnectionField;
import org.zoolu.sdp.MediaDescriptor;
import org.zoolu.sdp.MediaField;
import org.zoolu.sdp.SessionDescriptor;
import org.zoolu.sdp.TimeField;
import org.zoolu.sip.address.NameAddress;
import org.zoolu.sip.call.Call;
import org.zoolu.sip.call.CallListenerAdapter;
import org.zoolu.sip.call.ExtendedCall;
import org.zoolu.sip.call.SdpTools;
import org.zoolu.sip.header.StatusLine;
import org.zoolu.sip.message.Message;
import org.zoolu.sip.provider.SipProvider;
import org.zoolu.sip.provider.SipStack;
import org.zoolu.tools.Log;
import org.zoolu.tools.LogLevel;
import org.zoolu.tools.Parser;
/** Simple SIP user agent (UA).
* It includes audio/video applications.
* <p>
* It can use external audio/video tools as media applications.
* Currently only RAT (Robust Audio Tool) and VIC are supported as external applications.
*/
public class UserAgent extends CallListenerAdapter {
/** Logger instance. */
private static final Logger LOGGER =
Logger.getLogger(UserAgent.class.getName());
/** Event logger. */
Log log;
/** UserAgentProfile */
protected UserAgentProfile user_profile;
/** SipProvider */
protected SipProvider sip_provider;
/** Call */
//Call call;
protected ExtendedCall call;
/** Call transfer */
protected ExtendedCall call_transfer;
/** Audio application */
protected MediaLauncher audio_app=null;
/** Video application */
protected MediaLauncher video_app=null;
/** Local sdp */
protected String local_session=null;
/** UserAgent listener */
protected UserAgentListener listener=null;
/** Media file path */
final String MEDIA_PATH="media/local/ua/";
/** On wav file */
final String CLIP_ON=MEDIA_PATH+"on.wav";
/** Off wav file */
final String CLIP_OFF=MEDIA_PATH+"off.wav";
/** Ring wav file */
final String CLIP_RING=MEDIA_PATH+"ring.wav";
/** Ring sound */
AudioClipPlayer clip_ring;
/** On sound */
AudioClipPlayer clip_on;
/** Off sound */
AudioClipPlayer clip_off;
// *********************** Startup Configuration ***********************
/** UA_IDLE=0 */
static final String UA_IDLE="IDLE";
/** UA_INCOMING_CALL=1 */
static final String UA_INCOMING_CALL="INCOMING_CALL";
/** UA_OUTGOING_CALL=2 */
static final String UA_OUTGOING_CALL="OUTGOING_CALL";
/** UA_ONCALL=3 */
static final String UA_ONCALL="ONCALL";
/** Call state
* <P>UA_IDLE=0, <BR>UA_INCOMING_CALL=1, <BR>UA_OUTGOING_CALL=2, <BR>UA_ONCALL=3 */
String call_state=UA_IDLE;
// *************************** Basic methods ***************************
/** Changes the call state */
protected void changeStatus(String state)
{ call_state=state;
//printLog("state: "+call_state,LogLevel.MEDIUM);
}
/** Checks the call state */
protected boolean statusIs(String state)
{ return call_state.equals(state);
}
/** Gets the call state */
protected String getStatus()
{ return call_state;
}
/** Sets the automatic answer time (default is -1 that means no auto accept mode) */
public void setAcceptTime(int accept_time)
{ user_profile.accept_time=accept_time;
}
/** Sets the automatic hangup time (default is 0, that corresponds to manual hangup mode) */
public void setHangupTime(int time)
{ user_profile.hangup_time=time;
}
/** Sets the redirection url (default is null, that is no redircetion) */
public void setRedirection(String url)
{ user_profile.redirect_to=url;
}
/** Sets the no offer mode for the invite (default is false) */
public void setNoOfferMode(boolean nooffer)
{ user_profile.no_offer=nooffer;
}
/** Enables audio */
public void setAudio(boolean enable)
{ user_profile.audio=enable;
}
/** Enables video */
public void setVideo(boolean enable)
{ user_profile.video=enable;
}
/** Sets the receive only mode */
public void setReceiveOnlyMode(boolean r_only)
{ user_profile.recv_only=r_only;
}
/** Sets the send only mode */
public void setSendOnlyMode(boolean s_only)
{ user_profile.send_only=s_only;
}
/** Sets the send tone mode */
public void setSendToneMode(boolean s_tone)
{ user_profile.send_tone=s_tone;
}
/** Sets the send file */
public void setSendFile(String file_name)
{ user_profile.send_file=file_name;
}
/** Sets the recv file */
public void setRecvFile(String file_name)
{ user_profile.recv_file=file_name;
}
/** Sets the send stream */
public void setSendStream(InputStream stream)
{ user_profile.send_Stream=stream;
}
/** Sets the recv stream */
public void setRecvStream(OutputStream stream)
{ user_profile.recv_Stream=stream;
}
/** Gets the local SDP */
public String getSessionDescriptor()
{ return local_session;
}
/** Sets the local SDP */
public void setSessionDescriptor(String sdp)
{ local_session=sdp;
}
/** Inits the local SDP (no media spec) */
public void initSessionDescriptor()
{ SessionDescriptor sdp=new SessionDescriptor(user_profile.from_url,sip_provider.getViaAddress());
local_session=sdp.toString();
}
/** Adds a media to the SDP */
public void addMediaDescriptor(String media, int port, int avp, String codec, int rate)
{ if (local_session==null) initSessionDescriptor();
SessionDescriptor sdp=new SessionDescriptor(local_session);
String attr_param=String.valueOf(avp);
if (codec!=null) attr_param+=" "+codec+"/"+rate;
sdp.addMedia(new MediaField(media,port,0,"RTP/AVP",String.valueOf(avp)),new AttributeField("rtpmap",attr_param));
local_session=sdp.toString();
}
// *************************** Public Methods **************************
/** Costructs a UA with a default media port */
public UserAgent(SipProvider sip_provider, UserAgentProfile user_profile, UserAgentListener listener)
{ this.sip_provider=sip_provider;
log=sip_provider.getLog();
this.listener=listener;
this.user_profile=user_profile;
// if no contact_url and/or from_url has been set, create it now
user_profile.initContactAddress(sip_provider);
// load sounds
// ################# patch to make audio working with javax.sound.. #################
// currently AudioSender must be started before any AudioClipPlayer is initialized,
// since there is a problem with the definition of the audio format
//TODO Causa erro ao ser executado num PC sem placa de som
if (!user_profile.use_rat && !user_profile.use_jmf)
{ if (user_profile.audio && !user_profile.recv_only && user_profile.send_file==null && !user_profile.send_tone) local.media.AudioInput.initAudioLine();
if (user_profile.audio && !user_profile.send_only && user_profile.recv_file==null) local.media.AudioOutput.initAudioLine();
}
// ################# patch to make rat working.. #################
// in case of rat, do not load and play audio clips
if (!user_profile.use_rat)
{ try
{ String jar_file=user_profile.ua_jar;
// clip_on=new AudioClipPlayer(Archive.getAudioInputStream(Archive.getJarURL(jar_file,CLIP_ON)),null);
// clip_off=new AudioClipPlayer(Archive.getAudioInputStream(Archive.getJarURL(jar_file,CLIP_OFF)),null);
// clip_ring=new AudioClipPlayer(Archive.getAudioInputStream(Archive.getJarURL(jar_file,CLIP_RING)),null);
}
catch (Exception e)
{ printException(e,LogLevel.HIGH);
}
//clip_ring=new AudioClipPlayer(CLIP_RING,null);
//clip_on=new AudioClipPlayer(CLIP_ON,null);
//clip_off=new AudioClipPlayer(CLIP_OFF,null);
}
// set local sdp
initSessionDescriptor();
if (user_profile.audio || !user_profile.video) addMediaDescriptor("audio",user_profile.audio_port,user_profile.audio_avp,user_profile.audio_codec,user_profile.audio_sample_rate);
if (user_profile.video) addMediaDescriptor("video",user_profile.video_port,user_profile.video_avp,null,0);
}
/** Creates a new session descriptor */
/*private void newSession(int media_port)
{ SessionDescriptor local_sdp=new SessionDescriptor(user_profile.from_url,sip_provider.getAddress());
int audio_port=media_port;
int video_port=media_port+2;
//PATCH [040902] if (audio || !video) local_sdp.addMedia(new MediaField("audio",audio_port,0,"RTP/AVP","0"),new AttributeField("rtpmap","0 PCMU/8000"));
//PATCH [040902] if (video || !(audio || video)) local_sdp.addMedia(new MediaField("video",video_port,0,"RTP/AVP","7"),new AttributeField("rtpmap","17"));
local_sdp.addMedia(new MediaField("audio",audio_port,0,"RTP/AVP","0"),new AttributeField("rtpmap","0 PCMU/8000"));
local_session=local_sdp.toString();
}*/
/** Makes a new call (acting as UAC). */
public void call(String target_url) {
changeStatus(UA_OUTGOING_CALL);
call = new ExtendedCall(sip_provider, user_profile.from_url,
user_profile.contact_url, user_profile.username,
user_profile.realm, user_profile.passwd, this);
// in case of incomplete url (e.g. only 'user' is present), try to complete it
target_url=sip_provider.completeNameAddress(target_url).toString();
if (user_profile.no_offer) {
call.call(target_url);
} else {
call.call(target_url,local_session);
}
}
/** Waits for an incoming call (acting as UAS). */
public void listen()
{ changeStatus(UA_IDLE);
call=new ExtendedCall(sip_provider,user_profile.from_url,user_profile.contact_url,user_profile.username,user_profile.realm,user_profile.passwd,this);
call.listen();
}
/** Closes an ongoing, incoming, or pending call */
public void hangup()
{ if (clip_ring!=null) clip_ring.stop();
closeMediaApplication();
if (call!=null) call.hangup();
changeStatus(UA_IDLE);
}
/** Closes an ongoing, incoming, or pending call */
public void accept()
{ if (clip_ring!=null) clip_ring.stop();
if (call!=null) call.accept(local_session);
}
/** Redirects an incoming call */
public void redirect(String redirection)
{ if (clip_ring!=null) clip_ring.stop();
if (call!=null) call.redirect(redirection);
}
public void waitForEndOfMedia() {
if (audio_app != null) {
if (audio_app instanceof JAudioLauncher) {
((JAudioLauncher)audio_app).waitForEndOfMedia();
}
}
else if (video_app != null) {
}
}
public void waitForEndOfPlay() {
if (audio_app != null) {
if (audio_app instanceof JAudioLauncher) {
((JAudioLauncher)audio_app).waitForEndOfPlay();
}
}
}
public void waitForEndOfRecord() {
if (audio_app != null) {
if (audio_app instanceof JAudioLauncher) {
((JAudioLauncher)audio_app).waitForEndOfRecord();
}
}
}
/** Launches the Media Application (currently, the RAT audio tool) */
protected void launchMediaApplication()
{
// exit if the Media Application is already running
if (audio_app!=null || video_app!=null) {
LOGGER.info("DEBUG: media application is already running");
return;
}
SessionDescriptor local_sdp=new SessionDescriptor(call.getLocalSessionDescriptor());
String local_media_address=(new Parser(local_sdp.getConnection().toString())).skipString().skipString().getString();
int local_audio_port=0;
int local_video_port=0;
// parse local sdp
for (Enumeration e=local_sdp.getMediaDescriptors().elements(); e.hasMoreElements(); )
{ MediaField media=((MediaDescriptor)e.nextElement()).getMedia();
if (media.getMedia().equals("audio"))
local_audio_port=media.getPort();
if (media.getMedia().equals("video"))
local_video_port=media.getPort();
}
// parse remote sdp
SessionDescriptor remote_sdp=new SessionDescriptor(call.getRemoteSessionDescriptor());
String remote_media_address=(new Parser(remote_sdp.getConnection().toString())).skipString().skipString().getString();
int remote_audio_port=0;
int remote_video_port=0;
for (Enumeration e=remote_sdp.getMediaDescriptors().elements(); e.hasMoreElements(); )
{ MediaField media=((MediaDescriptor)e.nextElement()).getMedia();
if (media.getMedia().equals("audio"))
remote_audio_port=media.getPort();
if (media.getMedia().equals("video"))
remote_video_port=media.getPort();
}
// select the media direction (send_only, recv_ony, fullduplex)
int dir=0;
if (user_profile.recv_only) dir=-1;
else
if (user_profile.send_only) dir=1;
if (user_profile.audio && local_audio_port!=0 && remote_audio_port!=0){ // create an audio_app and start it
if (audio_app==null && (user_profile.send_Stream==null && user_profile.recv_Stream==null))
{ // for testing..
String audio_in=null;
if (user_profile.send_tone) audio_in=JAudioLauncher.TONE;
else if (user_profile.send_file!=null) audio_in=user_profile.send_file;
String audio_out=null;
//if (user_profile.recv_file!=null) audio_out=user_profile.recv_file;
//audio_app=new JAudioLauncher(local_audio_port,remote_media_address,remote_audio_port,dir,log);
//audio_app=new JAudioLauncher(local_audio_port,remote_media_address,remote_audio_port,dir,audio_in,audio_out,user_profile.audio_sample_rate,user_profile.audio_sample_size,user_profile.audio_frame_size,log);
}
else{
audio_app=new JAudioLauncher(local_audio_port,remote_media_address,remote_audio_port,dir,user_profile.send_Stream,user_profile.recv_Stream,user_profile.audio_sample_rate,user_profile.audio_sample_size,user_profile.audio_frame_size,log);
}
audio_app.startMedia();
}
if (user_profile.video && local_video_port!=0 && remote_video_port!=0)
{ // create a video_app and start it
if (user_profile.use_vic)
{ video_app=new VICLauncher(user_profile.bin_vic,local_video_port,remote_media_address,remote_video_port,log);
}
else
if (user_profile.use_jmf)
{ // try to use JMF video app
try
{ Class myclass=Class.forName("local.ua.JMFVideoLauncher");
Class[] parameter_types={ java.lang.Integer.TYPE, Class.forName("java.lang.String"), java.lang.Integer.TYPE, java.lang.Integer.TYPE, Class.forName("org.zoolu.tools.Log") };
Object[] parameters={ new Integer(local_video_port), remote_media_address, new Integer(remote_video_port), new Integer(dir), log };
java.lang.reflect.Constructor constructor=myclass.getConstructor(parameter_types);
video_app=(MediaLauncher)constructor.newInstance(parameters);
}
catch (Exception e) {
printException(e,LogLevel.HIGH);
LOGGER.warning("Error trying to create the JMFVideoLauncher "
+ e.getMessage());
}
}
// else
if (video_app==null) {
LOGGER.info("No external video application nor JMF has been provided: Video not started");
return;
}
video_app.startMedia();
}
}
/** Close the Media Application */
protected void closeMediaApplication()
{ if (audio_app!=null)
{ audio_app.stopMedia();
audio_app=null;
}
if (video_app!=null)
{ video_app.stopMedia();
video_app=null;
}
}
// ********************** Call callback functions **********************
/** Callback function called when arriving a new INVITE method (incoming call) */
public void onCallIncoming(Call call, NameAddress callee,
NameAddress caller, String sdp, Message invite) {
LOGGER.fine("onCallIncoming()");
if (call != this.call) {
LOGGER.fine("NOT the current call");
return;
}
LOGGER.info("INCOMING");
//System.out.println("DEBUG: inside UserAgent.onCallIncoming(): sdp=\n"+
// sdp);
changeStatus(UA_INCOMING_CALL);
call.ring();
if (sdp != null) { // Create the new SDP
SessionDescriptor remote_sdp = new SessionDescriptor(sdp);
SessionDescriptor local_sdp = new SessionDescriptor(local_session);
SessionDescriptor new_sdp = new SessionDescriptor(remote_sdp
.getOrigin(), remote_sdp.getSessionName(), local_sdp
.getConnection(), local_sdp.getTime());
new_sdp.addMediaDescriptors(local_sdp.getMediaDescriptors());
new_sdp = SdpTools.sdpMediaProduct(new_sdp, remote_sdp
.getMediaDescriptors());
new_sdp = SdpTools.sdpAttirbuteSelection(new_sdp, "rtpmap");
local_session = new_sdp.toString();
}
// play "ring" sound
if (clip_ring != null) {
clip_ring.loop();
}
if (listener != null) {
listener.onUaCallIncoming(this, callee, caller);
}
}
/** Callback function called when arriving a new Re-INVITE method (re-inviting/call modify) */
public void onCallModifying(Call call, String sdp, Message invite) {
LOGGER.fine("onCallModifying()");
if (call!=this.call) {
LOGGER.fine("NOT the current call");
return;
}
LOGGER.info("RE-INVITE/MODIFY");
// to be implemented.
// currently it simply accepts the session changes (see method onCallModifying() in CallListenerAdapter)
super.onCallModifying(call,sdp,invite); //original
}
/** Callback function that may be overloaded (extended). Called when arriving a 180 Ringing */
public void onCallRinging(Call call, Message resp) {
LOGGER.fine("onCallRinging()");
if (call!=this.call && call!=call_transfer) {
LOGGER.fine("NOT the current call");
return;
}
LOGGER.info("RINGING");
// play "on" sound
if (clip_on!=null) {
clip_on.replay();
}
if (listener!=null) {
listener.onUaCallRinging(this);
}
}
/** Callback function called when arriving a 2xx (call accepted) */
public void onCallAccepted(Call call, String sdp, Message resp) {
LOGGER.fine("onCallAccepted()");
if (call!=this.call && call!=call_transfer) {
LOGGER.fine("NOT the current call");
return;
}
LOGGER.info("ACCEPTED/CALL");
changeStatus(UA_ONCALL);
if (user_profile.no_offer)
{ // Create the new SDP
SessionDescriptor remote_sdp=new SessionDescriptor(sdp);
SessionDescriptor local_sdp=new SessionDescriptor(local_session);
SessionDescriptor new_sdp=new SessionDescriptor(remote_sdp.getOrigin(),remote_sdp.getSessionName(),local_sdp.getConnection(),local_sdp.getTime());
new_sdp.addMediaDescriptors(local_sdp.getMediaDescriptors());
new_sdp=SdpTools.sdpMediaProduct(new_sdp,remote_sdp.getMediaDescriptors());
new_sdp=SdpTools.sdpAttirbuteSelection(new_sdp,"rtpmap");
// update the local SDP
local_session=new_sdp.toString();
// answer with the local sdp
call.ackWithAnswer(local_session);
}
// play "on" sound
if (clip_on!=null) clip_on.replay();
if (listener!=null) listener.onUaCallAccepted(this);
//TODO
launchMediaApplication();
if (call==call_transfer)
{ StatusLine status_line=resp.getStatusLine();
int code=status_line.getCode();
String reason=status_line.getReason();
this.call.notify(code,reason);
}
}
/** Callback function called when arriving an ACK method (call confirmed) */
public void onCallConfirmed(Call call, String sdp, Message ack) {
LOGGER.fine("onCallConfirmed()");
if (call!=this.call) {
LOGGER.fine("NOT the current call");
return;
}
LOGGER.info("CONFIRMED/CALL");
changeStatus(UA_ONCALL);
// play "on" sound
if (clip_on!=null) clip_on.replay();
if (listener!=null) listener.onUaCallAccepted(this);
//TODO
launchMediaApplication();
if (user_profile.hangup_time>0) this.automaticHangup(user_profile.hangup_time);
}
/** Callback function called when arriving a 2xx (re-invite/modify accepted) */
public void onCallReInviteAccepted(Call call, String sdp, Message resp) {
LOGGER.fine("onCallReInviteAccepted()");
if (call!=this.call) {
LOGGER.fine("NOT the current call");
return;
}
LOGGER.info("RE-INVITE-ACCEPTED/CALL");
}
/** Callback function called when arriving a 4xx (re-invite/modify failure) */
public void onCallReInviteRefused(Call call, String reason, Message resp) {
LOGGER.fine("onCallReInviteRefused()");
if (call!=this.call) {
LOGGER.fine("NOT the current call");
return;
}
LOGGER.info("RE-INVITE-REFUSED ("+reason+")/CALL");
if (listener!=null) {
listener.onUaCallFailed(this);
}
}
/** Callback function called when arriving a 4xx (call failure) */
public void onCallRefused(Call call, String reason, Message resp) {
LOGGER.fine("onCallRefused()");
if (call!=this.call) {
LOGGER.fine("NOT the current call");
return;
}
LOGGER.info("REFUSED ("+reason+")");
changeStatus(UA_IDLE);
if (call==call_transfer)
{ StatusLine status_line=resp.getStatusLine();
int code=status_line.getCode();
//String reason=status_line.getReason();
this.call.notify(code,reason);
call_transfer=null;
}
// play "off" sound
if (clip_off!=null) clip_off.replay();
if (listener!=null) listener.onUaCallFailed(this);
}
/** Callback function called when arriving a 3xx (call redirection) */
public void onCallRedirection(Call call, String reason, Vector contact_list, Message resp) {
LOGGER.fine("onCallRedirection()");
if (call!=this.call) {
LOGGER.fine("NOT the current call");
return;
}
LOGGER.info("REDIRECTION ("+reason+")");
call.call(((String)contact_list.elementAt(0)));
}
/** Callback function that may be overloaded (extended). Called when arriving a CANCEL request */
public void onCallCanceling(Call call, Message cancel) {
LOGGER.fine("onCallCanceling()");
if (call!=this.call) {
LOGGER.fine("NOT the current call");
return;
}
LOGGER.info("CANCEL");
changeStatus(UA_IDLE);
// stop ringing
if (clip_ring!=null) clip_ring.stop();
// play "off" sound
if (clip_off!=null) clip_off.replay();
if (listener!=null) listener.onUaCallCancelled(this);
}
/** Callback function called when arriving a BYE request */
public void onCallClosing(Call call, Message bye) {
LOGGER.fine("onCallClosing()");
if (call!=this.call && call!=call_transfer) {
LOGGER.fine("NOT the current call");
return;
}
if (call!=call_transfer && call_transfer!=null) {
LOGGER.info("CLOSE PREVIOUS CALL");
this.call=call_transfer;
call_transfer=null;
return;
}
// else
LOGGER.info("CLOSE");
closeMediaApplication();
// play "off" sound
if (clip_off!=null) clip_off.replay();
if (listener!=null) listener.onUaCallClosed(this);
changeStatus(UA_IDLE);
}
/** Callback function called when arriving a response after a BYE request (call closed) */
public void onCallClosed(Call call, Message resp) {
LOGGER.fine("onCallClosed()");
if (call!=this.call) {
LOGGER.fine("NOT the current call");
return;
}
LOGGER.info("CLOSE/OK");
if (listener!=null) listener.onUaCallClosed(this);
changeStatus(UA_IDLE);
}
/** Callback function called when the invite expires */
public void onCallTimeout(Call call) {
LOGGER.fine("onCallTimeout()");
if (call!=this.call) {
LOGGER.fine("NOT the current call");
return;
}
LOGGER.info("NOT FOUND/TIMEOUT");
changeStatus(UA_IDLE);
if (call==call_transfer)
{ int code=408;
String reason="Request Timeout";
this.call.notify(code,reason);
call_transfer=null;
}
// play "off" sound
if (clip_off!=null) clip_off.replay();
if (listener!=null) listener.onUaCallFailed(this);
}
// ****************** ExtendedCall callback functions ******************
/** Callback function called when arriving a new REFER method (transfer request) */
public void onCallTransfer(ExtendedCall call, NameAddress refer_to, NameAddress refered_by, Message refer) {
LOGGER.fine("onCallTransfer()");
if (call!=this.call) {
LOGGER.fine("NOT the current call");
return;
}
LOGGER.info("Transfer to "+refer_to.toString());
call.acceptTransfer();
call_transfer=new ExtendedCall(sip_provider,user_profile.from_url,user_profile.contact_url,this);
call_transfer.call(refer_to.toString(),local_session);
}
/** Callback function called when a call transfer is accepted. */
public void onCallTransferAccepted(ExtendedCall call, Message resp) {
LOGGER.fine("onCallTransferAccepted()");
if (call!=this.call) {
LOGGER.fine("NOT the current call");
return;
}
LOGGER.info("Transfer accepted");
}
/** Callback function called when a call transfer is refused. */
public void onCallTransferRefused(ExtendedCall call, String reason, Message resp) {
LOGGER.fine("onCallTransferRefused()");
if (call!=this.call) {
LOGGER.fine("NOT the current call");
return;
}
LOGGER.info("Transfer refused");
}
/** Callback function called when a call transfer is successfully completed */
public void onCallTransferSuccess(ExtendedCall call, Message notify) {
LOGGER.fine("onCallTransferSuccess()");
if (call!=this.call) {
LOGGER.fine("NOT the current call");
return;
}
LOGGER.info("Transfer successed");
call.hangup();
if (listener!=null) listener.onUaCallTrasferred(this);
}
/** Callback function called when a call transfer is NOT sucessfully completed */
public void onCallTransferFailure(ExtendedCall call, String reason, Message notify) {
LOGGER.fine("onCallTransferFailure()");
if (call!=this.call) {
LOGGER.fine("NOT the current call");
return;
}
LOGGER.info("Transfer failed");
}
// ************************* Schedule events ***********************
/** Schedules a re-inviting event after <i>delay_time</i> secs. */
void reInvite(final String contact_url, final int delay_time)
{ SessionDescriptor sdp=new SessionDescriptor(local_session);
final SessionDescriptor new_sdp=new SessionDescriptor(sdp.getOrigin(),sdp.getSessionName(),new ConnectionField("IP4","0.0.0.0"),new TimeField());
new_sdp.addMediaDescriptors(sdp.getMediaDescriptors());
(new Thread() { public void run() { runReInvite(contact_url,new_sdp.toString(),delay_time); } }).start();
}
/** Re-invite. */
private void runReInvite(String contact, String body, int delay_time) {
try {
if (delay_time>0) {
Thread.sleep(delay_time*1000);
}
LOGGER.info("RE-INVITING/MODIFING");
if (call!=null && call.isOnCall()) {
LOGGER.info("REFER/TRANSFER");
call.modify(contact,body);
}
}
catch (Exception e) { e.printStackTrace(); }
}
/** Schedules a call-transfer event after <i>delay_time</i> secs. */
void callTransfer(final String transfer_to, final int delay_time)
{ (new Thread() { public void run() { runCallTransfer(transfer_to,delay_time); } }).start();
}
/** Call-transfer. */
private void runCallTransfer(String transfer_to, int delay_time)
{ try
{ if (delay_time>0) Thread.sleep(delay_time*1000);
if (call!=null && call.isOnCall()) {
LOGGER.info("REFER/TRANSFER");
call.transfer(transfer_to);
}
}
catch (Exception e) { e.printStackTrace(); }
}
/** Schedules an automatic answer event after <i>delay_time</i> secs. */
void automaticAccept(final int delay_time)
{ (new Thread() { public void run() { runAutomaticAccept(delay_time); } }).start();
}
/** Automatic answer. */
private void runAutomaticAccept(int delay_time)
{ try
{ if (delay_time>0) Thread.sleep(delay_time*1000);
if (call!=null) {
LOGGER.info("AUTOMATIC-ANSWER");
accept();
}
}
catch (Exception e) { e.printStackTrace(); }
}
/** Schedules an automatic hangup event after <i>delay_time</i> secs. */
void automaticHangup(final int delay_time)
{ (new Thread() { public void run() { runAutomaticHangup(delay_time); } }).start();
}
/** Automatic hangup. */
private void runAutomaticHangup(int delay_time)
{ try
{ if (delay_time>0) Thread.sleep(delay_time*1000);
if (call!=null && call.isOnCall()) {
LOGGER.info("AUTOMATIC-HANGUP");
hangup();
listen();
}
}
catch (Exception e) { e.printStackTrace(); }
}
// ****************************** Logs *****************************
/** Adds the Exception message to the default Log */
void printException(Exception e,int level)
{ if (log!=null) log.printException(e,level+SipStack.LOG_LEVEL_UA);
}
}