Bug 784519 - Part 3: Send Signaling State from SIPCC to PeerConnection r=ekr
authorAdam Roach [:abr] <adam@nostrum.com>
Thu, 16 May 2013 18:41:46 -0500
changeset 145812 017bc63ae2fad7b5faedce1a759c0e1837b1ed3b
parent 145811 0a613c3c8a01469ed71cef015cc57a1cef1aacf9
child 145813 ca78349dea64680fc617361882fde4af2c4ed3d2
push id2697
push userbbajaj@mozilla.com
push dateMon, 05 Aug 2013 18:49:53 +0000
treeherdermozilla-beta@dfec938c7b63 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersekr
bugs784519
milestone24.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 784519 - Part 3: Send Signaling State from SIPCC to PeerConnection r=ekr
CLOBBER
dom/media/PeerConnection.js
dom/media/bridge/IPeerConnection.idl
media/webrtc/signaling/include/CC_CallInfo.h
media/webrtc/signaling/signaling.gyp
media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h
media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp
media/webrtc/signaling/src/sipcc/core/ccapp/CCProvider.h
media/webrtc/signaling/src/sipcc/core/ccapp/ccapi_call_info.c
media/webrtc/signaling/src/sipcc/core/ccapp/ccprovider.c
media/webrtc/signaling/src/sipcc/core/common/ui.c
media/webrtc/signaling/src/sipcc/core/gsm/fsmdef.c
media/webrtc/signaling/src/sipcc/core/gsm/gsm_sdp.c
media/webrtc/signaling/src/sipcc/core/gsm/h/fsm.h
media/webrtc/signaling/src/sipcc/core/includes/sessionTypes.h
media/webrtc/signaling/src/sipcc/core/includes/uiapi.h
media/webrtc/signaling/src/sipcc/include/ccapi_call_info.h
media/webrtc/signaling/src/sipcc/include/fsmdef_states.h
media/webrtc/signaling/src/softphonewrapper/CC_SIPCCCallInfo.cpp
media/webrtc/signaling/src/softphonewrapper/CC_SIPCCCallInfo.h
--- a/CLOBBER
+++ b/CLOBBER
@@ -12,9 +12,9 @@
 #          O               O
 #          |               |
 #          O <-- Clobber   O  <-- Clobber
 #
 # Note: The description below will be part of the error message shown to users.
 #
 # Modifying this file will now automatically clobber the buildbot machines \o/
 #
-Bug 875929 removed a file from js/src/moz.build and apparently the build system didn't notice.
+Bug 784519 causes mystery Android build failures and mochi/crashtest failures
--- a/dom/media/PeerConnection.js
+++ b/dom/media/PeerConnection.js
@@ -760,20 +760,39 @@ RTCPeerConnection.prototype = {
     let sdp = this._getPC().remoteDescription;
     if (sdp.length == 0) {
       return null;
     }
     return new this._win.mozRTCSessionDescription({ type: this._remoteType,
                                                     sdp: sdp });
   },
 
-  get signalingState()     { return "stable"; }, // not yet implemented
   get iceGatheringState()  { return this._iceGatheringState; },
   get iceConnectionState() { return this._iceConnectionState; },
 
+  // Corresponds to constants in IPeerConnection.idl
+  _signalingStateMap: [
+    'invalid',
+    'stable',
+    'have-local-offer',
+    'have-remote-offer',
+    'have-local-pranswer',
+    'have-remote-pranswer',
+    'closed'
+  ],
+
+  get signalingState() {
+    // checking for our local pc closed indication
+    // before invoking the pc methods.
+    if(this._closed) {
+      return "closed";
+    }
+    return this._signalingStateMap[this._getPC().signalingState];
+  },
+
   changeIceGatheringState: function(state) {
     this._iceGatheringState = state;
   },
 
   changeIceConnectionState: function(state) {
     this._iceConnectionState = state;
     this.dispatchEvent(new this._win.Event("iceconnectionstatechange"));
   },
@@ -985,24 +1004,20 @@ PeerConnectionObserver.prototype = {
   },
 
   onAddIceCandidateError: function(code, message) {
     this._dompc._pendingType = null;
     this.callCB(this._dompc._onAddIceCandidateError, new RTCError(code, message));
     this._dompc._executeNext();
   },
 
-  onStateChange: function(state) {
-    if (state != Ci.IPeerConnectionObserver.kIceState) {
-      return;
-    }
-
-    switch (this._dompc._pc.iceState) {
+  handleIceStateChanges: function(iceState) {
+    switch (iceState) {
       case Ci.IPeerConnection.kIceWaiting:
-        this._dompc.changeIceConnectionState("completed");
+        this._dompc.changeIceConnectionState("new");
         this.callCB(this._dompc.ongatheringchange, "complete");
         this.callCB(this._onicechange, "starting");
         // Now that the PC is ready to go, execute any pending operations.
         this._dompc._executeNext();
         break;
       case Ci.IPeerConnection.kIceChecking:
         this._dompc.changeIceConnectionState("checking");
         this.callCB(this._onicechange, "checking");
@@ -1016,17 +1031,43 @@ PeerConnectionObserver.prototype = {
         this._dompc.changeIceConnectionState("connected");
         this.callCB(this._onicechange, "connected");
         break;
       case Ci.IPeerConnection.kIceFailed:
         this._dompc.changeIceConnectionState("failed");
         this.callCB(this._onicechange, "failed");
         break;
       default:
-        // Unknown state!
+        // Unknown ICE state!
+        this._dompc.reportWarning("Unhandled ice state: " + iceState, null, 0);
+        break;
+    }
+  },
+
+  onStateChange: function(state) {
+    switch (state) {
+      case Ci.IPeerConnectionObserver.kSignalingState:
+        this.callCB(this._dompc.onsignalingstatechange,
+                    this._dompc.signalingState);
+        break;
+
+      case Ci.IPeerConnectionObserver.kIceState:
+        this.handleIceStateChanges(this._dompc._pc.iceState);
+        break;
+
+      case Ci.IPeerConnectionObserver.kSdpState:
+        // No-op
+        break;
+
+      case Ci.IPeerConnectionObserver.kSipccState:
+        // No-op
+        break;
+
+      default:
+        this._dompc.reportWarning("Unhandled state type: " + state, null, 0);
         break;
     }
   },
 
   onAddStream: function(stream) {
     this.dispatchEvent(new this._dompc._win.MediaStreamEvent("addstream",
                                                              { stream: stream }));
   },
--- a/dom/media/bridge/IPeerConnection.idl
+++ b/dom/media/bridge/IPeerConnection.idl
@@ -27,16 +27,17 @@ interface IPeerConnectionManager : nsISu
 [scriptable, uuid(85ba28da-53d0-401d-afed-9cad69f727ff)]
 interface IPeerConnectionObserver : nsISupports
 {
   /* Constants */
   const long kReadyState = 0x1;
   const long kIceState = 0x2;
   const long kSdpState = 0x3;
   const long kSipccState = 0x4;
+  const long kSignalingState = 0x5;
 
   /* JSEP callbacks */
   void onCreateOfferSuccess(in string offer);
   void onCreateOfferError(in unsigned long name, in string message);
   void onCreateAnswerSuccess(in string answer);
   void onCreateAnswerError(in unsigned long name, in string message);
   void onSetLocalDescriptionSuccess();
   void onSetRemoteDescriptionSuccess();
@@ -86,16 +87,25 @@ interface IPeerConnection : nsISupports
 
   /* for readyState on Peer Connection */
   const long kNew = 0;
   const long kNegotiating = 1;
   const long kActive = 2;
   const long kClosing = 3;
   const long kClosed = 4;
 
+  /* RTCSignalingState from WebRTC spec */
+  const long kSignalingInvalid            = 0;
+  const long kSignalingStable             = 1;
+  const long kSignalingHaveLocalOffer     = 2;
+  const long kSignalingHaveRemoteOffer    = 3;
+  const long kSignalingHaveLocalPranswer  = 4;
+  const long kSignalingHaveRemotePranswer = 5;
+  const long kSignalingClosed             = 6;
+
   /* for 'type' in DataChannelInit dictionary */
   const unsigned short kDataChannelReliable = 0;
   const unsigned short kDataChannelPartialReliableRexmit = 1;
   const unsigned short kDataChannelPartialReliableTimed = 2;
 
   /* Constants for 'name' in error callbacks */
   const unsigned long kNoError                          = 0; // Test driver only
   const unsigned long kInvalidConstraintsType           = 1;
@@ -139,16 +149,17 @@ interface IPeerConnection : nsISupports
   void close();
 
   /* Attributes */
   readonly attribute string localDescription;
   readonly attribute string remoteDescription;
 
   readonly attribute unsigned long iceState;
   readonly attribute unsigned long readyState;
+  readonly attribute unsigned long signalingState;
   readonly attribute unsigned long sipccState;
 
   /* Data channels */
   nsIDOMDataChannel createDataChannel(in ACString label, in ACString protocol,
     in unsigned short type, in boolean outOfOrderAllowed,
     in unsigned short maxTime, in unsigned short maxNum,
     in boolean externalNegotiated, in unsigned short stream);
   void connectDataConnection(in unsigned short localport,
--- a/media/webrtc/signaling/include/CC_CallInfo.h
+++ b/media/webrtc/signaling/include/CC_CallInfo.h
@@ -4,22 +4,24 @@
 
 #pragma once
 
 #include <set>
 
 extern "C"
 {
 #include "ccapi_types.h"
+#include "fsmdef_states.h"
 }
 
 #include "CC_Common.h"
 #include "CC_CallTypes.h"
 #include "peer_connection_types.h"
 
+
 namespace CSF
 {
 
 	class ECC_API CC_CallInfo
     {
     public:
         NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CC_CallInfo)
     protected:
@@ -39,23 +41,37 @@ namespace CSF
         /**
            get Call state
            @param [in] handle - call info handle
            @return call state
          */
         virtual cc_call_state_t getCallState () = 0;
 
         /**
+           get FSM state
+           @param [in] handle - call info handle
+           @return FSM state
+         */
+        virtual fsmdef_states_t getFsmState () const = 0;
+
+        /**
            print Call state
            @param [in] handle - call info handle
            @return call state as string
          */
         virtual std::string callStateToString (cc_call_state_t state) = 0;
 
         /**
+           print FSM state
+           @param [in] handle - call info handle
+           @return call state as string
+         */
+        virtual std::string fsmStateToString (fsmdef_states_t state) const = 0;
+
+        /**
            print Call event
            @param [in] call event
            @return call event as string
          */
         virtual std::string callEventToString (ccapi_call_event_e callEvent) = 0;
 
         /**
            Get ringer state.
--- a/media/webrtc/signaling/signaling.gyp
+++ b/media/webrtc/signaling/signaling.gyp
@@ -382,16 +382,17 @@
         './src/sipcc/core/includes/configmgr.h',
         './src/sipcc/core/includes/debug.h',
         './src/sipcc/core/includes/dialplan.h',
         './src/sipcc/core/includes/dialplanint.h',
         './src/sipcc/core/includes/digcalc.h',
         './src/sipcc/core/includes/dns_utils.h',
         './src/sipcc/core/includes/dtmf.h',
         './src/sipcc/core/includes/embedded.h',
+        './src/sipcc/core/includes/fsmdef_states.h',
         './src/sipcc/core/includes/intelpentiumtypes.h',
         './src/sipcc/core/includes/kpml_common_util.h',
         './src/sipcc/core/includes/kpmlmap.h',
         './src/sipcc/core/includes/md5.h',
         './src/sipcc/core/includes/memory.h',
         './src/sipcc/core/includes/misc_apps_task.h',
         './src/sipcc/core/includes/misc_util.h',
         './src/sipcc/core/includes/phntask.h',
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
@@ -124,17 +124,19 @@ public:
                                  nsRefPtr<PeerConnectionImpl> aPC,
                                  IPeerConnectionObserver* aObserver)
       : mPC(aPC),
         mObserver(aObserver),
         mCode(static_cast<PeerConnectionImpl::Error>(aInfo->getStatusCode())),
         mReason(aInfo->getStatus()),
         mSdpStr(),
         mCallState(aInfo->getCallState()),
-        mStateStr(aInfo->callStateToString(mCallState)) {
+        mFsmState(aInfo->getFsmState()),
+        mStateStr(aInfo->callStateToString(mCallState)),
+        mFsmStateStr(aInfo->fsmStateToString(mFsmState)) {
     if (mCallState == REMOTESTREAMADD) {
       MediaStreamTable *streams = NULL;
       streams = aInfo->getMediaStreams();
       mRemoteStream = mPC->media()->GetRemoteStream(streams->media_stream_id);
       MOZ_ASSERT(mRemoteStream);
     }
     if ((mCallState == CREATEOFFERSUCCESS) || (mCallState == CREATEANSWERSUCCESS)) {
         mSdpStr = aInfo->getSDP();
@@ -168,17 +170,18 @@ public:
 
     nsCOMPtr<IPeerConnectionObserver> mObserver;
   };
 #endif
 
   NS_IMETHOD Run() {
 
     CSFLogInfo(logTag, "PeerConnectionObserverDispatch processing "
-                       "mCallState = %d (%s)", mCallState, mStateStr.c_str());
+               "mCallState = %d (%s), mFsmState = %d (%s)",
+               mCallState, mStateStr.c_str(), mFsmState, mFsmStateStr.c_str());
 
     if (mCallState == SETLOCALDESCERROR || mCallState == SETREMOTEDESCERROR) {
       const std::vector<std::string> &errors = mPC->GetSdpParseErrors();
       std::vector<std::string>::const_iterator i;
       for (i = errors.begin(); i != errors.end(); ++i) {
         mReason += " | SDP Parsing Error: " + *i;
       }
       if (errors.size()) {
@@ -187,16 +190,33 @@ public:
       mPC->ClearSdpParseErrorMessages();
     }
 
     if (mReason.length()) {
       CSFLogInfo(logTag, "Message contains error: %d: %s",
                  mCode, mReason.c_str());
     }
 
+    /*
+     * While the fsm_states_t (FSM_DEF_*) constants are a proper superset
+     * of SignalingState, and the order in which the SignalingState values
+     * appear matches the order they appear in fsm_states_t, their underlying
+     * numeric representation is different. Hence, we need to perform an
+     * offset calculation to map from one to the other.
+     */
+
+    if (mFsmState >= FSMDEF_S_STABLE && mFsmState <= FSMDEF_S_CLOSED) {
+      int offset = FSMDEF_S_STABLE - PeerConnectionImpl::kSignalingStable;
+      mPC->SetSignalingState_m(
+        static_cast<PeerConnectionImpl::SignalingState>(mFsmState - offset));
+    } else {
+      CSFLogError(logTag, ": **** UNHANDLED SIGNALING STATE : %d (%s)",
+                  mFsmState, mFsmStateStr.c_str());
+    }
+
     switch (mCallState) {
       case CREATEOFFERSUCCESS:
         mObserver->OnCreateOfferSuccess(mSdpStr.c_str());
         break;
 
       case CREATEANSWERSUCCESS:
         mObserver->OnCreateAnswerSuccess(mSdpStr.c_str());
         break;
@@ -285,26 +305,29 @@ public:
 
 private:
   nsRefPtr<PeerConnectionImpl> mPC;
   nsCOMPtr<IPeerConnectionObserver> mObserver;
   PeerConnectionImpl::Error mCode;
   std::string mReason;
   std::string mSdpStr;
   cc_call_state_t mCallState;
+  fsmdef_states_t mFsmState;
   std::string mStateStr;
+  std::string mFsmStateStr;
   nsRefPtr<RemoteSourceStreamInfo> mRemoteStream;
 };
 
 NS_IMPL_THREADSAFE_ISUPPORTS1(PeerConnectionImpl, IPeerConnection)
 
 PeerConnectionImpl::PeerConnectionImpl()
 : mRole(kRoleUnknown)
   , mCall(NULL)
   , mReadyState(kNew)
+  , mSignalingState(kSignalingStable)
   , mIceState(kIceGathering)
   , mPCObserver(NULL)
   , mWindow(NULL)
   , mIdentity(NULL)
   , mSTSThread(NULL)
   , mMedia(NULL)
   , mNumAudioStreams(0)
   , mNumVideoStreams(0)
@@ -1213,16 +1236,26 @@ PeerConnectionImpl::GetReadyState(uint32
   PC_AUTO_ENTER_API_CALL_NO_CHECK();
   MOZ_ASSERT(aState);
 
   *aState = mReadyState;
   return NS_OK;
 }
 
 NS_IMETHODIMP
+PeerConnectionImpl::GetSignalingState(uint32_t* aState)
+{
+  PC_AUTO_ENTER_API_CALL_NO_CHECK();
+  MOZ_ASSERT(aState);
+
+  *aState = mSignalingState;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 PeerConnectionImpl::GetSipccState(uint32_t* aState)
 {
   PC_AUTO_ENTER_API_CALL_NO_CHECK();
   MOZ_ASSERT(aState);
 
   PeerConnectionCtx* pcctx = PeerConnectionCtx::GetInstance();
   *aState = pcctx ? pcctx->sipcc_state() : kIdle;
   return NS_OK;
@@ -1378,16 +1411,32 @@ PeerConnectionImpl::ChangeReadyState(Pee
   }
   RUN_ON_THREAD(mThread, WrapRunnable(pco,
                                       &IPeerConnectionObserver::OnStateChange,
                                       // static_cast needed to work around old Android NDK r5c compiler
                                       static_cast<int>(IPeerConnectionObserver::kReadyState)),
     NS_DISPATCH_NORMAL);
 }
 
+void
+PeerConnectionImpl::SetSignalingState_m(SignalingState aSignalingState)
+{
+  PC_AUTO_ENTER_API_CALL_NO_CHECK();
+  if (mSignalingState == aSignalingState) {
+    return;
+  }
+
+  mSignalingState = aSignalingState;
+  nsCOMPtr<IPeerConnectionObserver> pco = do_QueryReferent(mPCObserver);
+  if (!pco) {
+    return;
+  }
+  pco->OnStateChange(IPeerConnectionObserver::kSignalingState);
+}
+
 PeerConnectionWrapper::PeerConnectionWrapper(const std::string& handle)
     : impl_(nullptr) {
   if (PeerConnectionCtx::GetInstance()->mPeerConnections.find(handle) ==
     PeerConnectionCtx::GetInstance()->mPeerConnections.end()) {
     return;
   }
 
   PeerConnectionImpl *impl = PeerConnectionCtx::GetInstance()->mPeerConnections[handle];
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h
@@ -128,16 +128,28 @@ public:
   enum ReadyState {
     kNew,
     kNegotiating,
     kActive,
     kClosing,
     kClosed
   };
 
+  /* Must match constants in IPeerConnection.idl */
+  /* Must also be int the same order as in fsmdef_states.h */
+  enum SignalingState {
+    kSignalingInvalid            = 0,
+    kSignalingStable             = 1,
+    kSignalingHaveLocalOffer     = 2,
+    kSignalingHaveRemoteOffer    = 3,
+    kSignalingHaveLocalPranswer  = 4,
+    kSignalingHaveRemotePranswer = 5,
+    kSignalingClosed             = 6
+  };
+
   enum SipccState {
     kIdle,
     kStarting,
     kStarted
   };
 
   // TODO(ekr@rtfm.com): make this conform to the specifications
   enum IceState {
@@ -263,16 +275,19 @@ public:
 
   // Called when OnLocal/RemoteDescriptionSuccess/Error
   // is called to start the list over.
   void ClearSdpParseErrorMessages();
 
   // Called to retreive the list of parsing errors.
   const std::vector<std::string> &GetSdpParseErrors();
 
+  // Sets the RTC Signaling State
+  void SetSignalingState_m(SignalingState aSignalingState);
+
 private:
   PeerConnectionImpl(const PeerConnectionImpl&rhs);
   PeerConnectionImpl& operator=(PeerConnectionImpl);
   nsresult Initialize(IPeerConnectionObserver* aObserver,
                       nsIDOMWindow* aWindow,
                       const IceConfiguration* aConfiguration,
                       const JS::Value* aRTCConfiguration,
                       nsIThread* aThread,
@@ -310,16 +325,17 @@ private:
   nsresult IceStateChange_m(IceState aState);
 
   // The role we are adopting
   Role mRole;
 
   // The call
   CSF::CC_CallPtr mCall;
   ReadyState mReadyState;
+  SignalingState mSignalingState;
 
   // ICE State
   IceState mIceState;
 
   nsCOMPtr<nsIThread> mThread;
   // Weak pointer to IPeerConnectionObserver
   // This is only safe to use on the main thread
   nsWeakPtr mPCObserver;
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp
@@ -391,17 +391,18 @@ PeerConnectionMedia::AddRemoteStream(nsR
   mRemoteSourceStreams.AppendElement(aInfo);
 
   return NS_OK;
 }
 
 nsresult
 PeerConnectionMedia::AddRemoteStreamHint(int aIndex, bool aIsVideo)
 {
-  if (aIndex >= mRemoteSourceStreams.Length()) {
+  if (aIndex < 0 ||
+      static_cast<unsigned int>(aIndex) >= mRemoteSourceStreams.Length()) {
     return NS_ERROR_ILLEGAL_VALUE;
   }
 
   RemoteSourceStreamInfo *pInfo = mRemoteSourceStreams.ElementAt(aIndex);
   MOZ_ASSERT(pInfo);
 
   if (aIsVideo) {
     pInfo->mTrackTypeHints |= DOMMediaStream::HINT_CONTENTS_VIDEO;
--- a/media/webrtc/signaling/src/sipcc/core/ccapp/CCProvider.h
+++ b/media/webrtc/signaling/src/sipcc/core/ccapp/CCProvider.h
@@ -11,16 +11,17 @@
 #include "cpr_memory.h"
 #include "cpr_errno.h"
 #include "cpr_in.h"
 #include "cpr_rand.h"
 #include "cpr_string.h"
 #include "cpr_threads.h"
 #include "phone_types.h"
 #include "session.h"
+#include "fsmdef_states.h"
 
 #include "cc_constants.h"
 #include "ccapi_types.h"
 #include "conf_roster.h"
 
 #define CC_DEVICE_ID 0
 #include "ccapi_service.h"
 
@@ -55,16 +56,17 @@ typedef struct cc_call_log_t_ {
 
 typedef struct cc_call_info_t_{
     uint32_t     ref_count;
     session_id_t  sess_id;
     line_t        line;
     callid_t      id;
     uint16_t      inst;
     cc_call_state_t    state;
+    fsmdef_states_t    fsm_state;
     cc_call_attr_t     attr;
     cc_call_type_t     type;
     cc_call_security_t security;
     cc_call_policy_t   policy;
     unsigned int  callref;
     int           isSelected;
     unsigned int  log_disp;
     string_t      clg_name;
--- a/media/webrtc/signaling/src/sipcc/core/ccapp/ccapi_call_info.c
+++ b/media/webrtc/signaling/src/sipcc/core/ccapp/ccapi_call_info.c
@@ -43,16 +43,35 @@ cc_call_state_t CCAPI_CallInfo_getCallSt
      CCAPP_DEBUG(DEB_F_PREFIX"returned %02X", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), data->state);
      return data->state;
   }
 
   return ONHOOK;
 }
 
 /**
+ * get FSM state
+ * @param handle - call handle
+ * @return call state
+ */
+fsmdef_states_t CCAPI_CallInfo_getFsmState(cc_callinfo_ref_t handle){
+  session_data_t *data = (session_data_t *)handle;
+  CCAPP_DEBUG(DEB_F_PREFIX"Entering",
+              DEB_F_PREFIX_ARGS(SIP_CC_PROV, __FUNCTION__));
+
+  if ( data ){
+     CCAPP_DEBUG(DEB_F_PREFIX"returned %02X",
+                 DEB_F_PREFIX_ARGS(SIP_CC_PROV, __FUNCTION__), data->state);
+     return data->fsm_state;
+  }
+
+  return FSMDEF_S_IDLE;
+}
+
+/**
  * get call attributes
  * @param handle - call handle
  * @return call attributes
  */
 cc_call_attr_t CCAPI_CallInfo_getCallAttr(cc_callinfo_ref_t handle){
   static const char *fname="CCAPI_CallInfo_getCallAttr";
   session_data_t *data = (session_data_t *)handle;
   CCAPP_DEBUG(DEB_F_PREFIX"Entering", DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname));
--- a/media/webrtc/signaling/src/sipcc/core/ccapp/ccprovider.c
+++ b/media/webrtc/signaling/src/sipcc/core/ccapp/ccprovider.c
@@ -1411,17 +1411,19 @@ static void ccappUpdateSessionData (sess
         data = cpr_malloc(sizeof(session_data_t));
         if ( data == NULL ) {
             APP_ERR_MSG("ccappUpdateSessionData Error: cpr_malloc failed for session data");
             return;
         }
         //Populate the session hash data the first time.
         memset(data, 0, sizeof(session_data_t));
         data->sess_id = sessUpd->sessionID;
-				data->state = call_state;
+        data->state = call_state;
+        data->fsm_state =
+            sessUpd->update.ccSessionUpd.data.state_data.fsm_state;
         data->line = sessUpd->update.ccSessionUpd.data.state_data.line_id;
         if (sessUpd->eventID == CALL_NEWCALL ||
             sessUpd->eventID == CREATE_OFFER ||
             sessUpd->eventID == CREATE_ANSWER ||
             sessUpd->eventID == SET_LOCAL_DESC ||
             sessUpd->eventID == SET_REMOTE_DESC ||
             sessUpd->eventID == UPDATE_LOCAL_DESC ||
             sessUpd->eventID == UPDATE_REMOTE_DESC ||
@@ -1501,18 +1503,22 @@ static void ccappUpdateSessionData (sess
 				data->inst = sessUpd->update.ccSessionUpd.data.call_info.instance_id;
 				data->security = sessUpd->update.ccSessionUpd.data.call_info.security;
 				data->policy = sessUpd->update.ccSessionUpd.data.call_info.policy;
                 break;
             case CALL_STATE:
                 if (createdSessionData == FALSE) {
                     return;
                 }
-                data->state = sessUpd->update.ccSessionUpd.data.state_data.state;
-				data->line = sessUpd->update.ccSessionUpd.data.state_data.line_id;
+                data->state =
+                    sessUpd->update.ccSessionUpd.data.state_data.state;
+                data->line =
+                    sessUpd->update.ccSessionUpd.data.state_data.line_id;
+                data->fsm_state =
+                    sessUpd->update.ccSessionUpd.data.state_data.fsm_state;
                 break;
             default:
                 break;
             }
         } else if (sessUpd->eventID == CALL_DELETE_LAST_DIGIT) {
             CCAPP_DEBUG(DEB_F_PREFIX"CALL_DELETE_LAST_DIGIT: event %d sessid %x.",
                     DEB_F_PREFIX_ARGS(SIP_CC_PROV, fname), sessUpd->eventID, sessUpd->sessionID );
             return;
@@ -1567,16 +1573,18 @@ static void ccappUpdateSessionData (sess
 	                        data->inst);
             if ( sessUpd->update.ccSessionUpd.data.state_data.state == HOLD &&
                  (data->state == REMHOLD || data->state == REMINUSE)){
                 data->state = REMHOLD;
             } else {
                 data->state = sessUpd->update.ccSessionUpd.data.state_data.state;
             }
             data->line = sessUpd->update.ccSessionUpd.data.state_data.line_id;
+            data->fsm_state =
+                sessUpd->update.ccSessionUpd.data.state_data.fsm_state;
             sessUpd->update.ccSessionUpd.data.state_data.attr = data->attr;
             sessUpd->update.ccSessionUpd.data.state_data.inst = data->inst;
 
             data->cause = sessUpd->update.ccSessionUpd.data.state_data.cause;
 
             //Update call state
              if ( data != NULL ){
                  capset_get_allowed_features(gCCApp.mode, data->state, data->allowed_features);
@@ -1624,18 +1632,20 @@ static void ccappUpdateSessionData (sess
 		if ( ! strncasecmp(data->gci, sessUpd->update.ccSessionUpd.data.gcid, CC_MAX_GCID)) {
 		  // No change in gci we can ignore the update
 		  return;
 		}
 		sstrncpy(data->gci, sessUpd->update.ccSessionUpd.data.gcid, CC_MAX_GCID);
 		ccsnap_gen_callEvent(CCAPI_CALL_EV_GCID, CREATE_CALL_HANDLE_FROM_SESSION_ID(sessUpd->sessionID));
 		break;
 	case CALL_NEWCALL:
-	    data->state = sessUpd->update.ccSessionUpd.data.state_data.state;
+        data->state = sessUpd->update.ccSessionUpd.data.state_data.state;
         data->line = sessUpd->update.ccSessionUpd.data.state_data.line_id;
+        data->fsm_state =
+            sessUpd->update.ccSessionUpd.data.state_data.fsm_state;
         data->attr = sessUpd->update.ccSessionUpd.data.state_data.attr;
         data->inst = sessUpd->update.ccSessionUpd.data.state_data.inst;
         return;
 		break;
 
 	case CALL_ATTR:
 		data->attr = sessUpd->update.ccSessionUpd.data.state_data.attr;
                 calllogger_update(data);
@@ -1797,16 +1807,18 @@ static void ccappUpdateSessionData (sess
     case UPDATE_LOCAL_DESC:
     case UPDATE_REMOTE_DESC:
     case ICE_CANDIDATE_ADD:
         data->sdp = sessUpd->update.ccSessionUpd.data.state_data.sdp;
         /* Fall through to the next case... */
     case REMOTE_STREAM_ADD:
         data->cause = sessUpd->update.ccSessionUpd.data.state_data.cause;
         data->state = sessUpd->update.ccSessionUpd.data.state_data.state;
+        data->fsm_state =
+            sessUpd->update.ccSessionUpd.data.state_data.fsm_state;
         data->media_stream_track_id = sessUpd->update.ccSessionUpd.data.state_data.media_stream_track_id;
         data->media_stream_id = sessUpd->update.ccSessionUpd.data.state_data.media_stream_id;
         strlib_free(data->status);
         data->status = sessUpd->update.ccSessionUpd.data.state_data.reason_text;
         capset_get_allowed_features(gCCApp.mode, data->state, data->allowed_features);
         ccsnap_gen_callEvent(CCAPI_CALL_EV_STATE, CREATE_CALL_HANDLE_FROM_SESSION_ID(data->sess_id));
         break;
     default:
@@ -2150,17 +2162,18 @@ void ccappFeatureUpdated (feature_update
         }
         //Added for automation test
         NOTIFY_LINE_DEBUG(DEB_NOTIFY_PREFIX"[line=%d][state=%s]",
                         "MWIChanged", featUpd->update.ccFeatUpd.data.mwi_status.line,
                         (featUpd->update.ccFeatUpd.data.mwi_status.status)?"ON":"OFF");
 
         break;
     case DEVICE_FEATURE_MWILAMP:
-        g_deviceInfo.mwi_lamp = featUpd->update.ccFeatUpd.data.state_data.state;
+        g_deviceInfo.mwi_lamp =
+          featUpd->update.ccFeatUpd.data.mwi_status.status;
 	ccsnap_gen_deviceEvent(CCAPI_DEVICE_EV_MWI_LAMP, CC_DEVICE_ID);
         break;
     case DEVICE_FEATURE_BLF:
         sub_hndlr_NotifyBLFStatus(featUpd->update.ccFeatUpd.data.blf_data.request_id,
                                   featUpd->update.ccFeatUpd.data.blf_data.state,
                                   featUpd->update.ccFeatUpd.data.blf_data.app_id);
         break;
     case DEVICE_FEATURE_MNC_REACHED:
--- a/media/webrtc/signaling/src/sipcc/core/common/ui.c
+++ b/media/webrtc/signaling/src/sipcc/core/common/ui.c
@@ -732,17 +732,17 @@ ui_change_mwi_lamp (int status)
 {
     feature_update_t msg;
 
 
     TNP_DEBUG(DEB_F_PREFIX"status=%d", DEB_F_PREFIX_ARGS(UI_API, __FUNCTION__), status);
 
     msg.sessionType = SESSIONTYPE_CALLCONTROL;
     msg.featureID = DEVICE_FEATURE_MWILAMP;
-    msg.update.ccFeatUpd.data.state_data.state = status;
+    msg.update.ccFeatUpd.data.mwi_status.status = status;
 
     if ( ccappTaskPostMsg(CCAPP_FEATURE_UPDATE, &msg, sizeof(feature_update_t), CCAPP_CCPROVIER) != CPR_SUCCESS ) {
         CCAPP_ERROR(CCAPP_F_PREFIX"failed to send DEVICE_FEATURE_MWILAMP(%d) msg", __FUNCTION__, status);
     }
 }
 
 /**
  *
@@ -1539,44 +1539,45 @@ void ui_log_disposition (callid_t call_i
 void
 ui_control_feature (line_t line_id, callid_t call_id,
                        int feat_list[], int len, int enable)
 {
     // do nothing.
 }
 
 /*
- *  Helper for the following four functions which all load up a
+ *  Helper for the following several functions which all load up a
  *  session_update message and post it.
  *
  */
-static void post_message_helper(
-    group_call_event_t eventId,
-    call_events event,
-    line_t nLine,
-    callid_t nCallId,
-    uint16_t call_instance_id,
-    string_t sdp,
-    pc_error error,
-    const char *format,
-    va_list args)
+static void post_message_helper(group_call_event_t eventId,
+                                call_events event,
+                                fsmdef_states_t new_state,
+                                line_t nLine,
+                                callid_t nCallId,
+                                uint16_t call_instance_id,
+                                string_t sdp,
+                                pc_error error,
+                                const char *format,
+                                va_list args)
 {
     flex_string fs;
     session_update_t msg;
     memset( &msg, 0, sizeof(session_update_t));
 
     if (nCallId == CC_NO_CALL_ID) {
         /* no operation when no call ID */
         return;
     }
 
     msg.sessionID = createSessionId(nLine, nCallId);
 
     msg.eventID = eventId;
     msg.update.ccSessionUpd.data.state_data.state = event;
+    msg.update.ccSessionUpd.data.state_data.fsm_state = new_state;
     msg.update.ccSessionUpd.data.state_data.inst = call_instance_id;
     msg.update.ccSessionUpd.data.state_data.line_id = nLine;
     msg.update.ccSessionUpd.data.state_data.sdp = sdp;
     msg.update.ccSessionUpd.data.state_data.cause = error;
 
     if (format) {
       flex_string_init(&fs);
       flex_string_vsprintf(&fs, format, args);
@@ -1597,154 +1598,185 @@ static void post_message_helper(
 }
 
 /**
  *  Send data from createOffer to the UI, can send success with SDP string
  *  or can send error
  *
  *  @return none
  */
-void ui_create_offer(call_events event, line_t nLine, callid_t nCallID,
-                     uint16_t call_instance_id, string_t sdp,
-                     pc_error error, const char *format, ...)
+void ui_create_offer(call_events event,
+                     fsmdef_states_t new_state,
+                     line_t nLine,
+                     callid_t nCallID,
+                     uint16_t call_instance_id,
+                     string_t sdp,
+                     pc_error error,
+                     const char *format, ...)
 {
     va_list ap;
 
     TNP_DEBUG(DEB_L_C_F_PREFIX"state=%d call_instance=%d",
               DEB_L_C_F_PREFIX_ARGS(UI_API, nLine, nCallID, __FUNCTION__),
               event, call_instance_id);
 
 
     va_start(ap, format);
-    post_message_helper(CREATE_OFFER, event, nLine, nCallID, call_instance_id,
-                        sdp, error, format, ap);
+    post_message_helper(CREATE_OFFER, event, new_state, nLine, nCallID,
+                        call_instance_id, sdp, error, format, ap);
     va_end(ap);
 
     return;
 }
 
 /**
  *  Send data from createAnswer to the UI, can send success with SDP string
  *  or can send error
  *
  *  @return none
  */
-void ui_create_answer(call_events event, line_t nLine, callid_t nCallID,
-                      uint16_t call_instance_id, string_t sdp,
-                      pc_error error, const char *format, ...)
+void ui_create_answer(call_events event,
+                      fsmdef_states_t new_state,
+                      line_t nLine,
+                      callid_t nCallID,
+                      uint16_t call_instance_id,
+                      string_t sdp,
+                      pc_error error,
+                      const char *format, ...)
 {
     va_list ap;
     TNP_DEBUG(DEB_L_C_F_PREFIX"state=%d call_instance=%d",
               DEB_L_C_F_PREFIX_ARGS(UI_API, nLine, nCallID, __FUNCTION__), event, call_instance_id);
 
     va_start(ap, format);
-    post_message_helper(CREATE_ANSWER, event, nLine, nCallID, call_instance_id,
-                        sdp, error, format, ap);
+    post_message_helper(CREATE_ANSWER, event, new_state, nLine, nCallID,
+                        call_instance_id, sdp, error, format, ap);
     va_end(ap);
 
     return;
 }
 
 /**
  *  Send data from setLocalDescription to the UI
  *
  *  @return none
  */
 
-void ui_set_local_description(call_events event, line_t nLine, callid_t nCallID,
-                              uint16_t call_instance_id, string_t sdp,
-                              pc_error error, const char *format, ...)
+void ui_set_local_description(call_events event,
+                              fsmdef_states_t new_state,
+                              line_t nLine,
+                              callid_t nCallID,
+                              uint16_t call_instance_id,
+                              string_t sdp,
+                              pc_error error,
+                              const char *format, ...)
 {
     va_list ap;
     TNP_DEBUG(DEB_L_C_F_PREFIX"state=%d call_instance=%d",
               DEB_L_C_F_PREFIX_ARGS(UI_API, nLine, nCallID, __FUNCTION__), event, call_instance_id);
 
     va_start(ap, format);
-    post_message_helper(SET_LOCAL_DESC, event, nLine, nCallID, call_instance_id,
-                        sdp, error, format, ap);
+    post_message_helper(SET_LOCAL_DESC, event, new_state, nLine, nCallID,
+                        call_instance_id, sdp, error, format, ap);
     va_end(ap);
 
     return;
 }
 
 /**
  *  Send data from setRemoteDescription to the UI
  *
  *  @return none
  */
 
-void ui_set_remote_description(call_events event, line_t nLine,
-                               callid_t nCallID, uint16_t call_instance_id,
-                               string_t sdp, pc_error error,
+void ui_set_remote_description(call_events event,
+                               fsmdef_states_t new_state,
+                               line_t nLine,
+                               callid_t nCallID,
+                               uint16_t call_instance_id,
+                               string_t sdp,
+                               pc_error error,
                                const char *format, ...)
 {
     va_list ap;
     TNP_DEBUG(DEB_L_C_F_PREFIX"state=%d call_instance=%d",
               DEB_L_C_F_PREFIX_ARGS(UI_API, nLine, nCallID, __FUNCTION__), event, call_instance_id);
 
     va_start(ap, format);
-    post_message_helper(SET_REMOTE_DESC, event, nLine, nCallID,
+    post_message_helper(SET_REMOTE_DESC, event, new_state, nLine, nCallID,
                         call_instance_id, sdp, error, format, ap);
     va_end(ap);
 
     return;
 }
 
 /**
  *  Let PeerConnection know about an updated local session description
  *
  *  @return none
  */
 
-void ui_update_local_description(call_events event, line_t nLine,
-                                 callid_t nCallID, uint16_t call_instance_id,
-                                 string_t sdp, pc_error error,
+void ui_update_local_description(call_events event,
+                                 fsmdef_states_t new_state,
+                                 line_t nLine,
+                                 callid_t nCallID,
+                                 uint16_t call_instance_id,
+                                 string_t sdp,
+                                 pc_error error,
                                  const char *format, ...)
 {
     va_list ap;
     TNP_DEBUG(DEB_L_C_F_PREFIX"state=%d call_instance=%d",
               DEB_L_C_F_PREFIX_ARGS(UI_API, nLine, nCallID, __FUNCTION__),
               event, call_instance_id);
 
     va_start(ap, format);
-    post_message_helper(UPDATE_LOCAL_DESC, event, nLine, nCallID,
+    post_message_helper(UPDATE_LOCAL_DESC, event, new_state, nLine, nCallID,
                         call_instance_id, sdp, error, format, ap);
     va_end(ap);
 
     return;
 }
 
 /**
  * Send data from addIceCandidate to the UI
  *
  * @return none
  */
 
-void ui_ice_candidate_add(call_events event, line_t nLine, callid_t nCallID,
-                          uint16_t call_instance_id, string_t sdp,
-                          pc_error error, const char *format, ...)
+void ui_ice_candidate_add(call_events event,
+                          fsmdef_states_t new_state,
+                          line_t nLine,
+                          callid_t nCallID,
+                          uint16_t call_instance_id,
+                          string_t sdp,
+                          pc_error error,
+                          const char *format, ...)
 {
     va_list ap;
     TNP_DEBUG(DEB_L_C_F_PREFIX"state=%d call_instance=%d",
               DEB_L_C_F_PREFIX_ARGS(UI_API, nLine, nCallID, __FUNCTION__), event, call_instance_id);
 
     va_start(ap, format);
-    post_message_helper(ICE_CANDIDATE_ADD, event, nLine, nCallID,
+    post_message_helper(ICE_CANDIDATE_ADD, event, new_state, nLine, nCallID,
                         call_instance_id, sdp, error, format, ap);
     va_end(ap);
 }
 
 /**
  *  Send Remote Stream data to the UI
  *
  *  @return none
  */
 
-void ui_on_remote_stream_added(call_events event, line_t nLine,
-                               callid_t nCallID, uint16_t call_instance_id,
+void ui_on_remote_stream_added(call_events event,
+                               fsmdef_states_t new_state,
+                               line_t nLine,
+                               callid_t nCallID,
+                               uint16_t call_instance_id,
                                cc_media_remote_track_table_t media_track)
 {
     session_update_t msg;
     fsmdef_dcb_t *dcb = fsmdef_get_dcb_by_call_id(nCallID);
     memset( &msg, 0, sizeof(session_update_t));
 
     if (nCallID == CC_NO_CALL_ID || dcb == NULL) {
         /* no operation when no call ID */
@@ -1754,16 +1786,17 @@ void ui_on_remote_stream_added(call_even
     TNP_DEBUG(DEB_L_C_F_PREFIX"state=%d call_instance=%d",
               DEB_L_C_F_PREFIX_ARGS(UI_API, nLine, nCallID, __FUNCTION__), event, call_instance_id);
 
 
     msg.sessionID = createSessionId(nLine, nCallID);
 
     msg.eventID = REMOTE_STREAM_ADD;
     msg.update.ccSessionUpd.data.state_data.state = event;
+    msg.update.ccSessionUpd.data.state_data.fsm_state = new_state;
     msg.update.ccSessionUpd.data.state_data.inst = call_instance_id;
     msg.update.ccSessionUpd.data.state_data.line_id = nLine;
     msg.update.ccSessionUpd.data.state_data.media_stream_track_id = media_track.track[0].media_stream_track_id;
     msg.update.ccSessionUpd.data.state_data.media_stream_id = (unsigned int)media_track.media_stream_id;
     msg.update.ccSessionUpd.data.state_data.cause = PC_NO_ERROR;
 
     if ( ccappTaskPostMsg(CCAPP_SESSION_UPDATE, &msg, sizeof(session_update_t), CCAPP_CCPROVIER) != CPR_SUCCESS ) {
         CCAPP_ERROR(CCAPP_F_PREFIX"failed to send CALL_STATE(%d) msg", __FUNCTION__, event);
--- a/media/webrtc/signaling/src/sipcc/core/gsm/fsmdef.c
+++ b/media/webrtc/signaling/src/sipcc/core/gsm/fsmdef.c
@@ -742,16 +742,46 @@ static sm_function_t fsmdef_function_tab
     /* CC_MSG_SETLOCALDESC     */ fsmdef_ev_setlocaldesc,
     /* CC_MSG_SETREMOTEDESC    */ fsmdef_ev_setremotedesc,
     /* CC_MSG_SETPEERCONNECTION */fsmdef_ev_default,
     /* CC_MSG_ADDSTREAM        */ fsmdef_ev_addstream,
     /* CC_MSG_REMOVESTREAM     */ fsmdef_ev_removestream,
     /* CC_MSG_ADDCANDIDATE     */ fsmdef_ev_addcandidate
     },
 
+/* FSMDEF_S_HAVE_LOCAL_PRANSWER  -------------------------------------------- */
+    {
+    /* CC_MSG_SETUP            */ fsmdef_ev_default,
+    /* CC_MSG_SETUP_ACK        */ fsmdef_ev_default,
+    /* CC_MSG_PROCEEDING       */ fsmdef_ev_default,
+    /* CC_MSG_ALERTING         */ fsmdef_ev_default,
+    /* CC_MSG_CONNECTED        */ fsmdef_ev_default,
+    /* CC_MSG_CONNECTED_ACK    */ fsmdef_ev_default,
+    /* CC_MSG_RELEASE          */ fsmdef_ev_default,
+    /* CC_MSG_RELEASE_COMPLETE */ fsmdef_ev_default,
+    /* CC_MSG_FEATURE          */ fsmdef_ev_default,
+    /* CC_MSG_FEATURE_ACK      */ fsmdef_ev_default,
+    /* CC_MSG_OFFHOOK          */ fsmdef_ev_default,
+    /* CC_MSG_ONHOOK           */ fsmdef_ev_onhook,
+    /* CC_MSG_LINE             */ fsmdef_ev_default,
+    /* CC_MSG_DIGIT_BEGIN      */ fsmdef_ev_default,
+    /* CC_MSG_DIGIT_END        */ fsmdef_ev_default,
+    /* CC_MSG_DIALSTRING       */ fsmdef_ev_default,
+    /* CC_MSG_MWI              */ fsmdef_ev_default,
+    /* CC_MSG_SESSION_AUDIT    */ fsmdef_ev_default,
+    /* CC_MSG_CREATEOFFER      */ fsmdef_ev_createoffer,
+    /* CC_MSG_CREATEANSWER     */ fsmdef_ev_createanswer,
+    /* CC_MSG_SETLOCALDESC     */ fsmdef_ev_setlocaldesc,
+    /* CC_MSG_SETREMOTEDESC    */ fsmdef_ev_default, /* Should not happen */
+    /* CC_MSG_SETPEERCONNECTION */fsmdef_ev_default,
+    /* CC_MSG_ADDSTREAM        */ fsmdef_ev_default,
+    /* CC_MSG_REMOVESTREAM     */ fsmdef_ev_default,
+    /* CC_MSG_ADDCANDIDATE     */ fsmdef_ev_addcandidate
+    },
+
 /* FSMDEF_S_HAVE_REMOTE_PRANSWER  ------------------------------------------- */
     {
     /* CC_MSG_SETUP            */ fsmdef_ev_default,
     /* CC_MSG_SETUP_ACK        */ fsmdef_ev_default,
     /* CC_MSG_PROCEEDING       */ fsmdef_ev_default,
     /* CC_MSG_ALERTING         */ fsmdef_ev_default,
     /* CC_MSG_CONNECTED        */ fsmdef_ev_default,
     /* CC_MSG_CONNECTED_ACK    */ fsmdef_ev_default,
@@ -772,46 +802,16 @@ static sm_function_t fsmdef_function_tab
     /* CC_MSG_SETLOCALDESC     */ fsmdef_ev_default, /* Should not happen */
     /* CC_MSG_SETREMOTEDESC    */ fsmdef_ev_setremotedesc,
     /* CC_MSG_SETPEERCONNECTION */fsmdef_ev_default,
     /* CC_MSG_ADDSTREAM        */ fsmdef_ev_default,
     /* CC_MSG_REMOVESTREAM     */ fsmdef_ev_default,
     /* CC_MSG_ADDCANDIDATE     */ fsmdef_ev_addcandidate
     },
 
-/* FSMDEF_S_HAVE_LOCAL_PRANSWER  -------------------------------------------- */
-    {
-    /* CC_MSG_SETUP            */ fsmdef_ev_default,
-    /* CC_MSG_SETUP_ACK        */ fsmdef_ev_default,
-    /* CC_MSG_PROCEEDING       */ fsmdef_ev_default,
-    /* CC_MSG_ALERTING         */ fsmdef_ev_default,
-    /* CC_MSG_CONNECTED        */ fsmdef_ev_default,
-    /* CC_MSG_CONNECTED_ACK    */ fsmdef_ev_default,
-    /* CC_MSG_RELEASE          */ fsmdef_ev_default,
-    /* CC_MSG_RELEASE_COMPLETE */ fsmdef_ev_default,
-    /* CC_MSG_FEATURE          */ fsmdef_ev_default,
-    /* CC_MSG_FEATURE_ACK      */ fsmdef_ev_default,
-    /* CC_MSG_OFFHOOK          */ fsmdef_ev_default,
-    /* CC_MSG_ONHOOK           */ fsmdef_ev_onhook,
-    /* CC_MSG_LINE             */ fsmdef_ev_default,
-    /* CC_MSG_DIGIT_BEGIN      */ fsmdef_ev_default,
-    /* CC_MSG_DIGIT_END        */ fsmdef_ev_default,
-    /* CC_MSG_DIALSTRING       */ fsmdef_ev_default,
-    /* CC_MSG_MWI              */ fsmdef_ev_default,
-    /* CC_MSG_SESSION_AUDIT    */ fsmdef_ev_default,
-    /* CC_MSG_CREATEOFFER      */ fsmdef_ev_createoffer,
-    /* CC_MSG_CREATEANSWER     */ fsmdef_ev_createanswer,
-    /* CC_MSG_SETLOCALDESC     */ fsmdef_ev_setlocaldesc,
-    /* CC_MSG_SETREMOTEDESC    */ fsmdef_ev_default, /* Should not happen */
-    /* CC_MSG_SETPEERCONNECTION */fsmdef_ev_default,
-    /* CC_MSG_ADDSTREAM        */ fsmdef_ev_default,
-    /* CC_MSG_REMOVESTREAM     */ fsmdef_ev_default,
-    /* CC_MSG_ADDCANDIDATE     */ fsmdef_ev_addcandidate
-    },
-
 /* FSMDEF_S_CLOSED  --------------------------------------------------------- */
     {
     /* CC_MSG_SETUP            */ fsmdef_ev_default,
     /* CC_MSG_SETUP_ACK        */ fsmdef_ev_default,
     /* CC_MSG_PROCEEDING       */ fsmdef_ev_default,
     /* CC_MSG_ALERTING         */ fsmdef_ev_default,
     /* CC_MSG_CONNECTED        */ fsmdef_ev_default,
     /* CC_MSG_CONNECTED_ACK    */ fsmdef_ev_default,
@@ -2411,59 +2411,60 @@ fsmdef_ev_default (sm_event_t *event)
     /*
      * For WebRTC events, we must send back a message to enable the
      * corresponding error callback to be called. Arguably, these
      * should always be caught by the PeerConnection and thrown as exceptions,
      * but this enusres that the behavior is water-tight.
      */
     switch (event->event) {
       case CC_MSG_CREATEOFFER:
-          ui_create_offer(evCreateOfferError, msg->line, msg->call_id,
-              dcb->caller_id.call_instance_id, strlib_empty(),
+          ui_create_offer(evCreateOfferError, fcb->state, msg->line,
+              msg->call_id, dcb->caller_id.call_instance_id, strlib_empty(),
               PC_INVALID_STATE, "Cannot create offer in state %s",
-              fsmdef_state_name(event->state));
+              fsmdef_state_name(fcb->state));
         break;
 
       case CC_MSG_CREATEANSWER:
-          ui_create_answer(evCreateAnswerError, msg->line, msg->call_id,
-              dcb->caller_id.call_instance_id, strlib_empty(),
+          ui_create_answer(evCreateAnswerError, fcb->state, msg->line,
+              msg->call_id, dcb->caller_id.call_instance_id, strlib_empty(),
               PC_INVALID_STATE, "Cannot create answer in state %s",
-              fsmdef_state_name(event->state));
+              fsmdef_state_name(fcb->state));
         break;
 
       case CC_MSG_SETLOCALDESC:
-          ui_set_local_description(evSetLocalDescError, msg->line,
+          ui_set_local_description(evSetLocalDescError, fcb->state, msg->line,
               msg->call_id, dcb->caller_id.call_instance_id, strlib_empty(),
               PC_INVALID_STATE, "Cannot set local description in state %s",
-              fsmdef_state_name(event->state));
+              fsmdef_state_name(fcb->state));
         break;
 
       case CC_MSG_SETREMOTEDESC:
-          ui_set_remote_description(evSetRemoteDescError, msg->line,
-              msg->call_id, dcb->caller_id.call_instance_id, strlib_empty(),
-              PC_INVALID_STATE, "Cannot set remote description in state %s",
-              fsmdef_state_name(event->state));
+          ui_set_remote_description(evSetRemoteDescError, fcb->state,
+              msg->line, msg->call_id, dcb->caller_id.call_instance_id,
+              strlib_empty(), PC_INVALID_STATE,
+              "Cannot set remote description in state %s",
+              fsmdef_state_name(fcb->state));
         break;
 
       case CC_MSG_ADDCANDIDATE:
-          ui_ice_candidate_add(evAddIceCandidateError, msg->line, msg->call_id,
-              dcb->caller_id.call_instance_id, strlib_empty(),
+          ui_ice_candidate_add(evAddIceCandidateError, fcb->state, msg->line,
+              msg->call_id, dcb->caller_id.call_instance_id, strlib_empty(),
               PC_INVALID_STATE, "Cannot add ICE candidate in state %s",
-              fsmdef_state_name(event->state));
+              fsmdef_state_name(fcb->state));
         break;
 
       case CC_MSG_ADDSTREAM:
       case CC_MSG_REMOVESTREAM:
           /* This shouldn't happen, since PeerConnection should check
            * the state before sending these events to us. The debug message
            * here is to catch the unexpected situation of such an event
            * getting through anyway. */
           FSM_DEBUG_SM(DEB_L_C_F_PREFIX"Cannot add or remove streams "
               "in state %s", DEB_L_C_F_PREFIX_ARGS(FSM, dcb->line,
-              msg->call_id, __FUNCTION__), fsmdef_state_name(event->state));
+              msg->call_id, __FUNCTION__), fsmdef_state_name(fcb->state));
         break;
 
       default:
           cc_call_state(dcb->call_id, dcb->line, CC_STATE_UNKNOWN, NULL);
         break;
     }
 
     return (SM_RC_END);
@@ -3145,24 +3146,24 @@ fsmdef_ev_createoffer (sm_event_t *event
        again. This will change when we allow renegotiation of ongoing
        sessions. See bug 840728. */
     if (dcb->local_sdp_complete) {
         FSM_DEBUG_SM(DEB_F_PREFIX"local SDP already created: returning "
         "prevously created SDP.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
 
         local_sdp = sipsdp_write_to_buf(dcb->sdp->src_sdp, &local_sdp_len);
         if (!local_sdp) {
-            ui_create_offer(evCreateOfferError, line, call_id,
+            ui_create_offer(evCreateOfferError, fcb->state, line, call_id,
                 dcb->caller_id.call_instance_id, strlib_empty(),
                 PC_INTERNAL_ERROR, "Could not re-create local SDP for offer");
             FSM_DEBUG_SM("%s", get_debug_string(FSM_DBG_SDP_BUILD_ERR));
             return (fsmdef_release(fcb, cause, FALSE));
         }
 
-        ui_create_offer(evCreateOfferSuccess, line, call_id,
+        ui_create_offer(evCreateOfferSuccess, fcb->state, line, call_id,
             dcb->caller_id.call_instance_id,
             strlib_malloc(local_sdp,-1), PC_NO_ERROR, NULL);
         free(local_sdp);
         return (SM_RC_END);
     }
 
     dcb->inbound = FALSE;
 
@@ -3174,27 +3175,27 @@ fsmdef_ev_createoffer (sm_event_t *event
 
     if (dcb->media_cap_tbl->cap[CC_VIDEO_1].enabled ||
         dcb->media_cap_tbl->cap[CC_AUDIO_1].enabled ||
         dcb->media_cap_tbl->cap[CC_DATACHANNEL_1].enabled) {
       has_stream = TRUE;
     }
 
     if (!has_stream) {
-      ui_create_offer(evCreateOfferError, line, call_id,
+      ui_create_offer(evCreateOfferError, fcb->state, line, call_id,
           dcb->caller_id.call_instance_id, strlib_empty(),
           PC_INVALID_STATE, "Cannot create SDP without any streams.");
       return SM_RC_END;
     }
 
     vcm_res = vcmGetIceParams(dcb->peerconnection, &ufrag, &ice_pwd);
     if (vcm_res) {
     	FSM_DEBUG_SM(DEB_F_PREFIX"vcmGetIceParams returned an error",
             DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
-      ui_create_offer(evCreateOfferError, line, call_id,
+      ui_create_offer(evCreateOfferError, fcb->state, line, call_id,
           dcb->caller_id.call_instance_id, strlib_empty(),
           PC_INTERNAL_ERROR, "Failed to get ICE parameters for local SDP");
       return (fsmdef_release(fcb, cause, FALSE));
     }
 
     dcb->ice_ufrag = (char *)cpr_malloc(strlen(ufrag) + 1);
     if (!dcb->ice_ufrag)
     	return SM_RC_END;
@@ -3216,38 +3217,38 @@ fsmdef_ev_createoffer (sm_event_t *event
 
     if (vcm_res) {
     	FSM_DEBUG_SM(DEB_F_PREFIX"vcmGetDtlsIdentity returned an error", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
         return SM_RC_END;
     }
 
     cause = gsmsdp_create_local_sdp(dcb, FALSE, TRUE, TRUE, TRUE, TRUE);
     if (cause != CC_CAUSE_OK) {
-        ui_create_offer(evCreateOfferError, line, call_id,
+        ui_create_offer(evCreateOfferError, fcb->state, line, call_id,
             dcb->caller_id.call_instance_id, strlib_empty(),
             PC_INTERNAL_ERROR, "Could not create local SDP for offer;"
                 " cause = %s", cc_cause_name(cause));
         FSM_DEBUG_SM("%s", get_debug_string(FSM_DBG_SDP_BUILD_ERR));
         return (fsmdef_release(fcb, cause, FALSE));
     }
 
     cause = gsmsdp_encode_sdp_and_update_version(dcb, &msg_body);
     if (cause != CC_CAUSE_OK) {
-        ui_create_offer(evCreateOfferError, line, call_id,
+        ui_create_offer(evCreateOfferError, fcb->state, line, call_id,
             dcb->caller_id.call_instance_id, strlib_empty(),
             PC_INTERNAL_ERROR, "Could not encode local SDP for offer;"
                 " cause = %s", cc_cause_name(cause));
         FSM_DEBUG_SM("%s", get_debug_string(FSM_DBG_SDP_BUILD_ERR));
         return (fsmdef_release(fcb, cause, FALSE));
     }
 
     dcb->local_sdp_complete = TRUE;
 
     /* Pass offer SDP back to UI */
-    ui_create_offer(evCreateOfferSuccess, line, call_id,
+    ui_create_offer(evCreateOfferSuccess, fcb->state, line, call_id,
         dcb->caller_id.call_instance_id,
         strlib_malloc(msg_body.parts[0].body, -1), PC_NO_ERROR, NULL);
     cc_free_msg_body_parts(&msg_body);
 
     return (SM_RC_END);
 }
 
 
@@ -3300,24 +3301,24 @@ fsmdef_ev_createanswer (sm_event_t *even
        again. This will change when we allow renegotiation of ongoing
        sessions. See bug 840728. */
     if (dcb->local_sdp_complete) {
         FSM_DEBUG_SM(DEB_F_PREFIX"local SDP already created: returning "
         "prevously created SDP.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
 
         local_sdp = sipsdp_write_to_buf(dcb->sdp->src_sdp, &local_sdp_len);
         if (!local_sdp) {
-            ui_create_answer(evCreateAnswerError, line, call_id,
+            ui_create_answer(evCreateAnswerError, fcb->state, line, call_id,
                 dcb->caller_id.call_instance_id, strlib_empty(),
                 PC_INTERNAL_ERROR, "Could not re-create local SDP for answer");
             FSM_DEBUG_SM("%s", get_debug_string(FSM_DBG_SDP_BUILD_ERR));
             return (fsmdef_release(fcb, cause, FALSE));
         }
 
-        ui_create_answer(evCreateAnswerSuccess, line, call_id,
+        ui_create_answer(evCreateAnswerSuccess, fcb->state, line, call_id,
             dcb->caller_id.call_instance_id,
             strlib_malloc(local_sdp,-1), PC_NO_ERROR, NULL);
         free(local_sdp);
         return (SM_RC_END);
     }
 
     dcb->inbound = TRUE;
 
@@ -3326,17 +3327,17 @@ fsmdef_ev_createanswer (sm_event_t *even
        fsmdef_free_constraints(msg->data.session.constraints);
        msg->data.session.constraints = 0;
     }
 
     vcm_res = vcmGetIceParams(dcb->peerconnection, &ufrag, &ice_pwd);
     if (vcm_res) {
     	FSM_DEBUG_SM(DEB_F_PREFIX"vcmGetIceParams returned an error",
             DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
-      ui_create_answer(evCreateAnswerError, line, call_id,
+      ui_create_answer(evCreateAnswerError, fcb->state, line, call_id,
           dcb->caller_id.call_instance_id, strlib_empty(),
           PC_INTERNAL_ERROR, "Could not get ICE parameters for answer");
       return (fsmdef_release(fcb, cause, FALSE));
     }
 
     dcb->ice_ufrag = (char *)cpr_malloc(strlen(ufrag) + 1);
     if (!dcb->ice_ufrag)
         return SM_RC_END;
@@ -3368,17 +3369,17 @@ fsmdef_ev_createanswer (sm_event_t *even
     gsmsdp_get_offered_media_types(fcb, dcb->sdp, &has_audio, &has_video, &has_data);
 
     /*
      * The sdp member of the dcb has local and remote sdp
      * this next function fills in the local part
      */
     cause = gsmsdp_create_local_sdp(dcb, TRUE, has_audio, has_video, has_data, FALSE);
     if (cause != CC_CAUSE_OK) {
-        ui_create_answer(evCreateAnswerError, line, call_id,
+        ui_create_answer(evCreateAnswerError, fcb->state, line, call_id,
             dcb->caller_id.call_instance_id, strlib_empty(),
             PC_INTERNAL_ERROR, "Could not create local SDP for answer;"
                 " cause = %s", cc_cause_name(cause));
         FSM_DEBUG_SM("%s", get_debug_string(FSM_DBG_SDP_BUILD_ERR));
         // Force clean up call without sending release
         return (fsmdef_release(fcb, cause, FALSE));
     }
 
@@ -3387,37 +3388,37 @@ fsmdef_ev_createanswer (sm_event_t *even
        want to set that conditionally. */
     cause = gsmsdp_negotiate_media_lines(fcb, dcb->sdp,
             /* initial_offer */       TRUE,
             /* offer */               TRUE,
             /* notify_stream_added */ FALSE,
             /* create_answer */       TRUE);
 
     if (cause != CC_CAUSE_OK) {
-        ui_create_answer(evCreateAnswerError, line, call_id,
+        ui_create_answer(evCreateAnswerError, fcb->state, line, call_id,
             dcb->caller_id.call_instance_id, strlib_empty(),
             PC_INTERNAL_ERROR, "Could not negotiate media lines; cause = %s",
                 cc_cause_name(cause));
         return (fsmdef_release(fcb, cause, FALSE));
     }
 
     cause = gsmsdp_encode_sdp_and_update_version(dcb, &msg_body);
     if (cause != CC_CAUSE_OK) {
-        ui_create_answer(evCreateAnswerError, line, call_id,
+        ui_create_answer(evCreateAnswerError, fcb->state, line, call_id,
             dcb->caller_id.call_instance_id, strlib_empty(),
             PC_INTERNAL_ERROR, "Could not encode SDP for answer; cause = %s",
                 cc_cause_name(cause));
         FSM_DEBUG_SM("%s", get_debug_string(FSM_DBG_SDP_BUILD_ERR));
         return (fsmdef_release(fcb, cause, FALSE));
     }
 
     dcb->local_sdp_complete = TRUE;
 
     /* Pass SDP back to UI */
-    ui_create_answer(evCreateAnswerSuccess, line, call_id,
+    ui_create_answer(evCreateAnswerSuccess, fcb->state, line, call_id,
         dcb->caller_id.call_instance_id,
         strlib_malloc(msg_body.parts[0].body, -1), PC_NO_ERROR, NULL);
     cc_free_msg_body_parts(&msg_body);
 
     return (SM_RC_END);
 }
 
 
@@ -3443,140 +3444,140 @@ fsmdef_ev_setlocaldesc(sm_event_t *event
     char                *local_sdp = NULL;
     uint32_t            local_sdp_len = 0;
 
     FSM_DEBUG_SM(DEB_F_PREFIX"Entered.", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
 
     if (dcb == NULL) {
         FSM_DEBUG_SM(DEB_F_PREFIX"dcb is NULL.",
           DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
-        ui_set_local_description(evSetLocalDescError, line, call_id,
+        fsm_change_state(fcb, __LINE__, FSMDEF_S_CLOSED);
+        ui_set_local_description(evSetLocalDescError, fcb->state, line, call_id,
             0, strlib_empty(),
             PC_INTERNAL_ERROR, "Unrecoverable error: dcb is NULL.");
-        fsm_change_state(fcb, __LINE__, FSMDEF_S_CLOSED);
         return (SM_RC_CLEANUP);
     }
 
     config_get_value(CFGID_SDPMODE, &sdpmode, sizeof(sdpmode));
     if (!sdpmode) {
-        ui_set_local_description(evSetLocalDescError, line, call_id,
+        fsm_change_state(fcb, __LINE__, FSMDEF_S_CLOSED);
+        ui_set_local_description(evSetLocalDescError, fcb->state, line, call_id,
             dcb->caller_id.call_instance_id, strlib_empty(),
             PC_INTERNAL_ERROR, "'sdpmode' configuration is false. This should "
             "never ever happen. Run for your lives!");
-        fsm_change_state(fcb, __LINE__, FSMDEF_S_CLOSED);
         return (SM_RC_END);
     }
 
     if (!dcb->sdp) {
-        ui_set_local_description(evSetLocalDescError, line, call_id,
+        ui_set_local_description(evSetLocalDescError, fcb->state, line, call_id,
            dcb->caller_id.call_instance_id, strlib_empty(),
            PC_INTERNAL_ERROR, "Setting of local SDP before calling "
            "createOffer or createAnswer is not currently supported.");
         return (SM_RC_END);
     }
 
     switch (action) {
 
     case JSEP_OFFER:
-        if (event->state != FSMDEF_S_STABLE &&
-            event->state != FSMDEF_S_HAVE_LOCAL_OFFER) {
-            ui_set_local_description(evSetLocalDescError, line, call_id,
-                dcb->caller_id.call_instance_id, strlib_empty(),
+        if (fcb->state != FSMDEF_S_STABLE &&
+            fcb->state != FSMDEF_S_HAVE_LOCAL_OFFER) {
+            ui_set_local_description(evSetLocalDescError, fcb->state, line,
+                call_id, dcb->caller_id.call_instance_id, strlib_empty(),
                 PC_INVALID_STATE, "Cannot set local offer in state %s",
-                fsmdef_state_name(event->state));
+                fsmdef_state_name(fcb->state));
             return (SM_RC_END);
         }
         /* TODO: Parse incoming SDP and act on it. */
         fsm_change_state(fcb, __LINE__, FSMDEF_S_HAVE_LOCAL_OFFER);
         break;
 
     case JSEP_ANSWER:
-        if (event->state != FSMDEF_S_HAVE_REMOTE_OFFER &&
-            event->state != FSMDEF_S_HAVE_LOCAL_PRANSWER) {
-            ui_set_local_description(evSetLocalDescError, line, call_id,
-                dcb->caller_id.call_instance_id, strlib_empty(),
+        if (fcb->state != FSMDEF_S_HAVE_REMOTE_OFFER &&
+            fcb->state != FSMDEF_S_HAVE_LOCAL_PRANSWER) {
+            ui_set_local_description(evSetLocalDescError, fcb->state, line,
+            call_id, dcb->caller_id.call_instance_id, strlib_empty(),
                 PC_INVALID_STATE, "Cannot set local answer in state %s",
-                fsmdef_state_name(event->state));
+                fsmdef_state_name(fcb->state));
             return (SM_RC_END);
         }
         /* TODO: Parse incoming SDP and act on it. */
         FSM_SET_FLAGS(dcb->msgs_sent, FSMDEF_MSG_CONNECTED);
 
         cc_call_state(dcb->call_id, dcb->line, CC_STATE_ANSWERED,
                       FSMDEF_CC_CALLER_ID);
 
         /*
          * Now that we have negotiated the media, time to set up ICE.
          * There also needs to be an ICE check in negotiate_media_lines.
          */
         cause = gsmsdp_install_peer_ice_attributes(fcb);
         if (cause != CC_CAUSE_OK) {
-            ui_set_local_description(evSetLocalDescError, line, call_id,
-                dcb->caller_id.call_instance_id, strlib_empty(),
+            ui_set_local_description(evSetLocalDescError, fcb->state, line,
+            call_id, dcb->caller_id.call_instance_id, strlib_empty(),
                 PC_INTERNAL_ERROR, "Could not configure local ICE state"
                 " from SDP; cause = %s", cc_cause_name(cause));
             return (SM_RC_END);
         }
 
         /* taken from fsmdef_ev_connected_ack start rx and tx  */
         cc_call_state(dcb->call_id, dcb->line, CC_STATE_CONNECTED,
                   FSMDEF_CC_CALLER_ID);
         /*
          * If DSP is not able to start rx/tx channels, release the call
          */
         if (dcb->dsp_out_of_resources == TRUE) {
             cc_call_state(fcb->dcb->call_id, fcb->dcb->line,
                 CC_STATE_UNKNOWN, NULL);
-            ui_set_local_description(evSetLocalDescError, line, call_id,
-                dcb->caller_id.call_instance_id, strlib_empty(),
+            ui_set_local_description(evSetLocalDescError, fcb->state, line,
+                call_id, dcb->caller_id.call_instance_id, strlib_empty(),
                 PC_INTERNAL_ERROR, "Cannot start media channels; cause = %s",
                 cc_cause_name(cause));
             return (SM_RC_END);
         }
         /* we may want to use the functionality in the following method
          *  to handle media capability changes, needs discussion
          * fsmdef_transition_to_connected(fcb);
          */
         fsm_change_state(fcb, __LINE__, FSMDEF_S_STABLE);
         break;
 
     case JSEP_PRANSWER:
-        if (event->state != FSMDEF_S_HAVE_REMOTE_OFFER &&
-            event->state != FSMDEF_S_HAVE_LOCAL_PRANSWER) {
-            ui_set_local_description(evSetLocalDescError, line, call_id,
-                dcb->caller_id.call_instance_id, strlib_empty(),
+        if (fcb->state != FSMDEF_S_HAVE_REMOTE_OFFER &&
+            fcb->state != FSMDEF_S_HAVE_LOCAL_PRANSWER) {
+            ui_set_local_description(evSetLocalDescError, fcb->state, line,
+                call_id, dcb->caller_id.call_instance_id, strlib_empty(),
                 PC_INVALID_STATE, "Cannot set local pranswer in state %s",
-                fsmdef_state_name(event->state));
+                fsmdef_state_name(fcb->state));
             return (SM_RC_END);
         }
-        ui_set_local_description(evSetLocalDescError, msg->line,
+        ui_set_local_description(evSetLocalDescError, fcb->state, msg->line,
             msg->call_id, dcb->caller_id.call_instance_id, strlib_empty(),
             PC_INTERNAL_ERROR, "Provisional answers are not yet supported");
         return (SM_RC_END);
 
     default:
-        ui_set_local_description(evSetLocalDescError, msg->line,
+        ui_set_local_description(evSetLocalDescError, fcb->state, msg->line,
             msg->call_id, dcb->caller_id.call_instance_id, strlib_empty(),
             PC_INTERNAL_ERROR, "Unknown session description type: %d",action);
         return (SM_RC_END);
     }
 
     /* Encode the current local SDP structure into a char buffer */
     local_sdp = sipsdp_write_to_buf(dcb->sdp->src_sdp, &local_sdp_len);
     if (!local_sdp) {
-        ui_set_local_description(evSetLocalDescError, msg->line, msg->call_id,
-            dcb->caller_id.call_instance_id, strlib_empty(),
+        ui_set_local_description(evSetLocalDescError, fcb->state, msg->line,
+            msg->call_id, dcb->caller_id.call_instance_id, strlib_empty(),
             PC_INTERNAL_ERROR, "Could not encode local SDP for local "
             "description");
         return (SM_RC_END);
     }
 
-    ui_set_local_description(evSetLocalDescSuccess, msg->line, msg->call_id,
-        dcb->caller_id.call_instance_id, strlib_malloc(local_sdp,-1),
-        PC_NO_ERROR, NULL);
+    ui_set_local_description(evSetLocalDescSuccess, fcb->state, msg->line,
+        msg->call_id, dcb->caller_id.call_instance_id,
+        strlib_malloc(local_sdp,-1), PC_NO_ERROR, NULL);
 
     free(local_sdp);
     return (SM_RC_END);
 }
 
 /**
  * SetRemoteDescription
  *
@@ -3604,41 +3605,41 @@ fsmdef_ev_setremotedesc(sm_event_t *even
     char                *remote_sdp = 0;
     uint32_t            remote_sdp_len = 0;
 
     FSM_DEBUG_SM(DEB_F_PREFIX"Entered.", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
 
     if (dcb == NULL) {
         FSM_DEBUG_SM(DEB_F_PREFIX"dcb is NULL.",
           DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
-        ui_set_remote_description(evSetRemoteDescError, line, call_id,
-            0, strlib_empty(),
+        fsm_change_state(fcb, __LINE__, FSMDEF_S_CLOSED);
+        ui_set_remote_description(evSetRemoteDescError, fcb->state, line,
+            call_id, 0, strlib_empty(),
             PC_INTERNAL_ERROR, "Unrecoverable error: dcb is NULL.");
-        fsm_change_state(fcb, __LINE__, FSMDEF_S_CLOSED);
         return (SM_RC_CLEANUP);
     }
 
     config_get_value(CFGID_SDPMODE, &sdpmode, sizeof(sdpmode));
     if (!sdpmode) {
-        ui_set_remote_description(evSetRemoteDescError, line, call_id,
-            dcb->caller_id.call_instance_id, strlib_empty(),
+        fsm_change_state(fcb, __LINE__, FSMDEF_S_CLOSED);
+        ui_set_remote_description(evSetRemoteDescError, fcb->state, line,
+            call_id, dcb->caller_id.call_instance_id, strlib_empty(),
             PC_INTERNAL_ERROR, "'sdpmode' configuration is false. This should "
             "never ever happen. Run for your lives!");
-        fsm_change_state(fcb, __LINE__, FSMDEF_S_CLOSED);
         return (SM_RC_END);
     }
 
     // XXX We don't currently support renegotiation. If the remote SDP
     // has already been set, return an error to the application. This is
     // temporary until Bug 840728 is implemented.
     if (dcb->sdp && dcb->sdp->dest_sdp) {
         FSM_DEBUG_SM(DEB_F_PREFIX"Renegotiation not currently supported.",
                      DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
-        ui_set_remote_description(evSetRemoteDescError, line, call_id,
-            dcb->caller_id.call_instance_id, strlib_empty(),
+        ui_set_remote_description(evSetRemoteDescError, fcb->state, line,
+            call_id, dcb->caller_id.call_instance_id, strlib_empty(),
             PC_INVALID_STATE, "Renegotiation of session description is not "
             "currently supported. See Bug 840728 for status.");
         return (SM_RC_END);
     }
 
     cc_initialize_msg_body_parts_info(&msg_body);
 
     /* !!! NOTE !!! The following code sets up the pointers inside
@@ -3656,28 +3657,28 @@ fsmdef_ev_setremotedesc(sm_event_t *even
     part->body_length = body_length;
     part->content_type = cc_content_type_SDP;
     part->content_disposition.required_handling = FALSE;
     part->content_disposition.disposition = cc_disposition_session;
     part->content_id = NULL;
 
     switch (action) {
     case JSEP_OFFER:
-        if (event->state != FSMDEF_S_STABLE &&
-            event->state != FSMDEF_S_HAVE_REMOTE_OFFER) {
-            ui_set_remote_description(evSetRemoteDescError, line, call_id,
-                dcb->caller_id.call_instance_id, strlib_empty(),
+        if (fcb->state != FSMDEF_S_STABLE &&
+            fcb->state != FSMDEF_S_HAVE_REMOTE_OFFER) {
+            ui_set_remote_description(evSetRemoteDescError, fcb->state, line,
+            call_id, dcb->caller_id.call_instance_id, strlib_empty(),
                 PC_INVALID_STATE, "Cannot set remote offer in state %s",
-                fsmdef_state_name(event->state));
+                fsmdef_state_name(fcb->state));
             return (SM_RC_END);
         }
         cause = gsmsdp_process_offer_sdp(fcb, &msg_body, TRUE);
         if (cause != CC_CAUSE_OK) {
-            ui_set_remote_description(evSetRemoteDescError, line, call_id,
-                dcb->caller_id.call_instance_id, strlib_empty(),
+            ui_set_remote_description(evSetRemoteDescError, fcb->state, line,
+            call_id, dcb->caller_id.call_instance_id, strlib_empty(),
                 PC_INTERNAL_ERROR, "Could not process offer SDP; "
                 "cause = %s", cc_cause_name(cause));
             return (SM_RC_END);
         }
 
         /*
          * Determine what media types are offered, used to create matching
          * local SDP for negotiation.
@@ -3687,66 +3688,66 @@ fsmdef_ev_setremotedesc(sm_event_t *even
 
         /*
          * The sdp member of the dcb has local and remote sdp
          * this next function fills in the local part
          */
         cause = gsmsdp_create_local_sdp(dcb, TRUE, has_audio, has_video,
             has_data, FALSE);
         if (cause != CC_CAUSE_OK) {
-            ui_set_remote_description(evSetRemoteDescError, line, call_id,
-              dcb->caller_id.call_instance_id, strlib_empty(),
+            ui_set_remote_description(evSetRemoteDescError, fcb->state, line,
+              call_id, dcb->caller_id.call_instance_id, strlib_empty(),
               PC_INTERNAL_ERROR, "Could not create local SDP; cause = %s",
               cc_cause_name(cause));
             FSM_DEBUG_SM("%s", get_debug_string(FSM_DBG_SDP_BUILD_ERR));
             // Force clean up call without sending release
             return (fsmdef_release(fcb, cause, FALSE));
         }
 
         cause = gsmsdp_negotiate_media_lines(fcb, dcb->sdp,
             TRUE, TRUE, TRUE, FALSE);
         if (cause != CC_CAUSE_OK) {
-            ui_set_remote_description(evSetRemoteDescError, line, call_id,
-              dcb->caller_id.call_instance_id, strlib_empty(),
+            ui_set_remote_description(evSetRemoteDescError, fcb->state, line,
+            call_id, dcb->caller_id.call_instance_id, strlib_empty(),
               PC_INTERNAL_ERROR, "Could not negotiate media lines; cause = %s",
               cc_cause_name(cause));
             return (fsmdef_release(fcb, cause, FALSE));
         }
 
         gsmsdp_clean_media_list(dcb);
 
         fsm_change_state(fcb, __LINE__, FSMDEF_S_HAVE_REMOTE_OFFER);
         break;
 
     case JSEP_ANSWER:
-        if (event->state != FSMDEF_S_HAVE_LOCAL_OFFER &&
-            event->state != FSMDEF_S_HAVE_REMOTE_PRANSWER) {
-            ui_set_remote_description(evSetRemoteDescError, line, call_id,
-                dcb->caller_id.call_instance_id, strlib_empty(),
+        if (fcb->state != FSMDEF_S_HAVE_LOCAL_OFFER &&
+            fcb->state != FSMDEF_S_HAVE_REMOTE_PRANSWER) {
+            ui_set_remote_description(evSetRemoteDescError, fcb->state, line,
+            call_id, dcb->caller_id.call_instance_id, strlib_empty(),
                 PC_INVALID_STATE, "Cannot set remote answer in state %s",
-                fsmdef_state_name(event->state));
+                fsmdef_state_name(fcb->state));
             return (SM_RC_END);
         }
         cause = gsmsdp_negotiate_answer_sdp(fcb, &msg_body);
         if (cause != CC_CAUSE_OK) {
-            ui_set_remote_description(evSetRemoteDescError, line, call_id,
-                dcb->caller_id.call_instance_id, strlib_empty(),
+            ui_set_remote_description(evSetRemoteDescError, fcb->state, line,
+                call_id, dcb->caller_id.call_instance_id, strlib_empty(),
                 PC_INTERNAL_ERROR, "Could not negotiate answer SDP; cause = %s",
                 cc_cause_name(cause));
             return (SM_RC_END);
         }
 
         /*
          * Now that we have negotiated the media, time to set up ICE.
          * There also needs to be an ICE check in negotiate_media_lines.
          */
         cause = gsmsdp_install_peer_ice_attributes(fcb);
         if (cause != CC_CAUSE_OK) {
-            ui_set_remote_description(evSetRemoteDescError, line, call_id,
-                dcb->caller_id.call_instance_id, strlib_empty(),
+            ui_set_remote_description(evSetRemoteDescError, fcb->state, line,
+                call_id, dcb->caller_id.call_instance_id, strlib_empty(),
                 PC_INTERNAL_ERROR, "Could not configure local ICE state"
                 " from SDP; cause = %s", cc_cause_name(cause));
             return (SM_RC_END);
         }
 
         cc_call_state(dcb->call_id, dcb->line, CC_STATE_CONNECTED,
             FSMDEF_CC_CALLER_ID);
 
@@ -3754,53 +3755,53 @@ fsmdef_ev_setremotedesc(sm_event_t *even
          * to handle media capability changes, needs discussion
          * fsmdef_transition_to_connected(fcb);
          */
 
         fsm_change_state(fcb, __LINE__, FSMDEF_S_STABLE);
         break;
 
     case JSEP_PRANSWER:
-        if (event->state != FSMDEF_S_HAVE_LOCAL_OFFER &&
-            event->state != FSMDEF_S_HAVE_REMOTE_PRANSWER) {
-            ui_set_remote_description(evSetRemoteDescError, line, call_id,
-                dcb->caller_id.call_instance_id, strlib_empty(),
+        if (fcb->state != FSMDEF_S_HAVE_LOCAL_OFFER &&
+            fcb->state != FSMDEF_S_HAVE_REMOTE_PRANSWER) {
+            ui_set_remote_description(evSetRemoteDescError, fcb->state, line,
+                call_id, dcb->caller_id.call_instance_id, strlib_empty(),
                 PC_INVALID_STATE, "Cannot set remote pranswer in state %s",
-                fsmdef_state_name(event->state));
+                fsmdef_state_name(fcb->state));
             return (SM_RC_END);
         }
-        ui_set_local_description(evSetLocalDescError, msg->line,
+        ui_set_local_description(evSetLocalDescError, fcb->state, msg->line,
             msg->call_id, dcb->caller_id.call_instance_id, strlib_empty(),
             PC_INTERNAL_ERROR, "Provisional answers are not yet supported");
         return (SM_RC_END);
 
     default:
-        ui_set_local_description(evSetLocalDescError, msg->line,
+        ui_set_local_description(evSetLocalDescError, fcb->state, msg->line,
             msg->call_id, dcb->caller_id.call_instance_id, strlib_empty(),
             PC_INTERNAL_ERROR, "Unknown session description type: %d",action);
         return (SM_RC_END);
     }
 
     /* For the sake of accuracy, we regenerate the SDP text from our parsed
        version: if we have any local variation in how we've interpreted the
        received SDP, then remoteDescription will reflect that variation. In
        practice, this shouldn't happen; but, if it does, at least this will
        allow the WebRTC application to figure out what's going on. */
 
     remote_sdp = sipsdp_write_to_buf(dcb->sdp->dest_sdp, &remote_sdp_len);
 
     if (!remote_sdp) {
-        ui_set_remote_description(evSetRemoteDescError, line, call_id,
-            dcb->caller_id.call_instance_id, strlib_empty(),
+        ui_set_remote_description(evSetRemoteDescError, fcb->state, line,
+        call_id, dcb->caller_id.call_instance_id, strlib_empty(),
             PC_INTERNAL_ERROR, "Could not serialize remote description;"
             " cause = %s",  cc_cause_name(cause));
         return (SM_RC_END);
     }
 
-    ui_set_remote_description(evSetRemoteDescSuccess, line, call_id,
+    ui_set_remote_description(evSetRemoteDescSuccess, fcb->state, line, call_id,
         dcb->caller_id.call_instance_id, strlib_malloc(remote_sdp,-1),
         PC_NO_ERROR, NULL);
 
     free(remote_sdp);
 
     return (SM_RC_END);
 }
 
@@ -3974,36 +3975,36 @@ fsmdef_ev_addcandidate(sm_event_t *event
     uint32_t            remote_sdp_len = 0;
     char                *candidate = 0;
     char                candidate_tmp[CANDIDATE_SIZE];
 
     FSM_DEBUG_SM(DEB_F_PREFIX"Entered.", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
 
     if (!dcb) {
         FSM_DEBUG_SM(DEB_F_PREFIX"dcb is NULL.", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
-        ui_ice_candidate_add(evAddIceCandidateError, line, call_id,
+        ui_ice_candidate_add(evAddIceCandidateError, fcb->state, line, call_id,
             0, strlib_empty(), PC_INTERNAL_ERROR, "DCB has not been created.");
         return SM_RC_CLEANUP;
     }
 
     config_get_value(CFGID_SDPMODE, &sdpmode, sizeof(sdpmode));
     if (sdpmode == FALSE) {
-        ui_ice_candidate_add(evAddIceCandidateError, line, call_id,
+        ui_ice_candidate_add(evAddIceCandidateError, fcb->state, line, call_id,
             dcb->caller_id.call_instance_id, strlib_empty(),
             PC_INTERNAL_ERROR, "'sdpmode' configuration is false. This should "
             "never ever happen. Run for your lives!");
         return (SM_RC_END);
     }
 
     if (!dcb->sdp) {
         FSM_DEBUG_SM(DEB_F_PREFIX"dcb->sdp is NULL. Has the "
             "remote description been set yet?\n",
             DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
 
-        ui_ice_candidate_add(evAddIceCandidateError, line, call_id,
+        ui_ice_candidate_add(evAddIceCandidateError, fcb->state, line, call_id,
             dcb->caller_id.call_instance_id, strlib_empty(),
             PC_INVALID_STATE, "Cannot add remote ICE candidates before "
                               "setting remote SDP.");
 
         return SM_RC_END;
     }
 
     /* Perform level lookup based on mid value */
@@ -4040,24 +4041,24 @@ fsmdef_ev_addcandidate(sm_event_t *event
     }
 
     /* Serialize the updated SDP and inform the PeerConnection of the
        new SDP contents. */
 
     remote_sdp = sipsdp_write_to_buf(dcb->sdp->dest_sdp, &remote_sdp_len);
 
     if (!remote_sdp) {
-        ui_ice_candidate_add(evAddIceCandidateError, line, call_id,
+        ui_ice_candidate_add(evAddIceCandidateError, fcb->state, line, call_id,
             dcb->caller_id.call_instance_id, strlib_empty(),
             PC_INTERNAL_ERROR, "Could not serialize new SDP after adding ICE "
             "candidate.");
         return (SM_RC_END);
     }
 
-    ui_ice_candidate_add(evAddIceCandidate, line, call_id,
+    ui_ice_candidate_add(evAddIceCandidate, fcb->state, line, call_id,
         dcb->caller_id.call_instance_id, strlib_malloc(remote_sdp,-1),
         PC_NO_ERROR, NULL);
 
     free(remote_sdp);
     return (SM_RC_END);
 }
 
 static void
--- a/media/webrtc/signaling/src/sipcc/core/gsm/gsm_sdp.c
+++ b/media/webrtc/signaling/src/sipcc/core/gsm/gsm_sdp.c
@@ -4744,17 +4744,17 @@ gsmsdp_negotiate_media_lines (fsm_fcb_t 
                         num_tracks &&
                         (!dcb_p->remote_media_stream_tbl->streams[j].
                          num_tracks_notified)) {
                         /* Note that we only notify when the number of tracks
                            changes from 0 -> !0 (i.e. on creation).
                            TODO(adam@nostrum.com): Figure out how to notify
                            when streams gain tracks */
                         ui_on_remote_stream_added(evOnRemoteStreamAdd,
-                            dcb_p->line, dcb_p->call_id,
+                            fcb_p->state, dcb_p->line, dcb_p->call_id,
                             dcb_p->caller_id.call_instance_id,
                             dcb_p->remote_media_stream_tbl->streams[j]);
 
                         dcb_p->remote_media_stream_tbl->streams[j].num_tracks_notified =
                             dcb_p->remote_media_stream_tbl->streams[j].num_tracks;
                     }
                 }
             }
--- a/media/webrtc/signaling/src/sipcc/core/gsm/h/fsm.h
+++ b/media/webrtc/signaling/src/sipcc/core/gsm/h/fsm.h
@@ -8,16 +8,17 @@
 #include "cpr_types.h"
 #include "sm.h"
 #include "ccapi.h"
 #include "vcm.h"
 #include "ccsip_core.h"
 #include "sll_lite.h"
 #include "sessionConstants.h"
 #include "ccsdp.h"
+#include "fsmdef_states.h"
 
 /* TODO: BLASBERG
  * fsm.h only needs the following from ccsip_core.h
  * should put basic sip types into a separate hdr file
 typedef enum {
     ALERTING_NONE,
     ALERTING_OLD,
     ALERTING_TONE,
@@ -61,49 +62,16 @@ typedef enum {
     FSMDEF_CALL_TYPE_NONE = CC_CALL_TYPE_NONE,
     FSMDEF_CALL_TYPE_INCOMING = CC_CALL_TYPE_INCOMING,
     FSMDEF_CALL_TYPE_OUTGOING = CC_CALL_TYPE_OUTGOING,
     FSMDEF_CALL_TYPE_FORWARD = CC_CALL_TYPE_FORWARDED,
     FSMDEF_CALL_TYPE_MAX
 } fsmdef_call_types_t;
 
 typedef enum {
-    FSMDEF_S_MIN = -1,
-
-    FSMDEF_S_IDLE,
-
-    /* SIP States */
-    FSMDEF_S_COLLECT_INFO,
-    FSMDEF_S_CALL_SENT,
-    FSMDEF_S_OUTGOING_PROCEEDING,
-    FSMDEF_S_KPML_COLLECT_INFO,
-    FSMDEF_S_OUTGOING_ALERTING,
-    FSMDEF_S_INCOMING_ALERTING,
-    FSMDEF_S_CONNECTING,
-    FSMDEF_S_JOINING,
-    FSMDEF_S_CONNECTED,
-    FSMDEF_S_CONNECTED_MEDIA_PEND,
-    FSMDEF_S_RELEASING,
-    FSMDEF_S_HOLD_PENDING,
-    FSMDEF_S_HOLDING,
-    FSMDEF_S_RESUME_PENDING,
-    FSMDEF_S_PRESERVED,
-
-    /* WebRTC States */
-    FSMDEF_S_STABLE,
-    FSMDEF_S_HAVE_LOCAL_OFFER,
-    FSMDEF_S_HAVE_REMOTE_OFFER,
-    FSMDEF_S_HAVE_REMOTE_PRANSWER,
-    FSMDEF_S_HAVE_LOCAL_PRANSWER,
-    FSMDEF_S_CLOSED,
-
-    FSMDEF_S_MAX
-} fsmdef_states_t;
-
-typedef enum {
     FSMDEF_MRTONE_NO_ACTION = 0,
     FSMDEF_MRTONE_PLAYED_MONITOR_TONE,
     FSMDEF_MRTONE_PLAYED_RECORDER_TONE,
     FSMDEF_MRTONE_PLAYED_BOTH_TONES,
     FSMDEF_MRTONE_RESUME_MONITOR_TONE,
     FSMDEF_MRTONE_RESUME_RECORDER_TONE,
     FSMDEF_MRTONE_RESUME_BOTH_TONES
 } fsmdef_monrec_tone_action_e;
--- a/media/webrtc/signaling/src/sipcc/core/includes/sessionTypes.h
+++ b/media/webrtc/signaling/src/sipcc/core/includes/sessionTypes.h
@@ -47,16 +47,17 @@ typedef struct {
   cc_media_track_id_t       track_id;
   cc_media_type_t           media_type;
   cc_level_t                level;
   cc_media_constraints_t *  constraints;
 } ccSession_feature_t;
 
 typedef struct {
   int          state;
+  int          fsm_state;
   int          attr;
   int          inst;
   line_t       line_id;
   int          cause;
   string_t     reason_text;
   string_t     sdp;
   unsigned int media_stream_id;
   unsigned int media_stream_track_id;
--- a/media/webrtc/signaling/src/sipcc/core/includes/uiapi.h
+++ b/media/webrtc/signaling/src/sipcc/core/includes/uiapi.h
@@ -4,16 +4,17 @@
 
 #ifndef _UIAPI_H_
 #define _UIAPI_H_
 
 #include "cpr_types.h"
 #include "phone_types.h"
 #include "string_lib.h"
 #include "vcm.h"
+#include "fsm.h"
 #include "ccapi.h"
 
 #include "sessionConstants.h"
 typedef enum {
     evMinEvent = 0,
     evOffHook = OFFHOOK,
     evOnHook = ONHOOK,
     evRingOut = RINGOUT,
@@ -175,63 +176,70 @@ void ui_update_video_offered (line_t lin
 void ui_call_stop_ringer(line_t line, callid_t call_id);
 void ui_call_start_ringer(vcm_ring_mode_t ringMode, short once, line_t line, callid_t call_id);
 void ui_BLF_notification (int request_id, cc_blf_state_t blf_state, int app_id);
 void ui_update_media_interface_change(line_t line, callid_t call_id, group_call_event_t event);
 
 /* WebRTC upcalls for PeerConnectionImpl */
 
 void ui_create_offer(call_events event,
+                     fsmdef_states_t new_state,
                      line_t nLine,
                      callid_t nCallID,
                      uint16_t call_instance_id,
                      string_t sdp,
                      pc_error error,
                      const char *format, ...);
 
 void ui_create_answer(call_events event,
+                      fsmdef_states_t new_state,
                       line_t nLine,
                       callid_t nCallID,
                       uint16_t call_instance_id,
                       string_t sdp,
                       pc_error error,
                       const char *format, ...);
 
 void ui_set_local_description(call_events event,
+                              fsmdef_states_t new_state,
                               line_t nLine,
                               callid_t nCallID,
                               uint16_t call_instance_id,
                               string_t sdp,
                               pc_error error,
                               const char *format, ...);
 
 void ui_set_remote_description(call_events event,
+                               fsmdef_states_t new_state,
                                line_t nLine,
                                callid_t nCallID,
                                uint16_t call_instance_id,
                                string_t sdp,
                                pc_error error,
                                const char *format, ...);
 
 void ui_update_local_description(call_events event,
+                                 fsmdef_states_t new_state,
                                  line_t nLine,
                                  callid_t nCallID,
                                  uint16_t call_instance_id,
                                  string_t sdp,
                                  pc_error error,
                                  const char *format, ...);
 
 void ui_ice_candidate_add(call_events event,
+                          fsmdef_states_t new_state,
                           line_t nLine,
                           callid_t nCallID,
                           uint16_t call_instance_id,
                           string_t sdp,
                           pc_error error,
                           const char *format, ...);
 
 void ui_on_remote_stream_added(call_events event,
+                               fsmdef_states_t new_state,
                                line_t nLine,
                                callid_t nCallID,
                                uint16_t call_instance_id,
                                cc_media_remote_track_table_t media_tracks);
 
 
 #endif
--- a/media/webrtc/signaling/src/sipcc/include/ccapi_call_info.h
+++ b/media/webrtc/signaling/src/sipcc/include/ccapi_call_info.h
@@ -2,32 +2,40 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef _CCAPI_CALL_INFO_H_
 #define _CCAPI_CALL_INFO_H_
 
 #include "ccapi_types.h"
 #include "peer_connection_types.h"
+#include "fsmdef_states.h"
 
 /**
  * get Line on which this call is
  * @param [in] handle - call info handle
  * @return cc_line_id_t - line ID
  */
 cc_lineid_t CCAPI_CallInfo_getLine(cc_callinfo_ref_t handle);
 
 /**
  * get Call state
  * @param [in] handle - call info handle
  * @return call state
  */
 cc_call_state_t CCAPI_CallInfo_getCallState(cc_callinfo_ref_t handle);
 
 /**
+ * get FSM state
+ * @param [in] handle - call info handle
+ * @return fsm state
+ */
+fsmdef_states_t CCAPI_CallInfo_getFsmState(cc_callinfo_ref_t handle);
+
+/**
  * get call attributes
  * @param [in] handle - call info handle
  * @return call attributes
  */
 cc_call_attr_t CCAPI_CallInfo_getCallAttr(cc_callinfo_ref_t handle);
 
 /**
  * get Call Type
new file mode 100644
--- /dev/null
+++ b/media/webrtc/signaling/src/sipcc/include/fsmdef_states.h
@@ -0,0 +1,44 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _FSMDEF_STATES_H_
+#define _FSMDEF_STATES_H_
+
+typedef enum {
+    FSMDEF_S_MIN = -1,
+
+    FSMDEF_S_IDLE,
+
+    /* SIP States */
+    FSMDEF_S_COLLECT_INFO,
+    FSMDEF_S_CALL_SENT,
+    FSMDEF_S_OUTGOING_PROCEEDING,
+    FSMDEF_S_KPML_COLLECT_INFO,
+    FSMDEF_S_OUTGOING_ALERTING,
+    FSMDEF_S_INCOMING_ALERTING,
+    FSMDEF_S_CONNECTING,
+    FSMDEF_S_JOINING,
+    FSMDEF_S_CONNECTED,
+    FSMDEF_S_CONNECTED_MEDIA_PEND,
+    FSMDEF_S_RELEASING,
+    FSMDEF_S_HOLD_PENDING,
+    FSMDEF_S_HOLDING,
+    FSMDEF_S_RESUME_PENDING,
+    FSMDEF_S_PRESERVED,
+
+    /* WebRTC States */
+    /* MUST be in the same order as PeerConnectionImpl::SignalingState */
+    FSMDEF_S_STABLE,
+    FSMDEF_S_HAVE_LOCAL_OFFER,
+    FSMDEF_S_HAVE_REMOTE_OFFER,
+    FSMDEF_S_HAVE_LOCAL_PRANSWER,
+    FSMDEF_S_HAVE_REMOTE_PRANSWER,
+    FSMDEF_S_CLOSED,
+
+    FSMDEF_S_MAX
+} fsmdef_states_t;
+
+const char * fsmdef_state_name (int state);
+
+#endif
--- a/media/webrtc/signaling/src/softphonewrapper/CC_SIPCCCallInfo.cpp
+++ b/media/webrtc/signaling/src/softphonewrapper/CC_SIPCCCallInfo.cpp
@@ -8,16 +8,17 @@
 
 #include "CC_SIPCCCallInfo.h"
 #include "CC_SIPCCLine.h"
 
 extern "C"
 {
 #include "ccapi_call.h"
 #include "ccapi_call_info.h"
+#include "fsmdef_states.h"
 }
 
 static const char* logTag = "CC_SIPCCCallInfo";
 
 using namespace std;
 using namespace CSF;
 
 CC_SIPCCCallInfo::CC_SIPCCCallInfo (cc_callinfo_ref_t callinfo) : callinfo_ref(callinfo)
@@ -51,16 +52,26 @@ CC_LinePtr CC_SIPCCCallInfo::getLine ()
 }
 */
 
 cc_call_state_t CC_SIPCCCallInfo::getCallState()
 {
     return CCAPI_CallInfo_getCallState(callinfo_ref);
 }
 
+fsmdef_states_t CC_SIPCCCallInfo::getFsmState() const
+{
+    return CCAPI_CallInfo_getFsmState(callinfo_ref);
+}
+
+std::string CC_SIPCCCallInfo::fsmStateToString (fsmdef_states_t state) const
+{
+  return fsmdef_state_name(state);
+}
+
 std::string CC_SIPCCCallInfo::callStateToString (cc_call_state_t state)
 {
   std::string statestr = "";
 
     switch(state) {
       case OFFHOOK:
         statestr = "OFFHOOK";
         break;
--- a/media/webrtc/signaling/src/softphonewrapper/CC_SIPCCCallInfo.h
+++ b/media/webrtc/signaling/src/softphonewrapper/CC_SIPCCCallInfo.h
@@ -30,23 +30,25 @@ namespace CSF
         CSF_DECLARE_WRAP(CC_SIPCCCallInfo, cc_callinfo_ref_t);
 
         CC_SIPCCCallMediaDataPtr  pMediaData;
 
     public:
         ~CC_SIPCCCallInfo ();
 
         cc_call_state_t getCallState ();
+        fsmdef_states_t getFsmState () const;
         bool getRingerState();
 
         virtual cc_call_attr_t getCallAttr();
 
 
         virtual CC_LinePtr getline ();
         virtual std::string callStateToString (cc_call_state_t state);
+        virtual std::string fsmStateToString (fsmdef_states_t state) const;
         virtual std::string callEventToString (ccapi_call_event_e callEvent);
         virtual cc_call_type_t getCallType();
         virtual std::string getCalledPartyName();
         virtual std::string getCalledPartyNumber();
         virtual std::string getCallingPartyName();
         virtual std::string getCallingPartyNumber();
         virtual std::string getAlternateNumber();
         virtual bool hasCapability (CC_CallCapabilityEnum::CC_CallCapability capability);