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

View File

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

View File

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

View File

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