am 3d7606aa: SIP: enhance timeout and registration status feedback.

Merge commit '3d7606aa607b24817e37c264f2141ed7b2d50be0' into gingerbread-plus-aosp

* commit '3d7606aa607b24817e37c264f2141ed7b2d50be0':
  SIP: enhance timeout and registration status feedback.
This commit is contained in:
Hung-ying Tyan
2010-09-13 02:48:22 -07:00
committed by Android Git Automerger
5 changed files with 156 additions and 105 deletions

View File

@@ -27,6 +27,7 @@ import android.net.NetworkInfo;
import android.net.sip.ISipService; import android.net.sip.ISipService;
import android.net.sip.ISipSession; import android.net.sip.ISipSession;
import android.net.sip.ISipSessionListener; import android.net.sip.ISipSessionListener;
import android.net.sip.SipErrorCode;
import android.net.sip.SipManager; import android.net.sip.SipManager;
import android.net.sip.SipProfile; import android.net.sip.SipProfile;
import android.net.sip.SipSessionAdapter; import android.net.sip.SipSessionAdapter;
@@ -528,6 +529,8 @@ public final class SipService extends ISipService.Stub {
private int mBackoff = 1; private int mBackoff = 1;
private boolean mRegistered; private boolean mRegistered;
private long mExpiryTime; private long mExpiryTime;
private SipErrorCode mErrorCode;
private String mErrorMessage;
private String getAction() { private String getAction() {
return toString(); return toString();
@@ -551,15 +554,25 @@ public final class SipService extends ISipService.Stub {
} }
public void stop() { public void stop() {
stop(false);
}
private void stopButKeepStates() {
stop(true);
}
private void stop(boolean keepStates) {
if (mSession == null) return; if (mSession == null) return;
if (mConnected) mSession.unregister(); if (mConnected && mRegistered) mSession.unregister();
mTimer.cancel(this); mTimer.cancel(this);
if (mKeepAliveProcess != null) { if (mKeepAliveProcess != null) {
mKeepAliveProcess.stop(); mKeepAliveProcess.stop();
mKeepAliveProcess = null; mKeepAliveProcess = null;
} }
mSession = null; if (!keepStates) {
mRegistered = false; mSession = null;
mRegistered = false;
}
} }
private boolean isStopped() { private boolean isStopped() {
@@ -568,20 +581,33 @@ public final class SipService extends ISipService.Stub {
public void setListener(ISipSessionListener listener) { public void setListener(ISipSessionListener listener) {
Log.v(TAG, "setListener(): " + listener); Log.v(TAG, "setListener(): " + listener);
mProxy.setListener(listener); synchronized (SipService.this) {
if (mSession == null) return; mProxy.setListener(listener);
if (mSession == null) return;
try { try {
if ((mSession != null) && SipSessionState.REGISTERING.equals( SipSessionState state = (mSession == null)
mSession.getState())) { ? SipSessionState.READY_TO_CALL
mProxy.onRegistering(mSession); : Enum.valueOf(
} else if (mRegistered) { SipSessionState.class, mSession.getState());
int duration = (int) if ((state == SipSessionState.REGISTERING)
(mExpiryTime - SystemClock.elapsedRealtime()); || (state == SipSessionState.DEREGISTERING)) {
mProxy.onRegistrationDone(mSession, duration); mProxy.onRegistering(mSession);
} else if (mRegistered) {
int duration = (int)
(mExpiryTime - SystemClock.elapsedRealtime());
mProxy.onRegistrationDone(mSession, duration);
} else if (mErrorCode != null) {
if (mErrorCode == SipErrorCode.TIME_OUT) {
mProxy.onRegistrationTimeout(mSession);
} else {
mProxy.onRegistrationFailed(mSession,
mErrorCode.toString(), mErrorMessage);
}
}
} catch (Throwable t) {
Log.w(TAG, "setListener(): " + t);
} }
} catch (Throwable t) {
Log.w(TAG, "setListener(): " + t);
} }
} }
@@ -590,6 +616,8 @@ public final class SipService extends ISipService.Stub {
} }
public void run() { public void run() {
mErrorCode = null;
mErrorMessage = null;
Log.v(TAG, " ~~~ registering"); Log.v(TAG, " ~~~ registering");
synchronized (SipService.this) { synchronized (SipService.this) {
if (mConnected && !isStopped()) mSession.register(EXPIRY_TIME); if (mConnected && !isStopped()) mSession.register(EXPIRY_TIME);
@@ -634,11 +662,7 @@ public final class SipService extends ISipService.Stub {
synchronized (SipService.this) { synchronized (SipService.this) {
if (!isStopped() && (session != mSession)) return; if (!isStopped() && (session != mSession)) return;
mRegistered = false; mRegistered = false;
try { mProxy.onRegistering(session);
mProxy.onRegistering(session);
} catch (Throwable t) {
Log.w(TAG, "onRegistering()", t);
}
} }
} }
@@ -647,11 +671,9 @@ public final class SipService extends ISipService.Stub {
Log.v(TAG, "onRegistrationDone(): " + session + ": " + mSession); Log.v(TAG, "onRegistrationDone(): " + session + ": " + mSession);
synchronized (SipService.this) { synchronized (SipService.this) {
if (!isStopped() && (session != mSession)) return; if (!isStopped() && (session != mSession)) return;
try {
mProxy.onRegistrationDone(session, duration); mProxy.onRegistrationDone(session, duration);
} catch (Throwable t) {
Log.w(TAG, "onRegistrationDone()", t);
}
if (isStopped()) return; if (isStopped()) return;
if (duration > 0) { if (duration > 0) {
@@ -687,19 +709,25 @@ public final class SipService extends ISipService.Stub {
} }
@Override @Override
public void onRegistrationFailed(ISipSession session, String className, public void onRegistrationFailed(ISipSession session,
String message) { String errorCodeString, String message) {
SipErrorCode errorCode =
Enum.valueOf(SipErrorCode.class, errorCodeString);
Log.v(TAG, "onRegistrationFailed(): " + session + ": " + mSession Log.v(TAG, "onRegistrationFailed(): " + session + ": " + mSession
+ ": " + className + ": " + message); + ": " + errorCode + ": " + message);
synchronized (SipService.this) { synchronized (SipService.this) {
if (!isStopped() && (session != mSession)) return; if (!isStopped() && (session != mSession)) return;
try { mErrorCode = errorCode;
mProxy.onRegistrationFailed(session, className, message); mErrorMessage = message;
} catch (Throwable t) { mProxy.onRegistrationFailed(session, errorCode.toString(),
Log.w(TAG, "onRegistrationFailed(): " + t); message);
}
if (!isStopped()) onError(); if (errorCode == SipErrorCode.INVALID_CREDENTIALS) {
Log.d(TAG, " pause auto-registration");
stopButKeepStates();
} else if (!isStopped()) {
onError();
}
} }
} }
@@ -708,11 +736,8 @@ public final class SipService extends ISipService.Stub {
Log.v(TAG, "onRegistrationTimeout(): " + session + ": " + mSession); Log.v(TAG, "onRegistrationTimeout(): " + session + ": " + mSession);
synchronized (SipService.this) { synchronized (SipService.this) {
if (!isStopped() && (session != mSession)) return; if (!isStopped() && (session != mSession)) return;
try { mErrorCode = SipErrorCode.TIME_OUT;
mProxy.onRegistrationTimeout(session); mProxy.onRegistrationTimeout(session);
} catch (Throwable t) {
Log.w(TAG, "onRegistrationTimeout(): " + t);
}
if (!isStopped()) { if (!isStopped()) {
mRegistered = false; mRegistered = false;

View File

@@ -412,13 +412,7 @@ class SipSessionGroup implements SipListener {
processCommand(command); processCommand(command);
} catch (SipException e) { } catch (SipException e) {
Log.w(TAG, "command error: " + command, e); Log.w(TAG, "command error: " + command, e);
// TODO: find a better way to do this onError(e);
if ((command instanceof RegisterCommand)
|| (command == DEREGISTER)) {
onRegistrationFailed(e);
} else {
onError(e);
}
} }
} }
}).start(); }).start();
@@ -480,7 +474,9 @@ class SipSessionGroup implements SipListener {
private void processCommand(EventObject command) throws SipException { private void processCommand(EventObject command) throws SipException {
if (!process(command)) { if (!process(command)) {
throw new SipException("wrong state to execute: " + command); onError(SipErrorCode.IN_PROGRESS,
"cannot initiate a new transaction to execute: "
+ command);
} }
} }
@@ -562,11 +558,8 @@ class SipSessionGroup implements SipListener {
if (evt instanceof TimeoutEvent) { if (evt instanceof TimeoutEvent) {
processTimeout((TimeoutEvent) evt); processTimeout((TimeoutEvent) evt);
} else { } else {
Log.d(TAG, "Transaction terminated:" + this); processTransactionTerminated(
if (!SipSessionState.IN_CALL.equals(mState)) { (TransactionTerminatedEvent) evt);
removeSipSession(this);
}
return true;
} }
return true; return true;
} else if (evt instanceof DialogTerminatedEvent) { } else if (evt instanceof DialogTerminatedEvent) {
@@ -585,6 +578,20 @@ class SipSessionGroup implements SipListener {
} }
} }
private void processTransactionTerminated(
TransactionTerminatedEvent event) {
switch (mState) {
case IN_CALL:
case READY_TO_CALL:
Log.d(TAG, "Transaction terminated; do nothing");
break;
default:
Log.d(TAG, "Transaction terminated early: " + this);
onError(SipErrorCode.TRANSACTION_TERMINTED,
"transaction terminated");
}
}
private void processTimeout(TimeoutEvent event) { private void processTimeout(TimeoutEvent event) {
Log.d(TAG, "processing Timeout..." + event); Log.d(TAG, "processing Timeout..." + event);
Transaction current = event.isServerTransaction() Transaction current = event.isServerTransaction()
@@ -600,25 +607,26 @@ class SipSessionGroup implements SipListener {
return; return;
} }
switch (mState) { switch (mState) {
case REGISTERING: case REGISTERING:
case DEREGISTERING: case DEREGISTERING:
reset(); reset();
mProxy.onRegistrationTimeout(this); mProxy.onRegistrationTimeout(this);
break; break;
case INCOMING_CALL: case INCOMING_CALL:
case INCOMING_CALL_ANSWERING: case INCOMING_CALL_ANSWERING:
case OUTGOING_CALL_CANCELING: case OUTGOING_CALL:
endCallOnError(SipErrorCode.TIME_OUT, event.toString()); case OUTGOING_CALL_CANCELING:
break; onError(SipErrorCode.TIME_OUT, event.toString());
case PINGING: break;
reset(); case PINGING:
mReRegisterFlag = true; reset();
mState = SipSessionState.READY_TO_CALL; mReRegisterFlag = true;
break; mState = SipSessionState.READY_TO_CALL;
break;
default: default:
// do nothing Log.d(TAG, " do nothing");
break; break;
} }
} }
@@ -665,7 +673,7 @@ class SipSessionGroup implements SipListener {
} else { } else {
Log.w(TAG, "peer did not respect our rport request"); Log.w(TAG, "peer did not respect our rport request");
} }
mState = SipSessionState.READY_TO_CALL; reset();
return true; return true;
} }
return false; return false;
@@ -687,7 +695,6 @@ class SipSessionGroup implements SipListener {
switch (statusCode) { switch (statusCode) {
case Response.OK: case Response.OK:
SipSessionState state = mState; SipSessionState state = mState;
reset();
onRegistrationDone((state == SipSessionState.REGISTERING) onRegistrationDone((state == SipSessionState.REGISTERING)
? getExpiryTime(((ResponseEvent) evt).getResponse()) ? getExpiryTime(((ResponseEvent) evt).getResponse())
: -1); : -1);
@@ -698,15 +705,13 @@ class SipSessionGroup implements SipListener {
case Response.PROXY_AUTHENTICATION_REQUIRED: case Response.PROXY_AUTHENTICATION_REQUIRED:
if (!handleAuthentication(event)) { if (!handleAuthentication(event)) {
Log.v(TAG, "Incorrect username/password"); Log.v(TAG, "Incorrect username/password");
reset();
onRegistrationFailed(SipErrorCode.INVALID_CREDENTIALS, onRegistrationFailed(SipErrorCode.INVALID_CREDENTIALS,
"incorrect username or password"); "incorrect username or password");
} }
return true; return true;
default: default:
if (statusCode >= 500) { if (statusCode >= 500) {
reset(); onRegistrationFailed(response);
onRegistrationFailed(createCallbackException(response));
return true; return true;
} }
} }
@@ -720,7 +725,6 @@ class SipSessionGroup implements SipListener {
String nonce = getNonceFromResponse(response); String nonce = getNonceFromResponse(response);
if (((nonce != null) && nonce.equals(mLastNonce)) || if (((nonce != null) && nonce.equals(mLastNonce)) ||
(nonce == mLastNonce)) { (nonce == mLastNonce)) {
Log.v(TAG, "Incorrect username/password");
return false; return false;
} else { } else {
mClientTransaction = mSipHelper.handleChallenge( mClientTransaction = mSipHelper.handleChallenge(
@@ -906,7 +910,7 @@ class SipSessionGroup implements SipListener {
} }
if (statusCode >= 400) { if (statusCode >= 400) {
onError(createCallbackException(response)); onError(response);
return true; return true;
} }
} else if (evt instanceof TransactionTerminatedEvent) { } else if (evt instanceof TransactionTerminatedEvent) {
@@ -954,10 +958,6 @@ class SipSessionGroup implements SipListener {
response.getReasonPhrase(), response.getStatusCode()); response.getReasonPhrase(), response.getStatusCode());
} }
private Exception createCallbackException(Response response) {
return new SipException(createErrorMessage(response));
}
private void establishCall() { private void establishCall() {
mState = SipSessionState.IN_CALL; mState = SipSessionState.IN_CALL;
mInCall = true; mInCall = true;
@@ -965,22 +965,22 @@ class SipSessionGroup implements SipListener {
} }
private void fallbackToPreviousInCall(Throwable exception) { private void fallbackToPreviousInCall(Throwable exception) {
mState = SipSessionState.IN_CALL;
exception = getRootCause(exception); exception = getRootCause(exception);
mProxy.onCallChangeFailed(this, getErrorCode(exception).toString(), fallbackToPreviousInCall(getErrorCode(exception),
exception.toString()); exception.toString());
} }
private void fallbackToPreviousInCall(SipErrorCode errorCode,
String message) {
mState = SipSessionState.IN_CALL;
mProxy.onCallChangeFailed(this, errorCode.toString(), message);
}
private void endCallNormally() { private void endCallNormally() {
reset(); reset();
mProxy.onCallEnded(this); mProxy.onCallEnded(this);
} }
private void endCallOnError(Throwable exception) {
exception = getRootCause(exception);
endCallOnError(getErrorCode(exception), exception.toString());
}
private void endCallOnError(SipErrorCode errorCode, String message) { private void endCallOnError(SipErrorCode errorCode, String message) {
reset(); reset();
mProxy.onError(this, errorCode.toString(), message); mProxy.onError(this, errorCode.toString(), message);
@@ -991,26 +991,34 @@ class SipSessionGroup implements SipListener {
mProxy.onCallBusy(this); mProxy.onCallBusy(this);
} }
private void onError(Throwable exception) { private void onError(SipErrorCode errorCode, String message) {
if (mInCall) { switch (mState) {
fallbackToPreviousInCall(exception); case REGISTERING:
} else { case DEREGISTERING:
endCallOnError(exception); onRegistrationFailed(errorCode, message);
break;
default:
if (mInCall) {
fallbackToPreviousInCall(errorCode, message);
} else {
endCallOnError(errorCode, message);
}
} }
} }
private void onError(Throwable exception) {
exception = getRootCause(exception);
onError(getErrorCode(exception), exception.toString());
}
private void onError(Response response) { private void onError(Response response) {
if (mInCall) { int statusCode = response.getStatusCode();
fallbackToPreviousInCall(createCallbackException(response)); if (!mInCall && ((statusCode == Response.TEMPORARILY_UNAVAILABLE)
|| (statusCode == Response.BUSY_HERE))) {
endCallOnBusy();
} else { } else {
int statusCode = response.getStatusCode(); onError(getErrorCode(statusCode), createErrorMessage(response));
if ((statusCode == Response.TEMPORARILY_UNAVAILABLE)
|| (statusCode == Response.BUSY_HERE)) {
endCallOnBusy();
} else {
endCallOnError(getErrorCode(statusCode),
createErrorMessage(response));
}
} }
} }
@@ -1053,19 +1061,29 @@ class SipSessionGroup implements SipListener {
} }
private void onRegistrationDone(int duration) { private void onRegistrationDone(int duration) {
reset();
mProxy.onRegistrationDone(this, duration); mProxy.onRegistrationDone(this, duration);
} }
private void onRegistrationFailed(SipErrorCode errorCode, private void onRegistrationFailed(SipErrorCode errorCode,
String message) { String message) {
reset();
mProxy.onRegistrationFailed(this, errorCode.toString(), message); mProxy.onRegistrationFailed(this, errorCode.toString(), message);
} }
private void onRegistrationFailed(Throwable exception) { private void onRegistrationFailed(Throwable exception) {
reset();
exception = getRootCause(exception); exception = getRootCause(exception);
onRegistrationFailed(getErrorCode(exception), onRegistrationFailed(getErrorCode(exception),
exception.toString()); exception.toString());
} }
private void onRegistrationFailed(Response response) {
reset();
int statusCode = response.getStatusCode();
onRegistrationFailed(getErrorCode(statusCode),
createErrorMessage(response));
}
} }
/** /**

View File

@@ -40,6 +40,7 @@ public abstract class Connection {
MMI, /* not presently used; dial() returns null */ MMI, /* not presently used; dial() returns null */
INVALID_NUMBER, /* invalid dial string */ INVALID_NUMBER, /* invalid dial string */
INVALID_CREDENTIALS, /* invalid credentials */ INVALID_CREDENTIALS, /* invalid credentials */
TIMED_OUT, /* client timed out */
LOST_SIGNAL, LOST_SIGNAL,
LIMIT_EXCEEDED, /* eg GSM ACM limit exceeded */ LIMIT_EXCEEDED, /* eg GSM ACM limit exceeded */
INCOMING_REJECTED, /* an incoming call that was rejected */ INCOMING_REJECTED, /* an incoming call that was rejected */

View File

@@ -826,7 +826,8 @@ public class SipPhone extends SipPhoneBase {
onError(Connection.DisconnectCause.INVALID_NUMBER); onError(Connection.DisconnectCause.INVALID_NUMBER);
break; break;
case TIME_OUT: case TIME_OUT:
onError(Connection.DisconnectCause.CONGESTION); case TRANSACTION_TERMINTED:
onError(Connection.DisconnectCause.TIMED_OUT);
break; break;
case INVALID_CREDENTIALS: case INVALID_CREDENTIALS:
onError(Connection.DisconnectCause.INVALID_CREDENTIALS); onError(Connection.DisconnectCause.INVALID_CREDENTIALS);

View File

@@ -31,6 +31,9 @@ public enum SipErrorCode {
/** When server responds with an error. */ /** When server responds with an error. */
SERVER_ERROR, SERVER_ERROR,
/** When transaction is terminated unexpectedly. */
TRANSACTION_TERMINTED,
/** When some error occurs on the device, possibly due to a bug. */ /** When some error occurs on the device, possibly due to a bug. */
CLIENT_ERROR, CLIENT_ERROR,
@@ -41,5 +44,8 @@ public enum SipErrorCode {
INVALID_REMOTE_URI, INVALID_REMOTE_URI,
/** When invalid credentials are provided. */ /** When invalid credentials are provided. */
INVALID_CREDENTIALS; INVALID_CREDENTIALS,
/** The client is in a transaction and cannot initiate a new one. */
IN_PROGRESS;
} }