diff options
author | Hung-ying Tyan <tyanh@google.com> | 2010-09-10 02:19:22 -0700 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2010-09-10 02:19:22 -0700 |
commit | ca3c24db3ae4b7a513f1ca76b1e7a3f56a020680 (patch) | |
tree | 1d9b25f6d8ac1565f5140812b0effeec78ab42ee | |
parent | 506d724cc356a7ba73995ef2929c65062277c83d (diff) | |
parent | 903e1031605d715e904811b0dd06cc6a518f0048 (diff) | |
download | frameworks_base-ca3c24db3ae4b7a513f1ca76b1e7a3f56a020680.zip frameworks_base-ca3c24db3ae4b7a513f1ca76b1e7a3f56a020680.tar.gz frameworks_base-ca3c24db3ae4b7a513f1ca76b1e7a3f56a020680.tar.bz2 |
Merge "SIP: add SipErrorCode for error feedback." into gingerbread
10 files changed, 213 insertions, 64 deletions
diff --git a/services/java/com/android/server/sip/SipSessionGroup.java b/services/java/com/android/server/sip/SipSessionGroup.java index 70ff9ea..2f7ddc4 100644 --- a/services/java/com/android/server/sip/SipSessionGroup.java +++ b/services/java/com/android/server/sip/SipSessionGroup.java @@ -25,6 +25,7 @@ import gov.nist.javax.sip.message.SIPMessage; import android.net.sip.ISipSession; import android.net.sip.ISipSessionListener; import android.net.sip.SessionDescription; +import android.net.sip.SipErrorCode; import android.net.sip.SipProfile; import android.net.sip.SipSessionAdapter; import android.net.sip.SipSessionState; @@ -34,6 +35,7 @@ import android.util.Log; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.DatagramSocket; +import java.net.UnknownHostException; import java.text.ParseException; import java.util.Collection; import java.util.EventObject; @@ -60,6 +62,7 @@ import javax.sip.TimeoutEvent; import javax.sip.Transaction; import javax.sip.TransactionState; import javax.sip.TransactionTerminatedEvent; +import javax.sip.TransactionUnavailableException; import javax.sip.address.Address; import javax.sip.address.SipURI; import javax.sip.header.CSeqHeader; @@ -77,6 +80,7 @@ import javax.sip.message.Response; class SipSessionGroup implements SipListener { private static final String TAG = "SipSession"; private static final String ANONYMOUS = "anonymous"; + private static final String SERVER_ERROR_PREFIX = "Response: "; private static final int EXPIRY_TIME = 3600; private static final EventObject DEREGISTER = new EventObject("Deregister"); @@ -282,7 +286,7 @@ class SipSessionGroup implements SipListener { Log.d(TAG, "event not processed: " + event); } } catch (Throwable e) { - Log.e(TAG, "event process error: " + event, e); + Log.w(TAG, "event process error: " + event, e); session.onError(e); } } @@ -407,6 +411,7 @@ class SipSessionGroup implements SipListener { try { 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)) { @@ -603,7 +608,7 @@ class SipSessionGroup implements SipListener { case INCOMING_CALL: case INCOMING_CALL_ANSWERING: case OUTGOING_CALL_CANCELING: - endCallOnError(new SipException("timed out")); + endCallOnError(SipErrorCode.TIME_OUT, event.toString()); break; case PINGING: reset(); @@ -691,15 +696,11 @@ class SipSessionGroup implements SipListener { return true; case Response.UNAUTHORIZED: case Response.PROXY_AUTHENTICATION_REQUIRED: - String nonce = getNonceFromResponse(response); - if (((nonce != null) && nonce.equals(mLastNonce)) || - (nonce == mLastNonce)) { + if (!handleAuthentication(event)) { Log.v(TAG, "Incorrect username/password"); reset(); - onRegistrationFailed(createCallbackException(response)); - } else { - mSipHelper.handleChallenge(event, getAccountManager()); - mLastNonce = nonce; + onRegistrationFailed(SipErrorCode.INVALID_CREDENTIALS, + "incorrect username or password"); } return true; default: @@ -713,6 +714,23 @@ class SipSessionGroup implements SipListener { return false; } + private boolean handleAuthentication(ResponseEvent event) + throws SipException { + Response response = event.getResponse(); + String nonce = getNonceFromResponse(response); + if (((nonce != null) && nonce.equals(mLastNonce)) || + (nonce == mLastNonce)) { + Log.v(TAG, "Incorrect username/password"); + return false; + } else { + mClientTransaction = mSipHelper.handleChallenge( + event, getAccountManager()); + mDialog = mClientTransaction.getDialog(); + mLastNonce = nonce; + return true; + } + } + private AccountManager getAccountManager() { return new AccountManager() { public UserCredentials getCredentials(ClientTransaction @@ -833,14 +851,12 @@ class SipSessionGroup implements SipListener { establishCall(); return true; case Response.PROXY_AUTHENTICATION_REQUIRED: - mClientTransaction = mSipHelper.handleChallenge( - (ResponseEvent) evt, getAccountManager()); - mDialog = mClientTransaction.getDialog(); - addSipSession(this); - return true; - case Response.BUSY_HERE: - reset(); - mProxy.onCallBusy(this); + if (handleAuthentication(event)) { + addSipSession(this); + } else { + endCallOnError(SipErrorCode.INVALID_CREDENTIALS, + "incorrect username or password"); + } return true; case Response.REQUEST_PENDING: // TODO: @@ -849,7 +865,7 @@ class SipSessionGroup implements SipListener { default: if (statusCode >= 400) { // error: an ack is sent automatically by the stack - onError(createCallbackException(response)); + onError(response); return true; } else if (statusCode >= 300) { // TODO: handle 3xx (redirect) @@ -933,9 +949,13 @@ class SipSessionGroup implements SipListener { return false; } + private String createErrorMessage(Response response) { + return String.format(SERVER_ERROR_PREFIX + "%s (%d)", + response.getReasonPhrase(), response.getStatusCode()); + } + private Exception createCallbackException(Response response) { - return new SipException(String.format("Response: %s (%d)", - response.getReasonPhrase(), response.getStatusCode())); + return new SipException(createErrorMessage(response)); } private void establishCall() { @@ -946,8 +966,9 @@ class SipSessionGroup implements SipListener { private void fallbackToPreviousInCall(Throwable exception) { mState = SipSessionState.IN_CALL; - mProxy.onCallChangeFailed(this, exception.getClass().getName(), - exception.getMessage()); + exception = getRootCause(exception); + mProxy.onCallChangeFailed(this, getErrorCode(exception).toString(), + exception.toString()); } private void endCallNormally() { @@ -956,9 +977,18 @@ class SipSessionGroup implements SipListener { } private void endCallOnError(Throwable exception) { + exception = getRootCause(exception); + endCallOnError(getErrorCode(exception), exception.toString()); + } + + private void endCallOnError(SipErrorCode errorCode, String message) { reset(); - mProxy.onError(this, exception.getClass().getName(), - exception.getMessage()); + mProxy.onError(this, errorCode.toString(), message); + } + + private void endCallOnBusy() { + reset(); + mProxy.onCallBusy(this); } private void onError(Throwable exception) { @@ -969,13 +999,72 @@ class SipSessionGroup implements SipListener { } } + private void onError(Response response) { + if (mInCall) { + fallbackToPreviousInCall(createCallbackException(response)); + } else { + int statusCode = response.getStatusCode(); + if ((statusCode == Response.TEMPORARILY_UNAVAILABLE) + || (statusCode == Response.BUSY_HERE)) { + endCallOnBusy(); + } else { + endCallOnError(getErrorCode(statusCode), + createErrorMessage(response)); + } + } + } + + private SipErrorCode getErrorCode(int responseStatusCode) { + switch (responseStatusCode) { + case Response.NOT_FOUND: + case Response.ADDRESS_INCOMPLETE: + return SipErrorCode.INVALID_REMOTE_URI; + case Response.REQUEST_TIMEOUT: + return SipErrorCode.TIME_OUT; + default: + if (responseStatusCode < 500) { + return SipErrorCode.CLIENT_ERROR; + } else { + return SipErrorCode.SERVER_ERROR; + } + } + } + + private Throwable getRootCause(Throwable exception) { + Throwable cause = exception.getCause(); + while (cause != null) { + exception = cause; + cause = exception.getCause(); + } + return exception; + } + + private SipErrorCode getErrorCode(Throwable exception) { + String message = exception.getMessage(); + if (exception instanceof UnknownHostException) { + return SipErrorCode.INVALID_REMOTE_URI; + } else if (exception instanceof IOException) { + return SipErrorCode.SOCKET_ERROR; + } else if (message.startsWith(SERVER_ERROR_PREFIX)) { + return SipErrorCode.SERVER_ERROR; + } else { + return SipErrorCode.CLIENT_ERROR; + } + } + private void onRegistrationDone(int duration) { mProxy.onRegistrationDone(this, duration); } + private void onRegistrationFailed(SipErrorCode errorCode, + String message) { + mProxy.onRegistrationFailed(this, errorCode.toString(), message); + } + private void onRegistrationFailed(Throwable exception) { - mProxy.onRegistrationFailed(this, exception.getClass().getName(), - exception.getMessage()); + exception = getRootCause(exception); + onRegistrationFailed(getErrorCode(exception), + exception.toString()); } } diff --git a/services/java/com/android/server/sip/SipSessionListenerProxy.java b/services/java/com/android/server/sip/SipSessionListenerProxy.java index 7234196..747d79f 100644 --- a/services/java/com/android/server/sip/SipSessionListenerProxy.java +++ b/services/java/com/android/server/sip/SipSessionListenerProxy.java @@ -124,12 +124,12 @@ class SipSessionListenerProxy extends ISipSessionListener.Stub { } public void onCallChangeFailed(final ISipSession session, - final String className, final String message) { + final String errorCode, final String message) { if (mListener == null) return; proxy(new Runnable() { public void run() { try { - mListener.onCallChangeFailed(session, className, message); + mListener.onCallChangeFailed(session, errorCode, message); } catch (Throwable t) { handle(t, "onCallChangeFailed()"); } @@ -137,13 +137,13 @@ class SipSessionListenerProxy extends ISipSessionListener.Stub { }); } - public void onError(final ISipSession session, final String className, + public void onError(final ISipSession session, final String errorCode, final String message) { if (mListener == null) return; proxy(new Runnable() { public void run() { try { - mListener.onError(session, className, message); + mListener.onError(session, errorCode, message); } catch (Throwable t) { handle(t, "onError()"); } @@ -179,12 +179,12 @@ class SipSessionListenerProxy extends ISipSessionListener.Stub { } public void onRegistrationFailed(final ISipSession session, - final String className, final String message) { + final String errorCode, final String message) { if (mListener == null) return; proxy(new Runnable() { public void run() { try { - mListener.onRegistrationFailed(session, className, message); + mListener.onRegistrationFailed(session, errorCode, message); } catch (Throwable t) { handle(t, "onRegistrationFailed()"); } diff --git a/telephony/java/com/android/internal/telephony/Connection.java b/telephony/java/com/android/internal/telephony/Connection.java index 11d0b1b..e5456e4 100644 --- a/telephony/java/com/android/internal/telephony/Connection.java +++ b/telephony/java/com/android/internal/telephony/Connection.java @@ -39,6 +39,7 @@ public abstract class Connection { CONGESTION, /* outgoing call to congested network */ MMI, /* not presently used; dial() returns null */ INVALID_NUMBER, /* invalid dial string */ + INVALID_CREDENTIALS, /* invalid credentials */ LOST_SIGNAL, LIMIT_EXCEEDED, /* eg GSM ACM limit exceeded */ INCOMING_REJECTED, /* an incoming call that was rejected */ diff --git a/telephony/java/com/android/internal/telephony/sip/SipPhone.java b/telephony/java/com/android/internal/telephony/sip/SipPhone.java index 6eb619a..bf1b939 100755 --- a/telephony/java/com/android/internal/telephony/sip/SipPhone.java +++ b/telephony/java/com/android/internal/telephony/sip/SipPhone.java @@ -23,6 +23,7 @@ import android.net.Uri; import android.net.rtp.AudioGroup; import android.net.rtp.AudioStream; import android.net.sip.SipAudioCall; +import android.net.sip.SipErrorCode; import android.net.sip.SipManager; import android.net.sip.SipProfile; import android.net.sip.SipSessionState; @@ -633,16 +634,15 @@ public class SipPhone extends SipPhoneBase { } @Override - protected void onError(String errorMessage) { - Log.w(LOG_TAG, "SIP error: " + errorMessage); + protected void onError(DisconnectCause cause) { + Log.w(LOG_TAG, "SIP error: " + cause); if (mSipAudioCall.isInCall()) { // Don't end the call when in call. // TODO: how to deliver the error to PhoneApp return; } - // FIXME: specify error - onCallEnded(DisconnectCause.ERROR_UNSPECIFIED); + onCallEnded(cause); } }; @@ -807,7 +807,7 @@ public class SipPhone extends SipPhoneBase { private abstract class SipAudioCallAdapter extends SipAudioCall.Adapter { protected abstract void onCallEnded(Connection.DisconnectCause cause); - protected abstract void onError(String errorMessage); + protected abstract void onError(Connection.DisconnectCause cause); @Override public void onCallEnded(SipAudioCall call) { @@ -820,8 +820,24 @@ public class SipPhone extends SipPhoneBase { } @Override - public void onError(SipAudioCall call, String errorMessage) { - onError(errorMessage); + public void onError(SipAudioCall call, String errorCode, + String errorMessage) { + switch (Enum.valueOf(SipErrorCode.class, errorCode)) { + case INVALID_REMOTE_URI: + onError(Connection.DisconnectCause.INVALID_NUMBER); + break; + case TIME_OUT: + onError(Connection.DisconnectCause.CONGESTION); + break; + case INVALID_CREDENTIALS: + onError(Connection.DisconnectCause.INVALID_CREDENTIALS); + break; + case SOCKET_ERROR: + case SERVER_ERROR: + case CLIENT_ERROR: + default: + onError(Connection.DisconnectCause.ERROR_UNSPECIFIED); + } } } } diff --git a/voip/java/android/net/sip/ISipSessionListener.aidl b/voip/java/android/net/sip/ISipSessionListener.aidl index c552a57..0a6220b 100644 --- a/voip/java/android/net/sip/ISipSessionListener.aidl +++ b/voip/java/android/net/sip/ISipSessionListener.aidl @@ -76,20 +76,20 @@ interface ISipSessionListener { * termination. * * @param session the session object that carries out the transaction - * @param errorClass name of the exception class + * @param errorCode error code defined in {@link SipErrorCode} * @param errorMessage error message */ - void onError(in ISipSession session, String errorClass, + void onError(in ISipSession session, String errorCode, String errorMessage); /** * Called when an error occurs during session modification negotiation. * * @param session the session object that carries out the transaction - * @param errorClass name of the exception class + * @param errorCode error code defined in {@link SipErrorCode} * @param errorMessage error message */ - void onCallChangeFailed(in ISipSession session, String errorClass, + void onCallChangeFailed(in ISipSession session, String errorCode, String errorMessage); /** @@ -111,10 +111,10 @@ interface ISipSessionListener { * Called when the registration fails. * * @param session the session object that carries out the transaction - * @param errorClass name of the exception class + * @param errorCode error code defined in {@link SipErrorCode} * @param errorMessage error message */ - void onRegistrationFailed(in ISipSession session, String errorClass, + void onRegistrationFailed(in ISipSession session, String errorCode, String errorMessage); /** diff --git a/voip/java/android/net/sip/SipAudioCall.java b/voip/java/android/net/sip/SipAudioCall.java index 8254543..02f82b3 100644 --- a/voip/java/android/net/sip/SipAudioCall.java +++ b/voip/java/android/net/sip/SipAudioCall.java @@ -90,9 +90,10 @@ public interface SipAudioCall { * Called when an error occurs. * * @param call the call object that carries out the audio call + * @param errorCode error code defined in {@link SipErrorCode} * @param errorMessage error message */ - void onError(SipAudioCall call, String errorMessage); + void onError(SipAudioCall call, String errorCode, String errorMessage); } /** @@ -126,7 +127,8 @@ public interface SipAudioCall { public void onCallHeld(SipAudioCall call) { onChanged(call); } - public void onError(SipAudioCall call, String errorMessage) { + public void onError(SipAudioCall call, String errorCode, + String errorMessage) { onChanged(call); } } diff --git a/voip/java/android/net/sip/SipAudioCallImpl.java b/voip/java/android/net/sip/SipAudioCallImpl.java index a312f83..2e2ca5b 100644 --- a/voip/java/android/net/sip/SipAudioCallImpl.java +++ b/voip/java/android/net/sip/SipAudioCallImpl.java @@ -108,7 +108,8 @@ public class SipAudioCallImpl extends SipSessionAdapter listener.onCalling(this); break; default: - listener.onError(this, "wrong state to attach call: " + state); + listener.onError(this, SipErrorCode.CLIENT_ERROR.toString(), + "wrong state to attach call: " + state); } } catch (Throwable t) { Log.e(TAG, "setListener()", t); @@ -275,14 +276,13 @@ public class SipAudioCallImpl extends SipSessionAdapter } @Override - public void onCallChangeFailed(ISipSession session, - String className, String message) { + public void onCallChangeFailed(ISipSession session, String errorCode, + String message) { Log.d(TAG, "sip call change failed: " + message); Listener listener = mListener; if (listener != null) { try { - listener.onError(SipAudioCallImpl.this, - className + ": " + message); + listener.onError(SipAudioCallImpl.this, errorCode, message); } catch (Throwable t) { Log.e(TAG, "onCallBusy()", t); } @@ -290,17 +290,16 @@ public class SipAudioCallImpl extends SipSessionAdapter } @Override - public void onError(ISipSession session, String className, + public void onError(ISipSession session, String errorCode, String message) { - Log.d(TAG, "sip session error: " + className + ": " + message); + Log.d(TAG, "sip session error: " + errorCode + ": " + message); synchronized (this) { if (!isInCall()) close(true); } Listener listener = mListener; if (listener != null) { try { - listener.onError(SipAudioCallImpl.this, - className + ": " + message); + listener.onError(SipAudioCallImpl.this, errorCode, message); } catch (Throwable t) { Log.e(TAG, "onError()", t); } diff --git a/voip/java/android/net/sip/SipErrorCode.java b/voip/java/android/net/sip/SipErrorCode.java new file mode 100644 index 0000000..2eb67e8 --- /dev/null +++ b/voip/java/android/net/sip/SipErrorCode.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.sip; + +/** + * Defines error code returned in + * {@link SipRegistrationListener#onRegistrationFailed(String, String, String)}, + * {@link ISipSessionListener#onError(ISipSession, String, String)}, + * {@link ISipSessionListener#onCallChangeFailed(ISipSession, String, String)} and + * {@link ISipSessionListener#onRegistrationFailed(ISipSession, String, String)}. + * @hide + */ +public enum SipErrorCode { + /** When some socket error occurs. */ + SOCKET_ERROR, + + /** When server responds with an error. */ + SERVER_ERROR, + + /** When some error occurs on the device, possibly due to a bug. */ + CLIENT_ERROR, + + /** When the transaction gets timed out. */ + TIME_OUT, + + /** When the remote URI is not valid. */ + INVALID_REMOTE_URI, + + /** When invalid credentials are provided. */ + INVALID_CREDENTIALS; +} diff --git a/voip/java/android/net/sip/SipRegistrationListener.java b/voip/java/android/net/sip/SipRegistrationListener.java index 63faaf8..22488d7 100644 --- a/voip/java/android/net/sip/SipRegistrationListener.java +++ b/voip/java/android/net/sip/SipRegistrationListener.java @@ -40,9 +40,9 @@ public interface SipRegistrationListener { * Called when the registration fails. * * @param localProfileUri the URI string of the SIP profile to register with - * @param errorClass name of the exception class + * @param errorCode error code defined in {@link SipErrorCode} * @param errorMessage error message */ - void onRegistrationFailed(String localProfileUri, String errorClass, + void onRegistrationFailed(String localProfileUri, String errorCode, String errorMessage); } diff --git a/voip/java/android/net/sip/SipSessionAdapter.java b/voip/java/android/net/sip/SipSessionAdapter.java index 770d4eb..6020f2c 100644 --- a/voip/java/android/net/sip/SipSessionAdapter.java +++ b/voip/java/android/net/sip/SipSessionAdapter.java @@ -42,14 +42,11 @@ public class SipSessionAdapter extends ISipSessionListener.Stub { public void onCallBusy(ISipSession session) { } - public void onCallChanged(ISipSession session, byte[] sessionDescription) { - } - - public void onCallChangeFailed(ISipSession session, String className, + public void onCallChangeFailed(ISipSession session, String errorCode, String message) { } - public void onError(ISipSession session, String className, String message) { + public void onError(ISipSession session, String errorCode, String message) { } public void onRegistering(ISipSession session) { @@ -58,7 +55,7 @@ public class SipSessionAdapter extends ISipSessionListener.Stub { public void onRegistrationDone(ISipSession session, int duration) { } - public void onRegistrationFailed(ISipSession session, String className, + public void onRegistrationFailed(ISipSession session, String errorCode, String message) { } |