Merge "Add timer to SIP session creation process." into gingerbread

This commit is contained in:
Hung-ying Tyan
2010-09-16 23:30:49 -07:00
committed by Android (Google) Code Review
6 changed files with 182 additions and 54 deletions

View File

@@ -84,7 +84,8 @@ class SipSessionGroup implements SipListener {
private static final boolean DEBUG_PING = DEBUG && false; private static final boolean DEBUG_PING = DEBUG && false;
private static final String ANONYMOUS = "anonymous"; private static final String ANONYMOUS = "anonymous";
private static final String SERVER_ERROR_PREFIX = "Response: "; private static final String SERVER_ERROR_PREFIX = "Response: ";
private static final int EXPIRY_TIME = 3600; private static final int EXPIRY_TIME = 3600; // in seconds
private static final int CANCEL_CALL_TIMER = 5; // in seconds
private static final EventObject DEREGISTER = new EventObject("Deregister"); private static final EventObject DEREGISTER = new EventObject("Deregister");
private static final EventObject END_CALL = new EventObject("End call"); private static final EventObject END_CALL = new EventObject("End call");
@@ -363,6 +364,40 @@ class SipSessionGroup implements SipListener {
String mPeerSessionDescription; String mPeerSessionDescription;
boolean mInCall; boolean mInCall;
boolean mReRegisterFlag = false; boolean mReRegisterFlag = false;
SessionTimer mTimer;
// lightweight timer
class SessionTimer {
private boolean mRunning = true;
void start(final int timeout) {
new Thread(new Runnable() {
public void run() {
sleep(timeout);
if (mRunning) timeout();
}
}).start();
}
synchronized void cancel() {
mRunning = false;
this.notify();
}
private void timeout() {
synchronized (SipSessionGroup.this) {
onError(SipErrorCode.TIME_OUT, "Session timed out!");
}
}
private synchronized void sleep(int timeout) {
try {
this.wait(timeout * 1000);
} catch (InterruptedException e) {
Log.e(TAG, "session timer interrupted!");
}
}
}
public SipSessionImpl(ISipSessionListener listener) { public SipSessionImpl(ISipSessionListener listener) {
setListener(listener); setListener(listener);
@@ -382,6 +417,8 @@ class SipSessionGroup implements SipListener {
mServerTransaction = null; mServerTransaction = null;
mClientTransaction = null; mClientTransaction = null;
mPeerSessionDescription = null; mPeerSessionDescription = null;
cancelSessionTimer();
} }
public boolean isInCall() { public boolean isInCall() {
@@ -434,16 +471,16 @@ class SipSessionGroup implements SipListener {
}).start(); }).start();
} }
public void makeCall(SipProfile peerProfile, public void makeCall(SipProfile peerProfile, String sessionDescription,
String sessionDescription) { int timeout) {
doCommandAsync( doCommandAsync(new MakeCallCommand(peerProfile, sessionDescription,
new MakeCallCommand(peerProfile, sessionDescription)); timeout));
} }
public void answerCall(String sessionDescription) { public void answerCall(String sessionDescription, int timeout) {
try { try {
processCommand( processCommand(new MakeCallCommand(mPeerProfile,
new MakeCallCommand(mPeerProfile, sessionDescription)); sessionDescription, timeout));
} catch (SipException e) { } catch (SipException e) {
onError(e); onError(e);
} }
@@ -453,9 +490,15 @@ class SipSessionGroup implements SipListener {
doCommandAsync(END_CALL); doCommandAsync(END_CALL);
} }
public void changeCall(String sessionDescription) { public void changeCall(String sessionDescription, int timeout) {
doCommandAsync( doCommandAsync(new MakeCallCommand(mPeerProfile, sessionDescription,
new MakeCallCommand(mPeerProfile, sessionDescription)); timeout));
}
public void changeCallWithTimeout(
String sessionDescription, int timeout) {
doCommandAsync(new MakeCallCommand(mPeerProfile, sessionDescription,
timeout));
} }
public void register(int duration) { public void register(int duration) {
@@ -800,6 +843,7 @@ class SipSessionGroup implements SipListener {
addSipSession(this); addSipSession(this);
mState = SipSessionState.OUTGOING_CALL; mState = SipSessionState.OUTGOING_CALL;
mProxy.onCalling(this); mProxy.onCalling(this);
startSessionTimer(cmd.getTimeout());
return true; return true;
} else if (evt instanceof RegisterCommand) { } else if (evt instanceof RegisterCommand) {
int duration = ((RegisterCommand) evt).getDuration(); int duration = ((RegisterCommand) evt).getDuration();
@@ -831,6 +875,7 @@ class SipSessionGroup implements SipListener {
((MakeCallCommand) evt).getSessionDescription(), ((MakeCallCommand) evt).getSessionDescription(),
mServerTransaction); mServerTransaction);
mState = SipSessionState.INCOMING_CALL_ANSWERING; mState = SipSessionState.INCOMING_CALL_ANSWERING;
startSessionTimer(((MakeCallCommand) evt).getTimeout());
return true; return true;
} else if (END_CALL == evt) { } else if (END_CALL == evt) {
mSipHelper.sendInviteBusyHere(mInviteReceived, mSipHelper.sendInviteBusyHere(mInviteReceived,
@@ -873,6 +918,7 @@ class SipSessionGroup implements SipListener {
if (mState == SipSessionState.OUTGOING_CALL) { if (mState == SipSessionState.OUTGOING_CALL) {
mState = SipSessionState.OUTGOING_CALL_RING_BACK; mState = SipSessionState.OUTGOING_CALL_RING_BACK;
mProxy.onRingingBack(this); mProxy.onRingingBack(this);
cancelSessionTimer();
} }
return true; return true;
case Response.OK: case Response.OK:
@@ -885,10 +931,10 @@ class SipSessionGroup implements SipListener {
if (handleAuthentication(event)) { if (handleAuthentication(event)) {
addSipSession(this); addSipSession(this);
} else if (mLastNonce == null) { } else if (mLastNonce == null) {
endCallOnError(SipErrorCode.SERVER_ERROR, onError(SipErrorCode.SERVER_ERROR,
"server does not provide challenge"); "server does not provide challenge");
} else { } else {
endCallOnError(SipErrorCode.INVALID_CREDENTIALS, onError(SipErrorCode.INVALID_CREDENTIALS,
"incorrect username or password"); "incorrect username or password");
} }
return true; return true;
@@ -914,6 +960,7 @@ class SipSessionGroup implements SipListener {
// response. // response.
mSipHelper.sendCancel(mClientTransaction); mSipHelper.sendCancel(mClientTransaction);
mState = SipSessionState.OUTGOING_CALL_CANCELING; mState = SipSessionState.OUTGOING_CALL_CANCELING;
startSessionTimer(CANCEL_CALL_TIMER);
return true; return true;
} }
return false; return false;
@@ -926,9 +973,13 @@ class SipSessionGroup implements SipListener {
Response response = event.getResponse(); Response response = event.getResponse();
int statusCode = response.getStatusCode(); int statusCode = response.getStatusCode();
if (expectResponse(Request.CANCEL, evt)) { if (expectResponse(Request.CANCEL, evt)) {
if (statusCode == Response.OK) { switch (statusCode) {
// do nothing; wait for REQUEST_TERMINATED case Response.OK:
return true; // do nothing; wait for REQUEST_TERMINATED
return true;
case Response.REQUEST_TERMINATED:
endCallNormally();
return true;
} }
} else if (expectResponse(Request.INVITE, evt)) { } else if (expectResponse(Request.INVITE, evt)) {
if (statusCode == Response.OK) { if (statusCode == Response.OK) {
@@ -978,11 +1029,27 @@ class SipSessionGroup implements SipListener {
mClientTransaction = mSipHelper.sendReinvite(mDialog, mClientTransaction = mSipHelper.sendReinvite(mDialog,
((MakeCallCommand) evt).getSessionDescription()); ((MakeCallCommand) evt).getSessionDescription());
mState = SipSessionState.OUTGOING_CALL; mState = SipSessionState.OUTGOING_CALL;
startSessionTimer(((MakeCallCommand) evt).getTimeout());
return true; return true;
} }
return false; return false;
} }
// timeout in seconds
private void startSessionTimer(int timeout) {
if (timeout > 0) {
mTimer = new SessionTimer();
mTimer.start(timeout);
}
}
private void cancelSessionTimer() {
if (mTimer != null) {
mTimer.cancel();
mTimer = null;
}
}
private String createErrorMessage(Response response) { private String createErrorMessage(Response response) {
return String.format(SERVER_ERROR_PREFIX + "%s (%d)", return String.format(SERVER_ERROR_PREFIX + "%s (%d)",
response.getReasonPhrase(), response.getStatusCode()); response.getReasonPhrase(), response.getStatusCode());
@@ -991,15 +1058,10 @@ class SipSessionGroup implements SipListener {
private void establishCall() { private void establishCall() {
mState = SipSessionState.IN_CALL; mState = SipSessionState.IN_CALL;
mInCall = true; mInCall = true;
cancelSessionTimer();
mProxy.onCallEstablished(this, mPeerSessionDescription); mProxy.onCallEstablished(this, mPeerSessionDescription);
} }
private void fallbackToPreviousInCall(Throwable exception) {
exception = getRootCause(exception);
fallbackToPreviousInCall(getErrorCode(exception),
exception.toString());
}
private void fallbackToPreviousInCall(SipErrorCode errorCode, private void fallbackToPreviousInCall(SipErrorCode errorCode,
String message) { String message) {
mState = SipSessionState.IN_CALL; mState = SipSessionState.IN_CALL;
@@ -1022,6 +1084,7 @@ class SipSessionGroup implements SipListener {
} }
private void onError(SipErrorCode errorCode, String message) { private void onError(SipErrorCode errorCode, String message) {
cancelSessionTimer();
switch (mState) { switch (mState) {
case REGISTERING: case REGISTERING:
case DEREGISTERING: case DEREGISTERING:
@@ -1255,11 +1318,18 @@ class SipSessionGroup implements SipListener {
private class MakeCallCommand extends EventObject { private class MakeCallCommand extends EventObject {
private String mSessionDescription; private String mSessionDescription;
private int mTimeout; // in seconds
public MakeCallCommand(SipProfile peerProfile, public MakeCallCommand(SipProfile peerProfile,
String sessionDescription) { String sessionDescription) {
this(peerProfile, sessionDescription, -1);
}
public MakeCallCommand(SipProfile peerProfile,
String sessionDescription, int timeout) {
super(peerProfile); super(peerProfile);
mSessionDescription = sessionDescription; mSessionDescription = sessionDescription;
mTimeout = timeout;
} }
public SipProfile getPeerProfile() { public SipProfile getPeerProfile() {
@@ -1269,6 +1339,9 @@ class SipSessionGroup implements SipListener {
public String getSessionDescription() { public String getSessionDescription() {
return mSessionDescription; return mSessionDescription;
} }
}
public int getTimeout() {
return mTimeout;
}
}
} }

View File

@@ -74,6 +74,7 @@ import java.util.List;
public class SipPhone extends SipPhoneBase { public class SipPhone extends SipPhoneBase {
private static final String LOG_TAG = "SipPhone"; private static final String LOG_TAG = "SipPhone";
private static final boolean LOCAL_DEBUG = true; private static final boolean LOCAL_DEBUG = true;
private static final int SESSION_TIMEOUT = 8; // in seconds
// A call that is ringing or (call) waiting // A call that is ringing or (call) waiting
private SipCall ringingCall = new SipCall(); private SipCall ringingCall = new SipCall();
@@ -675,7 +676,7 @@ public class SipPhone extends SipPhoneBase {
void acceptCall() throws CallStateException { void acceptCall() throws CallStateException {
try { try {
mSipAudioCall.answerCall(); mSipAudioCall.answerCall(SESSION_TIMEOUT);
} catch (SipException e) { } catch (SipException e) {
throw new CallStateException("acceptCall(): " + e); throw new CallStateException("acceptCall(): " + e);
} }
@@ -693,7 +694,7 @@ public class SipPhone extends SipPhoneBase {
void dial() throws SipException { void dial() throws SipException {
setState(Call.State.DIALING); setState(Call.State.DIALING);
mSipAudioCall = mSipManager.makeAudioCall(mContext, mProfile, mSipAudioCall = mSipManager.makeAudioCall(mContext, mProfile,
mPeer, null); mPeer, null, SESSION_TIMEOUT);
mSipAudioCall.setRingbackToneEnabled(false); mSipAudioCall.setRingbackToneEnabled(false);
mSipAudioCall.setListener(mAdapter); mSipAudioCall.setListener(mAdapter);
} }
@@ -701,7 +702,7 @@ public class SipPhone extends SipPhoneBase {
void hold() throws CallStateException { void hold() throws CallStateException {
setState(Call.State.HOLDING); setState(Call.State.HOLDING);
try { try {
mSipAudioCall.holdCall(); mSipAudioCall.holdCall(SESSION_TIMEOUT);
} catch (SipException e) { } catch (SipException e) {
throw new CallStateException("hold(): " + e); throw new CallStateException("hold(): " + e);
} }
@@ -711,7 +712,7 @@ public class SipPhone extends SipPhoneBase {
mSipAudioCall.setAudioGroup(audioGroup); mSipAudioCall.setAudioGroup(audioGroup);
setState(Call.State.ACTIVE); setState(Call.State.ACTIVE);
try { try {
mSipAudioCall.continueCall(); mSipAudioCall.continueCall(SESSION_TIMEOUT);
} catch (SipException e) { } catch (SipException e) {
throw new CallStateException("unhold(): " + e); throw new CallStateException("unhold(): " + e);
} }

View File

@@ -112,9 +112,11 @@ interface ISipSession {
* *
* @param callee the SIP profile to make the call to * @param callee the SIP profile to make the call to
* @param sessionDescription the session description of this call * @param sessionDescription the session description of this call
* @param timeout the session will be timed out if the call is not
* established within {@code timeout} seconds
* @see ISipSessionListener * @see ISipSessionListener
*/ */
void makeCall(in SipProfile callee, String sessionDescription); void makeCall(in SipProfile callee, String sessionDescription, int timeout);
/** /**
* Answers an incoming call with the specified session description. The * Answers an incoming call with the specified session description. The
@@ -122,8 +124,10 @@ interface ISipSession {
* {@link SipSessionState#INCOMING_CALL}. * {@link SipSessionState#INCOMING_CALL}.
* *
* @param sessionDescription the session description to answer this call * @param sessionDescription the session description to answer this call
* @param timeout the session will be timed out if the call is not
* established within {@code timeout} seconds
*/ */
void answerCall(String sessionDescription); void answerCall(String sessionDescription, int timeout);
/** /**
* Ends an established call, terminates an outgoing call or rejects an * Ends an established call, terminates an outgoing call or rejects an
@@ -140,6 +144,8 @@ interface ISipSession {
* to call when the session state is in {@link SipSessionState#IN_CALL}. * to call when the session state is in {@link SipSessionState#IN_CALL}.
* *
* @param sessionDescription the new session description * @param sessionDescription the new session description
* @param timeout the session will be timed out if the call is not
* established within {@code timeout} seconds
*/ */
void changeCall(String sessionDescription); void changeCall(String sessionDescription, int timeout);
} }

View File

@@ -158,12 +158,18 @@ public interface SipAudioCall {
void close(); void close();
/** /**
* Initiates an audio call to the specified profile. * Initiates an audio call to the specified profile. The attempt will be
* timed out if the call is not established within {@code timeout} seconds
* and {@code Listener.onError(SipAudioCall, SipErrorCode.TIME_OUT, String)}
* will be called.
* *
* @param callee the SIP profile to make the call to * @param callee the SIP profile to make the call to
* @param sipManager the {@link SipManager} object to help make call with * @param sipManager the {@link SipManager} object to help make call with
* @param timeout the timeout value in seconds
* @see Listener.onError
*/ */
void makeCall(SipProfile callee, SipManager sipManager) throws SipException; void makeCall(SipProfile callee, SipManager sipManager, int timeout)
throws SipException;
/** /**
* Attaches an incoming call to this call object. * Attaches an incoming call to this call object.
@@ -179,18 +185,38 @@ public interface SipAudioCall {
/** /**
* Puts a call on hold. When succeeds, {@link Listener#onCallHeld} is * Puts a call on hold. When succeeds, {@link Listener#onCallHeld} is
* called. * called. The attempt will be timed out if the call is not established
* within {@code timeout} seconds and
* {@code Listener.onError(SipAudioCall, SipErrorCode.TIME_OUT, String)}
* will be called.
*
* @param timeout the timeout value in seconds
* @see Listener.onError
*/ */
void holdCall() throws SipException; void holdCall(int timeout) throws SipException;
/** Answers a call. */ /**
void answerCall() throws SipException; * Answers a call. The attempt will be timed out if the call is not
* established within {@code timeout} seconds and
* {@code Listener.onError(SipAudioCall, SipErrorCode.TIME_OUT, String)}
* will be called.
*
* @param timeout the timeout value in seconds
* @see Listener.onError
*/
void answerCall(int timeout) throws SipException;
/** /**
* Continues a call that's on hold. When succeeds, * Continues a call that's on hold. When succeeds,
* {@link Listener#onCallEstablished} is called. * {@link Listener#onCallEstablished} is called. The attempt will be timed
* out if the call is not established within {@code timeout} seconds and
* {@code Listener.onError(SipAudioCall, SipErrorCode.TIME_OUT, String)}
* will be called.
*
* @param timeout the timeout value in seconds
* @see Listener.onError
*/ */
void continueCall() throws SipException; void continueCall(int timeout) throws SipException;
/** Puts the device to speaker mode. */ /** Puts the device to speaker mode. */
void setSpeakerMode(boolean speakerMode); void setSpeakerMode(boolean speakerMode);

View File

@@ -55,6 +55,7 @@ public class SipAudioCallImpl extends SipSessionAdapter
private static final boolean DONT_RELEASE_SOCKET = false; private static final boolean DONT_RELEASE_SOCKET = false;
private static final String AUDIO = "audio"; private static final String AUDIO = "audio";
private static final int DTMF = 101; private static final int DTMF = 101;
private static final int SESSION_TIMEOUT = 5; // in seconds
private Context mContext; private Context mContext;
private SipProfile mLocalProfile; private SipProfile mLocalProfile;
@@ -144,12 +145,21 @@ public class SipAudioCallImpl extends SipSessionAdapter
if (closeRtp) stopCall(RELEASE_SOCKET); if (closeRtp) stopCall(RELEASE_SOCKET);
stopRingbackTone(); stopRingbackTone();
stopRinging(); stopRinging();
mSipSession = null;
mInCall = false; mInCall = false;
mHold = false; mHold = false;
mSessionId = -1L; mSessionId = -1L;
mErrorCode = null; mErrorCode = null;
mErrorMessage = null; mErrorMessage = null;
if (mSipSession != null) {
try {
mSipSession.setListener(null);
} catch (RemoteException e) {
// don't care
}
mSipSession = null;
}
} }
public synchronized SipProfile getLocalProfile() { public synchronized SipProfile getLocalProfile() {
@@ -219,7 +229,7 @@ public class SipAudioCallImpl extends SipSessionAdapter
// session changing request // session changing request
try { try {
mPeerSd = new SdpSessionDescription(sessionDescription); mPeerSd = new SdpSessionDescription(sessionDescription);
answerCall(); answerCall(SESSION_TIMEOUT);
} catch (Throwable e) { } catch (Throwable e) {
Log.e(TAG, "onRinging()", e); Log.e(TAG, "onRinging()", e);
session.endCall(); session.endCall();
@@ -346,14 +356,15 @@ public class SipAudioCallImpl extends SipSessionAdapter
} }
public synchronized void makeCall(SipProfile peerProfile, public synchronized void makeCall(SipProfile peerProfile,
SipManager sipManager) throws SipException { SipManager sipManager, int timeout) throws SipException {
try { try {
mSipSession = sipManager.createSipSession(mLocalProfile, this); mSipSession = sipManager.createSipSession(mLocalProfile, this);
if (mSipSession == null) { if (mSipSession == null) {
throw new SipException( throw new SipException(
"Failed to create SipSession; network available?"); "Failed to create SipSession; network available?");
} }
mSipSession.makeCall(peerProfile, createOfferSessionDescription()); mSipSession.makeCall(peerProfile, createOfferSessionDescription(),
timeout);
} catch (Throwable e) { } catch (Throwable e) {
if (e instanceof SipException) { if (e instanceof SipException) {
throw (SipException) e; throw (SipException) e;
@@ -376,10 +387,10 @@ public class SipAudioCallImpl extends SipSessionAdapter
} }
} }
public synchronized void holdCall() throws SipException { public synchronized void holdCall(int timeout) throws SipException {
if (mHold) return; if (mHold) return;
try { try {
mSipSession.changeCall(createHoldSessionDescription()); mSipSession.changeCall(createHoldSessionDescription(), timeout);
mHold = true; mHold = true;
} catch (Throwable e) { } catch (Throwable e) {
throwSipException(e); throwSipException(e);
@@ -389,21 +400,21 @@ public class SipAudioCallImpl extends SipSessionAdapter
if (audioGroup != null) audioGroup.setMode(AudioGroup.MODE_ON_HOLD); if (audioGroup != null) audioGroup.setMode(AudioGroup.MODE_ON_HOLD);
} }
public synchronized void answerCall() throws SipException { public synchronized void answerCall(int timeout) throws SipException {
try { try {
stopRinging(); stopRinging();
mSipSession.answerCall(createAnswerSessionDescription()); mSipSession.answerCall(createAnswerSessionDescription(), timeout);
} catch (Throwable e) { } catch (Throwable e) {
Log.e(TAG, "answerCall()", e); Log.e(TAG, "answerCall()", e);
throwSipException(e); throwSipException(e);
} }
} }
public synchronized void continueCall() throws SipException { public synchronized void continueCall(int timeout) throws SipException {
if (!mHold) return; if (!mHold) return;
try { try {
mHold = false; mHold = false;
mSipSession.changeCall(createContinueSessionDescription()); mSipSession.changeCall(createContinueSessionDescription(), timeout);
} catch (Throwable e) { } catch (Throwable e) {
throwSipException(e); throwSipException(e);
} }

View File

@@ -218,44 +218,55 @@ public class SipManager {
} }
/** /**
* Creates a {@link SipAudioCall} to make a call. * Creates a {@link SipAudioCall} to make a call. The attempt will be timed
* out if the call is not established within {@code timeout} seconds and
* {@code SipAudioCall.Listener.onError(SipAudioCall, SipErrorCode.TIME_OUT, String)}
* will be called.
* *
* @param context context to create a {@link SipAudioCall} object * @param context context to create a {@link SipAudioCall} object
* @param localProfile the SIP profile to make the call from * @param localProfile the SIP profile to make the call from
* @param peerProfile the SIP profile to make the call to * @param peerProfile the SIP profile to make the call to
* @param listener to listen to the call events from {@link SipAudioCall}; * @param listener to listen to the call events from {@link SipAudioCall};
* can be null * can be null
* @param timeout the timeout value in seconds
* @return a {@link SipAudioCall} object * @return a {@link SipAudioCall} object
* @throws SipException if calling the SIP service results in an error * @throws SipException if calling the SIP service results in an error
* @see SipAudioCall.Listener.onError
*/ */
public SipAudioCall makeAudioCall(Context context, SipProfile localProfile, public SipAudioCall makeAudioCall(Context context, SipProfile localProfile,
SipProfile peerProfile, SipAudioCall.Listener listener) SipProfile peerProfile, SipAudioCall.Listener listener, int timeout)
throws SipException { throws SipException {
SipAudioCall call = new SipAudioCallImpl(context, localProfile); SipAudioCall call = new SipAudioCallImpl(context, localProfile);
call.setListener(listener); call.setListener(listener);
call.makeCall(peerProfile, this); call.makeCall(peerProfile, this, timeout);
return call; return call;
} }
/** /**
* Creates a {@link SipAudioCall} to make a call. To use this method, one * Creates a {@link SipAudioCall} to make a call. To use this method, one
* must call {@link #open(SipProfile)} first. * must call {@link #open(SipProfile)} first. The attempt will be timed out
* if the call is not established within {@code timeout} seconds and
* {@code SipAudioCall.Listener.onError(SipAudioCall, SipErrorCode.TIME_OUT, String)}
* will be called.
* *
* @param context context to create a {@link SipAudioCall} object * @param context context to create a {@link SipAudioCall} object
* @param localProfileUri URI of the SIP profile to make the call from * @param localProfileUri URI of the SIP profile to make the call from
* @param peerProfileUri URI of the SIP profile to make the call to * @param peerProfileUri URI of the SIP profile to make the call to
* @param listener to listen to the call events from {@link SipAudioCall}; * @param listener to listen to the call events from {@link SipAudioCall};
* can be null * can be null
* @param timeout the timeout value in seconds
* @return a {@link SipAudioCall} object * @return a {@link SipAudioCall} object
* @throws SipException if calling the SIP service results in an error * @throws SipException if calling the SIP service results in an error
* @see SipAudioCall.Listener.onError
*/ */
public SipAudioCall makeAudioCall(Context context, String localProfileUri, public SipAudioCall makeAudioCall(Context context, String localProfileUri,
String peerProfileUri, SipAudioCall.Listener listener) String peerProfileUri, SipAudioCall.Listener listener, int timeout)
throws SipException { throws SipException {
try { try {
return makeAudioCall(context, return makeAudioCall(context,
new SipProfile.Builder(localProfileUri).build(), new SipProfile.Builder(localProfileUri).build(),
new SipProfile.Builder(peerProfileUri).build(), listener); new SipProfile.Builder(peerProfileUri).build(), listener,
timeout);
} catch (ParseException e) { } catch (ParseException e) {
throw new SipException("build SipProfile", e); throw new SipException("build SipProfile", e);
} }