Merge "Use PhoneBase in the phone list." into gingerbread
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index fd6769c..deba70c 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -3542,10 +3542,10 @@
             for (int i = 0; i < childCount; i++) {
                 final View child = getChildAt(i);
                 if (child.isOverlayEnabled()) {
-                    canvas.translate(child.mLeft + child.mScrollX, child.mTop + child.mScrollY);
+                    canvas.translate(child.mLeft - child.mScrollX, child.mTop - child.mScrollY);
                     child.onDrawOverlay(canvas);
-                    canvas.translate(-(child.mLeft + child.mScrollX),
-                            -(child.mTop + child.mScrollY));
+                    canvas.translate(-(child.mLeft - child.mScrollX),
+                            -(child.mTop - child.mScrollY));
                 }
             }
         }
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index a14d004..a09b0c8 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -7727,8 +7727,9 @@
             bounds.bottom = bounds.top + drawableHeight;
 
             convertFromViewportToContentCoordinates(bounds);
+            invalidate();
             mDrawable.setBounds(bounds);
-            postInvalidate();
+            invalidate();
         }
 
         boolean hasFingerOn(float x, float y) {
@@ -7745,9 +7746,16 @@
             return Rect.intersects(mDrawable.getBounds(), fingerRect);
         }
 
+        void invalidate() {
+            final Rect bounds = mDrawable.getBounds();
+            TextView.this.invalidate(bounds.left, bounds.top,
+                    bounds.right, bounds.bottom);
+        }
+
         void postInvalidate() {
             final Rect bounds = mDrawable.getBounds();
-            TextView.this.postInvalidate(bounds.left, bounds.top, bounds.right, bounds.bottom);
+            TextView.this.postInvalidate(bounds.left, bounds.top,
+                    bounds.right, bounds.bottom);
         }
 
         void postInvalidateDelayed(long delay) {
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index d565c68..720dc97 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -342,4 +342,6 @@
     <!-- 2 means give warning -->
     <integer name="config_datause_notification_type">2</integer>
 
+    <!-- Enables SIP on WIFI only -->
+    <bool name="config_sip_wifi_only">false</bool>
 </resources>
diff --git a/libs/utils/Looper.cpp b/libs/utils/Looper.cpp
index 4aa50d6..b46279e 100644
--- a/libs/utils/Looper.cpp
+++ b/libs/utils/Looper.cpp
@@ -184,44 +184,50 @@
 #if DEBUG_POLL_AND_WAKE
     LOGD("%p ~ pollOnce - handling events from %d fds", this, eventCount);
 #endif
-    { // acquire lock
-        AutoMutex _l(mLock);
-        for (int i = 0; i < eventCount; i++) {
-            int fd = eventItems[i].data.fd;
-            uint32_t epollEvents = eventItems[i].events;
-            if (fd == mWakeReadPipeFd) {
-                if (epollEvents & EPOLLIN) {
+    bool acquiredLock = false;
+    for (int i = 0; i < eventCount; i++) {
+        int fd = eventItems[i].data.fd;
+        uint32_t epollEvents = eventItems[i].events;
+        if (fd == mWakeReadPipeFd) {
+            if (epollEvents & EPOLLIN) {
 #if DEBUG_POLL_AND_WAKE
-                    LOGD("%p ~ pollOnce - awoken", this);
+                LOGD("%p ~ pollOnce - awoken", this);
 #endif
-                    char buffer[16];
-                    ssize_t nRead;
-                    do {
-                        nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer));
-                    } while ((nRead == -1 && errno == EINTR) || nRead == sizeof(buffer));
-                } else {
-                    LOGW("Ignoring unexpected epoll events 0x%x on wake read pipe.", epollEvents);
-                }
+                char buffer[16];
+                ssize_t nRead;
+                do {
+                    nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer));
+                } while ((nRead == -1 && errno == EINTR) || nRead == sizeof(buffer));
             } else {
-                ssize_t requestIndex = mRequests.indexOfKey(fd);
-                if (requestIndex >= 0) {
-                    int events = 0;
-                    if (epollEvents & EPOLLIN) events |= ALOOPER_EVENT_INPUT;
-                    if (epollEvents & EPOLLOUT) events |= ALOOPER_EVENT_OUTPUT;
-                    if (epollEvents & EPOLLERR) events |= ALOOPER_EVENT_ERROR;
-                    if (epollEvents & EPOLLHUP) events |= ALOOPER_EVENT_HANGUP;
+                LOGW("Ignoring unexpected epoll events 0x%x on wake read pipe.", epollEvents);
+            }
+        } else {
+            if (! acquiredLock) {
+                mLock.lock();
+                acquiredLock = true;
+            }
 
-                    Response response;
-                    response.events = events;
-                    response.request = mRequests.valueAt(requestIndex);
-                    mResponses.push(response);
-                } else {
-                    LOGW("Ignoring unexpected epoll events 0x%x on fd %d that is "
-                            "no longer registered.", epollEvents, fd);
-                }
+            ssize_t requestIndex = mRequests.indexOfKey(fd);
+            if (requestIndex >= 0) {
+                int events = 0;
+                if (epollEvents & EPOLLIN) events |= ALOOPER_EVENT_INPUT;
+                if (epollEvents & EPOLLOUT) events |= ALOOPER_EVENT_OUTPUT;
+                if (epollEvents & EPOLLERR) events |= ALOOPER_EVENT_ERROR;
+                if (epollEvents & EPOLLHUP) events |= ALOOPER_EVENT_HANGUP;
+
+                Response response;
+                response.events = events;
+                response.request = mRequests.valueAt(requestIndex);
+                mResponses.push(response);
+            } else {
+                LOGW("Ignoring unexpected epoll events 0x%x on fd %d that is "
+                        "no longer registered.", epollEvents, fd);
             }
         }
     }
+    if (acquiredLock) {
+        mLock.unlock();
+    }
 
     for (size_t i = 0; i < mResponses.size(); i++) {
         const Response& response = mResponses.itemAt(i);
diff --git a/opengl/libs/EGL/Loader.cpp b/opengl/libs/EGL/Loader.cpp
index 5d6ac26..747c829 100644
--- a/opengl/libs/EGL/Loader.cpp
+++ b/opengl/libs/EGL/Loader.cpp
@@ -136,30 +136,24 @@
      */
     
     void* dso;
-    char path[PATH_MAX];
     int index = int(display);
     driver_t* hnd = 0;
-    const char* const format = "/system/lib/egl/lib%s_%s.so";
     
     char const* tag = getTag(index, impl);
     if (tag) {
-        snprintf(path, PATH_MAX, format, "GLES", tag);
-        dso = load_driver(path, cnx, EGL | GLESv1_CM | GLESv2);
+        dso = load_driver("GLES", tag, cnx, EGL | GLESv1_CM | GLESv2);
         if (dso) {
             hnd = new driver_t(dso);
         } else {
             // Always load EGL first
-            snprintf(path, PATH_MAX, format, "EGL", tag);
-            dso = load_driver(path, cnx, EGL);
+            dso = load_driver("EGL", tag, cnx, EGL);
             if (dso) {
                 hnd = new driver_t(dso);
 
                 // TODO: make this more automated
-                snprintf(path, PATH_MAX, format, "GLESv1_CM", tag);
-                hnd->set( load_driver(path, cnx, GLESv1_CM), GLESv1_CM );
+                hnd->set( load_driver("GLESv1_CM", tag, cnx, GLESv1_CM), GLESv1_CM );
 
-                snprintf(path, PATH_MAX, format, "GLESv2", tag);
-                hnd->set( load_driver(path, cnx, GLESv2), GLESv2 );
+                hnd->set( load_driver("GLESv2", tag, cnx, GLESv2), GLESv2 );
             }
         }
     }
@@ -222,12 +216,20 @@
     }
 }
 
-void *Loader::load_driver(const char* driver_absolute_path,
+void *Loader::load_driver(const char* kind, const char *tag,
         egl_connection_t* cnx, uint32_t mask)
 {
+    char driver_absolute_path[PATH_MAX];
+    const char* const search1 = "/vendor/lib/egl/lib%s_%s.so";
+    const char* const search2 = "/system/lib/egl/lib%s_%s.so";
+
+    snprintf(driver_absolute_path, PATH_MAX, search1, kind, tag);
     if (access(driver_absolute_path, R_OK)) {
-        // this happens often, we don't want to log an error
-        return 0;
+        snprintf(driver_absolute_path, PATH_MAX, search2, kind, tag);
+        if (access(driver_absolute_path, R_OK)) {
+            // this happens often, we don't want to log an error
+            return 0;
+        }
     }
 
     void* dso = dlopen(driver_absolute_path, RTLD_NOW | RTLD_LOCAL);
diff --git a/opengl/libs/EGL/Loader.h b/opengl/libs/EGL/Loader.h
index 8659b0b..580d6e4 100644
--- a/opengl/libs/EGL/Loader.h
+++ b/opengl/libs/EGL/Loader.h
@@ -74,7 +74,7 @@
     
 private:
     Loader();
-    void *load_driver(const char* driver, egl_connection_t* cnx, uint32_t mask);
+    void *load_driver(const char* kind, const char *tag, egl_connection_t* cnx, uint32_t mask);
 
     static __attribute__((noinline))
     void init_api(void* dso, 
diff --git a/services/java/com/android/server/connectivity/Tethering.java b/services/java/com/android/server/connectivity/Tethering.java
index eb0a8a9..1bc5e4b 100644
--- a/services/java/com/android/server/connectivity/Tethering.java
+++ b/services/java/com/android/server/connectivity/Tethering.java
@@ -35,6 +35,7 @@
 import android.net.NetworkUtils;
 import android.os.Binder;
 import android.os.Environment;
+import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.IBinder;
 import android.os.INetworkManagementService;
@@ -110,6 +111,14 @@
     private boolean mUsbMassStorageOff;  // track the status of USB Mass Storage
     private boolean mUsbConnected;       // track the status of USB connection
 
+    // mUsbHandler message
+    static final int USB_STATE_CHANGE = 1;
+    static final int USB_DISCONNECTED = 0;
+    static final int USB_CONNECTED = 1;
+
+    // Time to delay before processing USB disconnect events
+    static final long USB_DISCONNECT_DELAY = 1000;
+
     public Tethering(Context context, Looper looper) {
         Log.d(TAG, "Tethering starting");
         mContext = context;
@@ -421,12 +430,25 @@
         }
     }
 
+    private Handler mUsbHandler = new Handler() {
+        @Override
+        public void handleMessage(Message msg) {
+            mUsbConnected = (msg.arg1 == USB_CONNECTED);
+            updateUsbStatus();
+        }
+    };
+
     private class StateReceiver extends BroadcastReceiver {
         public void onReceive(Context content, Intent intent) {
             String action = intent.getAction();
             if (action.equals(Usb.ACTION_USB_STATE)) {
-                mUsbConnected = intent.getExtras().getBoolean(Usb.USB_CONNECTED);
-                updateUsbStatus();
+                // process connect events immediately, but delay handling disconnects
+                // to debounce USB configuration changes
+                boolean connected = intent.getExtras().getBoolean(Usb.USB_CONNECTED);
+                Message msg = Message.obtain(mUsbHandler, USB_STATE_CHANGE,
+                        (connected ? USB_CONNECTED : USB_DISCONNECTED), 0);
+                mUsbHandler.removeMessages(USB_STATE_CHANGE);
+                mUsbHandler.sendMessageDelayed(msg, connected ? 0 : USB_DISCONNECT_DELAY);
             } else if (action.equals(Intent.ACTION_MEDIA_SHARED)) {
                 mUsbMassStorageOff = false;
                 updateUsbStatus();
diff --git a/services/java/com/android/server/sip/SipService.java b/services/java/com/android/server/sip/SipService.java
index d7747fb..a2ebc69 100644
--- a/services/java/com/android/server/sip/SipService.java
+++ b/services/java/com/android/server/sip/SipService.java
@@ -35,6 +35,10 @@
 import android.net.wifi.WifiManager;
 import android.os.Binder;
 import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.text.TextUtils;
@@ -71,6 +75,9 @@
     private boolean mConnected;
     private WakeupTimer mTimer;
     private WifiManager.WifiLock mWifiLock;
+    private boolean mWifiOnly;
+
+    private MyExecutor mExecutor;
 
     // SipProfile URI --> group
     private Map<String, SipSessionGroupExt> mSipGroups =
@@ -99,6 +106,13 @@
                 new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
 
         mTimer = new WakeupTimer(context);
+        mWifiOnly = SipManager.isSipWifiOnly(context);
+    }
+
+    private MyExecutor getExecutor() {
+        // create mExecutor lazily
+        if (mExecutor == null) mExecutor = new MyExecutor();
+        return mExecutor;
     }
 
     public synchronized SipProfile[] getListOfProfiles() {
@@ -474,10 +488,10 @@
         }
 
         @Override
-        public void onError(ISipSession session, String errorClass,
+        public void onError(ISipSession session, int errorCode,
                 String message) {
-            if (DEBUG) Log.d(TAG, "sip session error: " + errorClass + ": "
-                    + message);
+            if (DEBUG) Log.d(TAG, "sip session error: "
+                    + SipErrorCode.toString(errorCode) + ": " + message);
         }
 
         public boolean isOpened() {
@@ -507,6 +521,15 @@
         }
 
         public void run() {
+            // delegate to mExecutor
+            getExecutor().addTask(new Runnable() {
+                public void run() {
+                    realRun();
+                }
+            });
+        }
+
+        private void realRun() {
             synchronized (SipService.this) {
                 SipSessionGroup.SipSessionImpl session = mSession.duplicate();
                 if (DEBUG) Log.d(TAG, "~~~ keepalive");
@@ -533,7 +556,7 @@
         private int mBackoff = 1;
         private boolean mRegistered;
         private long mExpiryTime;
-        private SipErrorCode mErrorCode;
+        private int mErrorCode;
         private String mErrorMessage;
 
         private String getAction() {
@@ -589,10 +612,9 @@
                 if (mSession == null) return;
 
                 try {
-                    SipSessionState state = (mSession == null)
+                    int state = (mSession == null)
                             ? SipSessionState.READY_TO_CALL
-                            : Enum.valueOf(
-                                    SipSessionState.class, mSession.getState());
+                            : mSession.getState();
                     if ((state == SipSessionState.REGISTERING)
                             || (state == SipSessionState.DEREGISTERING)) {
                         mProxy.onRegistering(mSession);
@@ -600,12 +622,12 @@
                         int duration = (int)
                                 (mExpiryTime - SystemClock.elapsedRealtime());
                         mProxy.onRegistrationDone(mSession, duration);
-                    } else if (mErrorCode != null) {
+                    } else if (mErrorCode != SipErrorCode.NO_ERROR) {
                         if (mErrorCode == SipErrorCode.TIME_OUT) {
                             mProxy.onRegistrationTimeout(mSession);
                         } else {
-                            mProxy.onRegistrationFailed(mSession,
-                                    mErrorCode.toString(), mErrorMessage);
+                            mProxy.onRegistrationFailed(mSession, mErrorCode,
+                                    mErrorMessage);
                         }
                     }
                 } catch (Throwable t) {
@@ -619,7 +641,16 @@
         }
 
         public void run() {
-            mErrorCode = null;
+            // delegate to mExecutor
+            getExecutor().addTask(new Runnable() {
+                public void run() {
+                    realRun();
+                }
+            });
+        }
+
+        private void realRun() {
+            mErrorCode = SipErrorCode.NO_ERROR;
             mErrorMessage = null;
             if (DEBUG) Log.d(TAG, "~~~ registering");
             synchronized (SipService.this) {
@@ -712,18 +743,15 @@
         }
 
         @Override
-        public void onRegistrationFailed(ISipSession session,
-                String errorCodeString, String message) {
-            SipErrorCode errorCode =
-                    Enum.valueOf(SipErrorCode.class, errorCodeString);
+        public void onRegistrationFailed(ISipSession session, int errorCode,
+                String message) {
             if (DEBUG) Log.d(TAG, "onRegistrationFailed(): " + session + ": "
-                    + errorCode + ": " + message);
+                    + SipErrorCode.toString(errorCode) + ": " + message);
             synchronized (SipService.this) {
                 if (!isStopped() && (session != mSession)) return;
                 mErrorCode = errorCode;
                 mErrorMessage = message;
-                mProxy.onRegistrationFailed(session, errorCode.toString(),
-                        message);
+                mProxy.onRegistrationFailed(session, errorCode, message);
 
                 if (errorCode == SipErrorCode.INVALID_CREDENTIALS) {
                     if (DEBUG) Log.d(TAG, "   pause auto-registration");
@@ -774,6 +802,15 @@
                     String type = netInfo.getTypeName();
                     NetworkInfo.State state = netInfo.getState();
 
+                    if (mWifiOnly && (netInfo.getType() !=
+                            ConnectivityManager.TYPE_WIFI)) {
+                        if (DEBUG) {
+                            Log.d(TAG, "Wifi only, other connectivity ignored: "
+                                    + type);
+                        }
+                        return;
+                    }
+
                     NetworkInfo activeNetInfo = getActiveNetworkInfo();
                     if (DEBUG) {
                         if (activeNetInfo != null) {
@@ -822,7 +859,7 @@
                 if (connected) {
                     if (mTask != null) mTask.cancel();
                     mTask = new MyTimerTask(type, connected);
-                    mTimer.schedule(mTask, 3 * 1000L);
+                    mTimer.schedule(mTask, 2 * 1000L);
                     // TODO: hold wakup lock so that we can finish change before
                     // the device goes to sleep
                 } else {
@@ -845,6 +882,15 @@
 
             @Override
             public void run() {
+                // delegate to mExecutor
+                getExecutor().addTask(new Runnable() {
+                    public void run() {
+                        realRun();
+                    }
+                });
+            }
+
+            private void realRun() {
                 synchronized (SipService.this) {
                     if (mTask != this) {
                         Log.w(TAG, "  unexpected task: " + mNetworkType
@@ -1155,4 +1201,30 @@
             return (this == that);
         }
     }
+
+    // Single-threaded executor
+    private static class MyExecutor extends Handler {
+        MyExecutor() {
+            super(createLooper());
+        }
+
+        private static Looper createLooper() {
+            HandlerThread thread = new HandlerThread("SipService");
+            thread.start();
+            return thread.getLooper();
+        }
+
+        void addTask(Runnable task) {
+            Message.obtain(this, 0/* don't care */, task).sendToTarget();
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            if (msg.obj instanceof Runnable) {
+                ((Runnable) msg.obj).run();
+            } else {
+                Log.w(TAG, "can't handle msg: " + msg);
+            }
+        }
+    }
 }
diff --git a/services/java/com/android/server/sip/SipSessionGroup.java b/services/java/com/android/server/sip/SipSessionGroup.java
index baf9a8e..b4c2241 100644
--- a/services/java/com/android/server/sip/SipSessionGroup.java
+++ b/services/java/com/android/server/sip/SipSessionGroup.java
@@ -300,7 +300,8 @@
             boolean isLoggable = isLoggable(session, event);
             boolean processed = (session != null) && session.process(event);
             if (isLoggable && processed) {
-                Log.d(TAG, "new state after: " + session.mState);
+                Log.d(TAG, "new state after: "
+                        + SipSessionState.toString(session.mState));
             }
         } catch (Throwable e) {
             Log.w(TAG, "event process error: " + event, e);
@@ -331,7 +332,8 @@
 
         public boolean process(EventObject evt) throws SipException {
             if (isLoggable(this, evt)) Log.d(TAG, " ~~~~~   " + this + ": "
-                    + mState + ": processing " + log(evt));
+                    + SipSessionState.toString(mState) + ": processing "
+                    + log(evt));
             if (isRequestEvent(Request.INVITE, evt)) {
                 RequestEvent event = (RequestEvent) evt;
                 SipSessionImpl newSession = new SipSessionImpl(mProxy);
@@ -356,7 +358,7 @@
     class SipSessionImpl extends ISipSession.Stub {
         SipProfile mPeerProfile;
         SipSessionListenerProxy mProxy = new SipSessionListenerProxy();
-        SipSessionState mState = SipSessionState.READY_TO_CALL;
+        int mState = SipSessionState.READY_TO_CALL;
         RequestEvent mInviteReceived;
         Dialog mDialog;
         ServerTransaction mServerTransaction;
@@ -447,8 +449,8 @@
             return null;
         }
 
-        public String getState() {
-            return mState.toString();
+        public int getState() {
+            return mState;
         }
 
         public void setListener(ISipSessionListener listener) {
@@ -521,7 +523,7 @@
             mState = SipSessionState.PINGING;
             try {
                 processCommand(new OptionsCommand());
-                while (SipSessionState.PINGING.equals(mState)) {
+                while (SipSessionState.PINGING == mState) {
                     Thread.sleep(1000);
                 }
             } catch (SipException e) {
@@ -547,7 +549,8 @@
         public String toString() {
             try {
                 String s = super.toString();
-                return s.substring(s.indexOf("@")) + ":" + mState;
+                return s.substring(s.indexOf("@")) + ":"
+                        + SipSessionState.toString(mState);
             } catch (Throwable e) {
                 return super.toString();
             }
@@ -555,7 +558,8 @@
 
         public boolean process(EventObject evt) throws SipException {
             if (isLoggable(this, evt)) Log.d(TAG, " ~~~~~   " + this + ": "
-                    + mState + ": processing " + log(evt));
+                    + SipSessionState.toString(mState) + ": processing "
+                    + log(evt));
             synchronized (SipSessionGroup.this) {
                 if (isClosed()) return false;
 
@@ -570,30 +574,30 @@
                 boolean processed;
 
                 switch (mState) {
-                case REGISTERING:
-                case DEREGISTERING:
+                case SipSessionState.REGISTERING:
+                case SipSessionState.DEREGISTERING:
                     processed = registeringToReady(evt);
                     break;
-                case PINGING:
+                case SipSessionState.PINGING:
                     processed = keepAliveProcess(evt);
                     break;
-                case READY_TO_CALL:
+                case SipSessionState.READY_TO_CALL:
                     processed = readyForCall(evt);
                     break;
-                case INCOMING_CALL:
+                case SipSessionState.INCOMING_CALL:
                     processed = incomingCall(evt);
                     break;
-                case INCOMING_CALL_ANSWERING:
+                case SipSessionState.INCOMING_CALL_ANSWERING:
                     processed = incomingCallToInCall(evt);
                     break;
-                case OUTGOING_CALL:
-                case OUTGOING_CALL_RING_BACK:
+                case SipSessionState.OUTGOING_CALL:
+                case SipSessionState.OUTGOING_CALL_RING_BACK:
                     processed = outgoingCall(evt);
                     break;
-                case OUTGOING_CALL_CANCELING:
+                case SipSessionState.OUTGOING_CALL_CANCELING:
                     processed = outgoingCallToReady(evt);
                     break;
-                case IN_CALL:
+                case SipSessionState.IN_CALL:
                     processed = inCall(evt);
                     break;
                 default:
@@ -640,8 +644,8 @@
         private void processTransactionTerminated(
                 TransactionTerminatedEvent event) {
             switch (mState) {
-                case IN_CALL:
-                case READY_TO_CALL:
+                case SipSessionState.IN_CALL:
+                case SipSessionState.READY_TO_CALL:
                     Log.d(TAG, "Transaction terminated; do nothing");
                     break;
                 default:
@@ -666,18 +670,18 @@
                 return;
             }
             switch (mState) {
-                case REGISTERING:
-                case DEREGISTERING:
+                case SipSessionState.REGISTERING:
+                case SipSessionState.DEREGISTERING:
                     reset();
                     mProxy.onRegistrationTimeout(this);
                     break;
-                case INCOMING_CALL:
-                case INCOMING_CALL_ANSWERING:
-                case OUTGOING_CALL:
-                case OUTGOING_CALL_CANCELING:
+                case SipSessionState.INCOMING_CALL:
+                case SipSessionState.INCOMING_CALL_ANSWERING:
+                case SipSessionState.OUTGOING_CALL:
+                case SipSessionState.OUTGOING_CALL_CANCELING:
                     onError(SipErrorCode.TIME_OUT, event.toString());
                     break;
-                case PINGING:
+                case SipSessionState.PINGING:
                     reset();
                     mReRegisterFlag = true;
                     mState = SipSessionState.READY_TO_CALL;
@@ -753,7 +757,7 @@
                 int statusCode = response.getStatusCode();
                 switch (statusCode) {
                 case Response.OK:
-                    SipSessionState state = mState;
+                    int state = mState;
                     onRegistrationDone((state == SipSessionState.REGISTERING)
                             ? getExpiryTime(((ResponseEvent) evt).getResponse())
                             : -1);
@@ -1062,10 +1066,9 @@
             mProxy.onCallEstablished(this, mPeerSessionDescription);
         }
 
-        private void fallbackToPreviousInCall(SipErrorCode errorCode,
-                String message) {
+        private void fallbackToPreviousInCall(int errorCode, String message) {
             mState = SipSessionState.IN_CALL;
-            mProxy.onCallChangeFailed(this, errorCode.toString(), message);
+            mProxy.onCallChangeFailed(this, errorCode, message);
         }
 
         private void endCallNormally() {
@@ -1073,9 +1076,9 @@
             mProxy.onCallEnded(this);
         }
 
-        private void endCallOnError(SipErrorCode errorCode, String message) {
+        private void endCallOnError(int errorCode, String message) {
             reset();
-            mProxy.onError(this, errorCode.toString(), message);
+            mProxy.onError(this, errorCode, message);
         }
 
         private void endCallOnBusy() {
@@ -1083,11 +1086,11 @@
             mProxy.onCallBusy(this);
         }
 
-        private void onError(SipErrorCode errorCode, String message) {
+        private void onError(int errorCode, String message) {
             cancelSessionTimer();
             switch (mState) {
-                case REGISTERING:
-                case DEREGISTERING:
+                case SipSessionState.REGISTERING:
+                case SipSessionState.DEREGISTERING:
                     onRegistrationFailed(errorCode, message);
                     break;
                 default:
@@ -1115,7 +1118,7 @@
             }
         }
 
-        private SipErrorCode getErrorCode(int responseStatusCode) {
+        private int getErrorCode(int responseStatusCode) {
             switch (responseStatusCode) {
                 case Response.TEMPORARILY_UNAVAILABLE:
                 case Response.FORBIDDEN:
@@ -1151,7 +1154,7 @@
             return exception;
         }
 
-        private SipErrorCode getErrorCode(Throwable exception) {
+        private int getErrorCode(Throwable exception) {
             String message = exception.getMessage();
             if (exception instanceof UnknownHostException) {
                 return SipErrorCode.INVALID_REMOTE_URI;
@@ -1169,10 +1172,9 @@
             mProxy.onRegistrationDone(this, duration);
         }
 
-        private void onRegistrationFailed(SipErrorCode errorCode,
-                String message) {
+        private void onRegistrationFailed(int errorCode, String message) {
             reset();
-            mProxy.onRegistrationFailed(this, errorCode.toString(), message);
+            mProxy.onRegistrationFailed(this, errorCode, message);
         }
 
         private void onRegistrationFailed(Throwable exception) {
@@ -1262,7 +1264,7 @@
     private static boolean isLoggable(SipSessionImpl s) {
         if (s != null) {
             switch (s.mState) {
-                case PINGING:
+                case SipSessionState.PINGING:
                     return DEBUG_PING;
             }
         }
diff --git a/services/java/com/android/server/sip/SipSessionListenerProxy.java b/services/java/com/android/server/sip/SipSessionListenerProxy.java
index 747d79f..a4cd102 100644
--- a/services/java/com/android/server/sip/SipSessionListenerProxy.java
+++ b/services/java/com/android/server/sip/SipSessionListenerProxy.java
@@ -124,7 +124,7 @@
     }
 
     public void onCallChangeFailed(final ISipSession session,
-            final String errorCode, final String message) {
+            final int errorCode, final String message) {
         if (mListener == null) return;
         proxy(new Runnable() {
             public void run() {
@@ -137,7 +137,7 @@
         });
     }
 
-    public void onError(final ISipSession session, final String errorCode,
+    public void onError(final ISipSession session, final int errorCode,
             final String message) {
         if (mListener == null) return;
         proxy(new Runnable() {
@@ -179,7 +179,7 @@
     }
 
     public void onRegistrationFailed(final ISipSession session,
-            final String errorCode, final String message) {
+            final int errorCode, final String message) {
         if (mListener == null) return;
         proxy(new Runnable() {
             public void run() {
diff --git a/telephony/java/com/android/internal/telephony/sip/SipPhone.java b/telephony/java/com/android/internal/telephony/sip/SipPhone.java
index 4887950..0a87ddb 100755
--- a/telephony/java/com/android/internal/telephony/sip/SipPhone.java
+++ b/telephony/java/com/android/internal/telephony/sip/SipPhone.java
@@ -802,15 +802,15 @@
 
     private static Call.State getCallStateFrom(SipAudioCall sipAudioCall) {
         if (sipAudioCall.isOnHold()) return Call.State.HOLDING;
-        SipSessionState sessionState = sipAudioCall.getState();
+        int sessionState = sipAudioCall.getState();
         switch (sessionState) {
-            case READY_TO_CALL:            return Call.State.IDLE;
-            case INCOMING_CALL:
-            case INCOMING_CALL_ANSWERING:  return Call.State.INCOMING;
-            case OUTGOING_CALL:            return Call.State.DIALING;
-            case OUTGOING_CALL_RING_BACK:  return Call.State.ALERTING;
-            case OUTGOING_CALL_CANCELING:  return Call.State.DISCONNECTING;
-            case IN_CALL:                  return Call.State.ACTIVE;
+            case SipSessionState.READY_TO_CALL:            return Call.State.IDLE;
+            case SipSessionState.INCOMING_CALL:
+            case SipSessionState.INCOMING_CALL_ANSWERING:  return Call.State.INCOMING;
+            case SipSessionState.OUTGOING_CALL:            return Call.State.DIALING;
+            case SipSessionState.OUTGOING_CALL_RING_BACK:  return Call.State.ALERTING;
+            case SipSessionState.OUTGOING_CALL_CANCELING:  return Call.State.DISCONNECTING;
+            case SipSessionState.IN_CALL:                  return Call.State.ACTIVE;
             default:
                 Log.w(LOG_TAG, "illegal connection state: " + sessionState);
                 return Call.State.DISCONNECTED;
@@ -823,7 +823,9 @@
 
         @Override
         public void onCallEnded(SipAudioCall call) {
-            onCallEnded(Connection.DisconnectCause.NORMAL);
+            onCallEnded(call.isInCall()
+                    ? Connection.DisconnectCause.NORMAL
+                    : Connection.DisconnectCause.INCOMING_MISSED);
         }
 
         @Override
@@ -832,30 +834,31 @@
         }
 
         @Override
-        public void onError(SipAudioCall call, SipErrorCode errorCode,
+        public void onError(SipAudioCall call, int errorCode,
                 String errorMessage) {
             switch (errorCode) {
-                case PEER_NOT_REACHABLE:
+                case SipErrorCode.PEER_NOT_REACHABLE:
                     onError(Connection.DisconnectCause.NUMBER_UNREACHABLE);
                     break;
-                case INVALID_REMOTE_URI:
+                case SipErrorCode.INVALID_REMOTE_URI:
                     onError(Connection.DisconnectCause.INVALID_NUMBER);
                     break;
-                case TIME_OUT:
-                case TRANSACTION_TERMINTED:
+                case SipErrorCode.TIME_OUT:
+                case SipErrorCode.TRANSACTION_TERMINTED:
                     onError(Connection.DisconnectCause.TIMED_OUT);
                     break;
-                case DATA_CONNECTION_LOST:
+                case SipErrorCode.DATA_CONNECTION_LOST:
                     onError(Connection.DisconnectCause.LOST_SIGNAL);
                     break;
-                case INVALID_CREDENTIALS:
+                case SipErrorCode.INVALID_CREDENTIALS:
                     onError(Connection.DisconnectCause.INVALID_CREDENTIALS);
                     break;
-                case SOCKET_ERROR:
-                case SERVER_ERROR:
-                case CLIENT_ERROR:
+                case SipErrorCode.SOCKET_ERROR:
+                case SipErrorCode.SERVER_ERROR:
+                case SipErrorCode.CLIENT_ERROR:
                 default:
-                    Log.w(LOG_TAG, "error: " + errorCode + ": " + errorMessage);
+                    Log.w(LOG_TAG, "error: " + SipErrorCode.toString(errorCode)
+                            + ": " + errorMessage);
                     onError(Connection.DisconnectCause.ERROR_UNSPECIFIED);
             }
         }
diff --git a/voip/java/android/net/sip/ISipSession.aidl b/voip/java/android/net/sip/ISipSession.aidl
index 5661b8f..2d515db 100644
--- a/voip/java/android/net/sip/ISipSession.aidl
+++ b/voip/java/android/net/sip/ISipSession.aidl
@@ -49,14 +49,11 @@
 
     /**
      * Gets the session state. The value returned must be one of the states in
-     * {@link SipSessionState}. One may convert it to {@link SipSessionState} by
-     * <code>
-     *      Enum.valueOf(SipSessionState.class, session.getState());
-     * </code>
+     * {@link SipSessionState}.
      *
      * @return the session state
      */
-    String getState();
+    int getState();
 
     /**
      * Checks if the session is in a call.
diff --git a/voip/java/android/net/sip/ISipSessionListener.aidl b/voip/java/android/net/sip/ISipSessionListener.aidl
index 0a6220b..5920bca 100644
--- a/voip/java/android/net/sip/ISipSessionListener.aidl
+++ b/voip/java/android/net/sip/ISipSessionListener.aidl
@@ -79,8 +79,7 @@
      * @param errorCode error code defined in {@link SipErrorCode}
      * @param errorMessage error message
      */
-    void onError(in ISipSession session, String errorCode,
-            String errorMessage);
+    void onError(in ISipSession session, int errorCode, String errorMessage);
 
     /**
      * Called when an error occurs during session modification negotiation.
@@ -89,7 +88,7 @@
      * @param errorCode error code defined in {@link SipErrorCode}
      * @param errorMessage error message
      */
-    void onCallChangeFailed(in ISipSession session, String errorCode,
+    void onCallChangeFailed(in ISipSession session, int errorCode,
             String errorMessage);
 
     /**
@@ -114,7 +113,7 @@
      * @param errorCode error code defined in {@link SipErrorCode}
      * @param errorMessage error message
      */
-    void onRegistrationFailed(in ISipSession session, String errorCode,
+    void onRegistrationFailed(in ISipSession session, int errorCode,
             String errorMessage);
 
     /**
diff --git a/voip/java/android/net/sip/SipAudioCall.java b/voip/java/android/net/sip/SipAudioCall.java
index 4abea20..0069fe0 100644
--- a/voip/java/android/net/sip/SipAudioCall.java
+++ b/voip/java/android/net/sip/SipAudioCall.java
@@ -90,9 +90,9 @@
          * @param call the call object that carries out the audio call
          * @param errorCode error code of this error
          * @param errorMessage error message
+         * @see SipErrorCode
          */
-        void onError(SipAudioCall call, SipErrorCode errorCode,
-                String errorMessage);
+        void onError(SipAudioCall call, int errorCode, String errorMessage);
     }
 
     /**
@@ -126,7 +126,7 @@
         public void onCallHeld(SipAudioCall call) {
             onChanged(call);
         }
-        public void onError(SipAudioCall call, SipErrorCode errorCode,
+        public void onError(SipAudioCall call, int errorCode,
                 String errorMessage) {
             onChanged(call);
         }
@@ -318,10 +318,11 @@
 
     /**
      * Gets the state of the {@link ISipSession} that carries this call.
+     * The value returned must be one of the states in {@link SipSessionState}.
      *
      * @return the session state
      */
-    SipSessionState getState();
+    int getState();
 
     /**
      * Gets the {@link ISipSession} that carries this call.
diff --git a/voip/java/android/net/sip/SipAudioCallImpl.java b/voip/java/android/net/sip/SipAudioCallImpl.java
index e61e878..ccf4d15 100644
--- a/voip/java/android/net/sip/SipAudioCallImpl.java
+++ b/voip/java/android/net/sip/SipAudioCallImpl.java
@@ -81,7 +81,7 @@
     private WifiManager mWm;
     private WifiManager.WifiLock mWifiHighPerfLock;
 
-    private SipErrorCode mErrorCode;
+    private int mErrorCode = SipErrorCode.NO_ERROR;
     private String mErrorMessage;
 
     public SipAudioCallImpl(Context context, SipProfile localProfile) {
@@ -100,7 +100,7 @@
         try {
             if ((listener == null) || !callbackImmediately) {
                 // do nothing
-            } else if (mErrorCode != null) {
+            } else if (mErrorCode != SipErrorCode.NO_ERROR) {
                 listener.onError(this, mErrorCode, mErrorMessage);
             } else if (mInCall) {
                 if (mHold) {
@@ -109,18 +109,18 @@
                     listener.onCallEstablished(this);
                 }
             } else {
-                SipSessionState state = getState();
+                int state = getState();
                 switch (state) {
-                    case READY_TO_CALL:
+                    case SipSessionState.READY_TO_CALL:
                         listener.onReadyToCall(this);
                         break;
-                    case INCOMING_CALL:
+                    case SipSessionState.INCOMING_CALL:
                         listener.onRinging(this, getPeerProfile(mSipSession));
                         break;
-                    case OUTGOING_CALL:
+                    case SipSessionState.OUTGOING_CALL:
                         listener.onCalling(this);
                         break;
-                    case OUTGOING_CALL_RING_BACK:
+                    case SipSessionState.OUTGOING_CALL_RING_BACK:
                         listener.onRingingBack(this);
                         break;
                 }
@@ -150,7 +150,7 @@
         mInCall = false;
         mHold = false;
         mSessionId = -1L;
-        mErrorCode = null;
+        mErrorCode = SipErrorCode.NO_ERROR;
         mErrorMessage = null;
 
         if (mSipSession != null) {
@@ -175,10 +175,10 @@
         }
     }
 
-    public synchronized SipSessionState getState() {
+    public synchronized int getState() {
         if (mSipSession == null) return SipSessionState.READY_TO_CALL;
         try {
-            return Enum.valueOf(SipSessionState.class, mSipSession.getState());
+            return mSipSession.getState();
         } catch (RemoteException e) {
             return SipSessionState.REMOTE_ERROR;
         }
@@ -269,7 +269,6 @@
     @Override
     public void onCallEnded(ISipSession session) {
         Log.d(TAG, "sip call ended: " + session);
-        close();
         Listener listener = mListener;
         if (listener != null) {
             try {
@@ -278,12 +277,12 @@
                 Log.e(TAG, "onCallEnded()", t);
             }
         }
+        close();
     }
 
     @Override
     public void onCallBusy(ISipSession session) {
         Log.d(TAG, "sip call busy: " + session);
-        close(false);
         Listener listener = mListener;
         if (listener != null) {
             try {
@@ -292,17 +291,14 @@
                 Log.e(TAG, "onCallBusy()", t);
             }
         }
-    }
-
-    private SipErrorCode getErrorCode(String errorCode) {
-        return Enum.valueOf(SipErrorCode.class, errorCode);
+        close(false);
     }
 
     @Override
-    public void onCallChangeFailed(ISipSession session, String errorCode,
+    public void onCallChangeFailed(ISipSession session, int errorCode,
             String message) {
         Log.d(TAG, "sip call change failed: " + message);
-        mErrorCode = getErrorCode(errorCode);
+        mErrorCode = errorCode;
         mErrorMessage = message;
         Listener listener = mListener;
         if (listener != null) {
@@ -315,17 +311,11 @@
     }
 
     @Override
-    public void onError(ISipSession session, String errorCodeString,
-            String message) {
-        Log.d(TAG, "sip session error: " + errorCodeString + ": " + message);
-        SipErrorCode errorCode = mErrorCode = getErrorCode(errorCodeString);
+    public void onError(ISipSession session, int errorCode, String message) {
+        Log.d(TAG, "sip session error: " + SipErrorCode.toString(errorCode)
+                + ": " + message);
+        mErrorCode = errorCode;
         mErrorMessage = message;
-        synchronized (this) {
-            if ((mErrorCode == SipErrorCode.DATA_CONNECTION_LOST)
-                    || !isInCall()) {
-                close(true);
-            }
-        }
         Listener listener = mListener;
         if (listener != null) {
             try {
@@ -334,6 +324,12 @@
                 Log.e(TAG, "onError()", t);
             }
         }
+        synchronized (this) {
+            if ((errorCode == SipErrorCode.DATA_CONNECTION_LOST)
+                    || !isInCall()) {
+                close(true);
+            }
+        }
     }
 
     public synchronized void attachCall(ISipSession session,
@@ -608,10 +604,10 @@
         try {
             startAudioInternal();
         } catch (UnknownHostException e) {
-            onError(mSipSession, SipErrorCode.PEER_NOT_REACHABLE.toString(),
+            onError(mSipSession, SipErrorCode.PEER_NOT_REACHABLE,
                     e.getMessage());
         } catch (Throwable e) {
-            onError(mSipSession, SipErrorCode.CLIENT_ERROR.toString(),
+            onError(mSipSession, SipErrorCode.CLIENT_ERROR,
                     e.getMessage());
         }
     }
diff --git a/voip/java/android/net/sip/SipErrorCode.java b/voip/java/android/net/sip/SipErrorCode.java
index a27f740..7496d28 100644
--- a/voip/java/android/net/sip/SipErrorCode.java
+++ b/voip/java/android/net/sip/SipErrorCode.java
@@ -24,34 +24,69 @@
  * {@link ISipSessionListener#onRegistrationFailed}.
  * @hide
  */
-public enum SipErrorCode {
+public class SipErrorCode {
+    /** Not an error. */
+    public static final int NO_ERROR = 0;
+
     /** When some socket error occurs. */
-    SOCKET_ERROR,
+    public static final int SOCKET_ERROR = -1;
 
     /** When server responds with an error. */
-    SERVER_ERROR,
+    public static final int SERVER_ERROR = -2;
 
     /** When transaction is terminated unexpectedly. */
-    TRANSACTION_TERMINTED,
+    public static final int TRANSACTION_TERMINTED = -3;
 
     /** When some error occurs on the device, possibly due to a bug. */
-    CLIENT_ERROR,
+    public static final int CLIENT_ERROR = -4;
 
     /** When the transaction gets timed out. */
-    TIME_OUT,
+    public static final int TIME_OUT = -5;
 
     /** When the remote URI is not valid. */
-    INVALID_REMOTE_URI,
+    public static final int INVALID_REMOTE_URI = -6;
 
     /** When the peer is not reachable. */
-    PEER_NOT_REACHABLE,
+    public static final int PEER_NOT_REACHABLE = -7;
 
     /** When invalid credentials are provided. */
-    INVALID_CREDENTIALS,
+    public static final int INVALID_CREDENTIALS = -8;
 
     /** The client is in a transaction and cannot initiate a new one. */
-    IN_PROGRESS,
+    public static final int IN_PROGRESS = -9;
 
     /** When data connection is lost. */
-    DATA_CONNECTION_LOST;
+    public static final int DATA_CONNECTION_LOST = -10;
+
+    public static String toString(int errorCode) {
+        switch (errorCode) {
+            case NO_ERROR:
+                return "NO_ERROR";
+            case SOCKET_ERROR:
+                return "SOCKET_ERROR";
+            case SERVER_ERROR:
+                return "SERVER_ERROR";
+            case TRANSACTION_TERMINTED:
+                return "TRANSACTION_TERMINTED";
+            case CLIENT_ERROR:
+                return "CLIENT_ERROR";
+            case TIME_OUT:
+                return "TIME_OUT";
+            case INVALID_REMOTE_URI:
+                return "INVALID_REMOTE_URI";
+            case PEER_NOT_REACHABLE:
+                return "PEER_NOT_REACHABLE";
+            case INVALID_CREDENTIALS:
+                return "INVALID_CREDENTIALS";
+            case IN_PROGRESS:
+                return "IN_PROGRESS";
+            case DATA_CONNECTION_LOST:
+                return "DATA_CONNECTION_LOST";
+            default:
+                return "UNKNOWN";
+        }
+    }
+
+    private SipErrorCode() {
+    }
 }
diff --git a/voip/java/android/net/sip/SipManager.java b/voip/java/android/net/sip/SipManager.java
index 36895cd..31768d7 100644
--- a/voip/java/android/net/sip/SipManager.java
+++ b/voip/java/android/net/sip/SipManager.java
@@ -97,6 +97,14 @@
          */
     }
 
+    /**
+     * Returns true if SIP is only available on WIFI.
+     */
+    public static boolean isSipWifiOnly(Context context) {
+        return context.getResources().getBoolean(
+                com.android.internal.R.bool.config_sip_wifi_only);
+    }
+
     private SipManager() {
         createSipService();
     }
@@ -503,10 +511,9 @@
         }
 
         @Override
-        public void onRegistrationFailed(ISipSession session, String errorCode,
+        public void onRegistrationFailed(ISipSession session, int errorCode,
                 String message) {
-            mListener.onRegistrationFailed(getUri(session),
-                    Enum.valueOf(SipErrorCode.class, errorCode), message);
+            mListener.onRegistrationFailed(getUri(session), errorCode, message);
         }
 
         @Override
diff --git a/voip/java/android/net/sip/SipRegistrationListener.java b/voip/java/android/net/sip/SipRegistrationListener.java
index 705f271..37c9ce2 100644
--- a/voip/java/android/net/sip/SipRegistrationListener.java
+++ b/voip/java/android/net/sip/SipRegistrationListener.java
@@ -42,7 +42,8 @@
      * @param localProfileUri the URI string of the SIP profile to register with
      * @param errorCode error code of this error
      * @param errorMessage error message
+     * @see SipErrorCode
      */
-    void onRegistrationFailed(String localProfileUri, SipErrorCode errorCode,
+    void onRegistrationFailed(String localProfileUri, int errorCode,
             String errorMessage);
 }
diff --git a/voip/java/android/net/sip/SipSessionAdapter.java b/voip/java/android/net/sip/SipSessionAdapter.java
index 6020f2c..86aca37 100644
--- a/voip/java/android/net/sip/SipSessionAdapter.java
+++ b/voip/java/android/net/sip/SipSessionAdapter.java
@@ -42,11 +42,11 @@
     public void onCallBusy(ISipSession session) {
     }
 
-    public void onCallChangeFailed(ISipSession session, String errorCode,
+    public void onCallChangeFailed(ISipSession session, int errorCode,
             String message) {
     }
 
-    public void onError(ISipSession session, String errorCode, String message) {
+    public void onError(ISipSession session, int errorCode, String message) {
     }
 
     public void onRegistering(ISipSession session) {
@@ -55,7 +55,7 @@
     public void onRegistrationDone(ISipSession session, int duration) {
     }
 
-    public void onRegistrationFailed(ISipSession session, String errorCode,
+    public void onRegistrationFailed(ISipSession session, int errorCode,
             String message) {
     }
 
diff --git a/voip/java/android/net/sip/SipSessionState.java b/voip/java/android/net/sip/SipSessionState.java
index 5bab112..31e9d3f 100644
--- a/voip/java/android/net/sip/SipSessionState.java
+++ b/voip/java/android/net/sip/SipSessionState.java
@@ -20,47 +20,75 @@
  * Defines {@link ISipSession} states.
  * @hide
  */
-public enum SipSessionState {
+public class SipSessionState {
     /** When session is ready to initiate a call or transaction. */
-    READY_TO_CALL,
+    public static final int READY_TO_CALL = 0;
 
     /** When the registration request is sent out. */
-    REGISTERING,
+    public static final int REGISTERING = 1;
 
     /** When the unregistration request is sent out. */
-    DEREGISTERING,
+    public static final int DEREGISTERING = 2;
 
     /** When an INVITE request is received. */
-    INCOMING_CALL,
+    public static final int INCOMING_CALL = 3;
 
     /** When an OK response is sent for the INVITE request received. */
-    INCOMING_CALL_ANSWERING,
+    public static final int INCOMING_CALL_ANSWERING = 4;
 
     /** When an INVITE request is sent. */
-    OUTGOING_CALL,
+    public static final int OUTGOING_CALL = 5;
 
     /** When a RINGING response is received for the INVITE request sent. */
-    OUTGOING_CALL_RING_BACK,
+    public static final int OUTGOING_CALL_RING_BACK = 6;
 
     /** When a CANCEL request is sent for the INVITE request sent. */
-    OUTGOING_CALL_CANCELING,
+    public static final int OUTGOING_CALL_CANCELING = 7;
 
     /** When a call is established. */
-    IN_CALL,
+    public static final int IN_CALL = 8;
 
     /** Some error occurs when making a remote call to {@link ISipSession}. */
-    REMOTE_ERROR,
+    public static final int REMOTE_ERROR = 9;
 
     /** When an OPTIONS request is sent. */
-    PINGING;
+    public static final int PINGING = 10;
+
+    /** Not defined. */
+    public static final int NOT_DEFINED = 101;
 
     /**
-     * Checks if the specified string represents the same state as this object.
-     *
-     * @return true if the specified string represents the same state as this
-     *      object
+     * Converts the state to string.
      */
-    public boolean equals(String state) {
-        return toString().equals(state);
+    public static String toString(int state) {
+        switch (state) {
+            case READY_TO_CALL:
+                return "READY_TO_CALL";
+            case REGISTERING:
+                return "REGISTERING";
+            case DEREGISTERING:
+                return "DEREGISTERING";
+            case INCOMING_CALL:
+                return "INCOMING_CALL";
+            case INCOMING_CALL_ANSWERING:
+                return "INCOMING_CALL_ANSWERING";
+            case OUTGOING_CALL:
+                return "OUTGOING_CALL";
+            case OUTGOING_CALL_RING_BACK:
+                return "OUTGOING_CALL_RING_BACK";
+            case OUTGOING_CALL_CANCELING:
+                return "OUTGOING_CALL_CANCELING";
+            case IN_CALL:
+                return "IN_CALL";
+            case REMOTE_ERROR:
+                return "REMOTE_ERROR";
+            case PINGING:
+                return "PINGING";
+            default:
+                return "NOT_DEFINED";
+        }
+    }
+
+    private SipSessionState() {
     }
 }