Bug 906843 - Instrument signaling for isolation of system delays r=ehugg
authorAdam Roach [:abr] <adam@nostrum.com>
Wed, 04 Sep 2013 18:50:28 -0500
changeset 145530 95e4b6b44eebb9ab0fdf4389f13eb2f3ea527ec1
parent 145529 7509132548d925670b3051c919f901b9b96dd619
child 145531 ba4acc701c7eba41836a681babc05ded9136bd5f
push id33311
push useradam@nostrum.com
push dateWed, 04 Sep 2013 23:50:41 +0000
treeherdermozilla-inbound@95e4b6b44eeb [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersehugg
bugs906843
milestone26.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 906843 - Instrument signaling for isolation of system delays r=ehugg
dom/media/bridge/Makefile.in
media/webrtc/signaling/include/CC_Call.h
media/webrtc/signaling/include/CC_CallInfo.h
media/webrtc/signaling/signaling.gyp
media/webrtc/signaling/src/common/time_profiling/timecard.c
media/webrtc/signaling/src/common/time_profiling/timecard.h
media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h
media/webrtc/signaling/src/sipcc/core/ccapp/CCProvider.h
media/webrtc/signaling/src/sipcc/core/ccapp/cc_call_feature.c
media/webrtc/signaling/src/sipcc/core/ccapp/ccapi_call.c
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/ccapi.c
media/webrtc/signaling/src/sipcc/core/gsm/fsmdef.c
media/webrtc/signaling/src/sipcc/core/includes/ccapi.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/cc_call_feature.h
media/webrtc/signaling/src/sipcc/include/ccapi_call.h
media/webrtc/signaling/src/sipcc/include/ccapi_call_info.h
media/webrtc/signaling/src/softphonewrapper/CC_SIPCCCall.cpp
media/webrtc/signaling/src/softphonewrapper/CC_SIPCCCall.h
media/webrtc/signaling/src/softphonewrapper/CC_SIPCCCallInfo.cpp
media/webrtc/signaling/src/softphonewrapper/CC_SIPCCCallInfo.h
media/webrtc/signaling/test/Makefile.in
media/webrtc/signaling/test/sdp_unittests.cpp
--- a/dom/media/bridge/Makefile.in
+++ b/dom/media/bridge/Makefile.in
@@ -13,12 +13,13 @@ include $(DEPTH)/config/autoconf.mk
 EXPORT_LIBRARY = 1
 LOCAL_INCLUDES += \
   -I$(topsrcdir)/media/mtransport \
   -I$(topsrcdir)/media/webrtc/signaling/include \
   -I$(topsrcdir)/media/webrtc/signaling/src/sipcc/include \
   -I$(topsrcdir)/media/webrtc/signaling/src/peerconnection \
   -I$(topsrcdir)/media/webrtc/signaling/src/mediapipeline \
   -I$(topsrcdir)/media/webrtc/signaling/src/media-conduit \
+  -I$(topsrcdir)/media/webrtc/signaling/src/common/time_profiling \
   -I$(topsrcdir)/ipc/chromium/src \
   $(NULL)
 
 include $(topsrcdir)/config/rules.mk
--- a/media/webrtc/signaling/include/CC_Call.h
+++ b/media/webrtc/signaling/include/CC_Call.h
@@ -8,16 +8,22 @@
 #include "ECC_Types.h"
 #include "mozilla/RefPtr.h"
 
 extern "C"
 {
 #include "ccapi_types.h"
 }
 
+#if defined(__cplusplus) && __cplusplus >= 201103L
+typedef struct Timecard Timecard;
+#else
+#include "timecard.h"
+#endif
+
 namespace CSF
 {
     class ECC_API CC_Call
     {
     public:
         NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CC_Call)
 
     protected:
@@ -267,29 +273,29 @@ namespace CSF
            @param [in] video_pref - video direction desired on call
            @param [in] digits - digits to be dialed. can be empty then this API simply goes offhook
            @param [in] ip address - the ip address of the peer to call
 
            @return void
           */
         virtual void originateP2PCall (cc_sdp_direction_t video_pref, const std::string & digits, const std::string & ip) = 0;
 
-        virtual void createOffer (cc_media_constraints_t* constraints) = 0;
+        virtual void createOffer (cc_media_constraints_t* constraints, Timecard *) = 0;
 
-        virtual void createAnswer(cc_media_constraints_t* constraints) = 0;
+        virtual void createAnswer(cc_media_constraints_t* constraints, Timecard *) = 0;
 
-        virtual void setLocalDescription(cc_jsep_action_t action, const std::string & sdp) = 0;
+        virtual void setLocalDescription(cc_jsep_action_t action, const std::string & sdp, Timecard *) = 0;
 
-        virtual void setRemoteDescription(cc_jsep_action_t action, const std::string & sdp) = 0;
+        virtual void setRemoteDescription(cc_jsep_action_t action, const std::string & sdp, Timecard *) = 0;
 
         virtual void setPeerConnection(const std::string& handle) = 0;
 
         virtual void addStream(cc_media_stream_id_t stream_id, cc_media_track_id_t track_id, cc_media_type_t media_type) = 0;
 
         virtual void removeStream(cc_media_stream_id_t stream_id, cc_media_track_id_t track_id, cc_media_type_t media_type) = 0;
 
         virtual const std::string& getPeerConnection() const = 0;
 
-        virtual void addICECandidate(const std::string & candidate, const std::string & mid, unsigned short level) = 0;
+        virtual void addICECandidate(const std::string & candidate, const std::string & mid, unsigned short level, Timecard *) = 0;
 
     };
 }
 
--- a/media/webrtc/signaling/include/CC_CallInfo.h
+++ b/media/webrtc/signaling/include/CC_CallInfo.h
@@ -7,20 +7,26 @@
 #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"
 
+#if defined(__cplusplus) && __cplusplus >= 201103L
+typedef struct Timecard Timecard;
+#else
+#include "timecard.h"
+#endif
 
 namespace CSF
 {
 
 	class ECC_API CC_CallInfo
     {
     public:
         NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CC_CallInfo)
@@ -345,10 +351,16 @@ namespace CSF
 
         /**
            get media streams
            @return media stream table
            Note:Ownership of the MediaStreamTable is responsibiliy of
            the caller.
          */
         virtual MediaStreamTable* getMediaStreams() const = 0;
+
+        /**
+          Get the current operation's timecard (if any), and assume ownership
+          of its memory.
+         */
+        virtual Timecard *takeTimecard() = 0;
     };
 };
--- a/media/webrtc/signaling/signaling.gyp
+++ b/media/webrtc/signaling/signaling.gyp
@@ -36,16 +36,17 @@
       # INCLUDES
       #
       'include_dirs': [
         '..',
         './src',
         './src/callcontrol',
         './src/common',
         './src/common/browser_logging',
+        './src/common/time_profiling',
         './src/media',
         './src/media-conduit',
         './src/mediapipeline',
         './src/softphonewrapper',
         './src/peerconnection',
         './include',
         './src/sipcc/include',
         './src/sipcc/cpr/include',
@@ -89,16 +90,19 @@
         './src/common/CommonTypes.h',
         './src/common/csf_common.h',
         './src/common/NullDeleter.h',
         './src/common/Wrapper.h',
         './src/common/NullTransport.h',
         # Browser Logging
         './src/common/browser_logging/CSFLog.cpp',
         './src/common/browser_logging/CSFLog.h',
+        # Browser Logging
+        './src/common/time_profiling/timecard.c',
+        './src/common/time_profiling/timecard.h',
         # Call Control
         './src/callcontrol/CC_CallTypes.cpp',
         './src/callcontrol/CallControlManager.cpp',
         './src/callcontrol/CallControlManagerImpl.cpp',
         './src/callcontrol/ECC_Types.cpp',
         './src/callcontrol/PhoneDetailsImpl.cpp',
         './src/callcontrol/debug-psipcc-types.cpp',
         './src/callcontrol/CallControlManagerImpl.h',
@@ -262,16 +266,17 @@
       'target_name': 'sipcc',
       'type': 'static_library',
 
       #
       # INCLUDES
       #
       'include_dirs': [
         './src/common/browser_logging',
+        './src/common/time_profiling',
         './src/sipcc/include',
         './src/sipcc/core/includes',
         './src/sipcc/cpr/include',
         './src/sipcc/core/common',
         './src/sipcc/core/sipstack/h',
         './src/sipcc/core/ccapp',
         './src/sipcc/core/sdp',
         './src/sipcc/core/gsm/h',
new file mode 100644
--- /dev/null
+++ b/media/webrtc/signaling/src/common/time_profiling/timecard.c
@@ -0,0 +1,124 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#include <stdio.h>
+#include "timecard.h"
+#include "mozilla/mozalloc.h"
+
+Timecard *
+create_timecard()
+{
+  Timecard *tc = moz_xcalloc(1,sizeof(Timecard));
+  tc->entries_allocated = TIMECARD_INITIAL_TABLE_SIZE;
+  tc->entries = moz_xcalloc(tc->entries_allocated, sizeof(TimecardEntry));
+  tc->start_time = PR_Now();
+  return tc;
+}
+
+void
+destroy_timecard(Timecard *tc)
+{
+  moz_free(tc->entries);
+  moz_free(tc);
+}
+
+void
+stamp_timecard(Timecard *tc,
+               const char *event,
+               const char *file,
+               unsigned int line,
+               const char *function)
+{
+  TimecardEntry *entry = NULL;
+
+  /* Trim the path component from the filename */
+  const char *last_slash = file;
+  while (*file) {
+    if (*file == '/' || *file == '\\') {
+      last_slash = file;
+    }
+    file++;
+  }
+  file = last_slash;
+  if (*file == '/' || *file == '\\') {
+    file++;
+  }
+
+  /* Ensure there is enough space left in the entries list */
+  if (tc->curr_entry == tc->entries_allocated) {
+    tc->entries_allocated *= 2;
+    tc->entries = moz_xrealloc(tc->entries,
+                               tc->entries_allocated * sizeof(TimecardEntry));
+  }
+
+  /* Record the data into the timecard entry */
+  entry = &tc->entries[tc->curr_entry];
+  entry->timestamp = PR_Now();
+  entry->event = event;
+  entry->file = file;
+  entry->line = line;
+  entry->function = function;
+  tc->curr_entry++;
+}
+
+void
+print_timecard(Timecard *tc)
+{
+  size_t i;
+  TimecardEntry *entry;
+  size_t event_width = 5;
+  size_t file_width = 4;
+  size_t function_width = 8;
+  size_t line_width;
+  PRTime offset, delta;
+
+  for (i = 0; i < tc->curr_entry; i++) {
+    entry = &tc->entries[i];
+    if (strlen(entry->event) > event_width) {
+      event_width = strlen(entry->event);
+    }
+    if (strlen(entry->file) > file_width) {
+      file_width = strlen(entry->file);
+    }
+    if (strlen(entry->function) > function_width) {
+      function_width = strlen(entry->function);
+    }
+  }
+
+  printf("\nTimecard created %4lld.%6.6lld\n\n",
+          tc->start_time / PR_USEC_PER_SEC, tc->start_time % PR_USEC_PER_SEC);
+
+  line_width = 1 + 11 + 11 + event_width + file_width + 6 +
+                   function_width + (4 * 3);
+
+  printf(" %-11s | %-11s | %-*s | %-*s | %-*s\n",
+          "Timestamp", "Delta",
+          (int)event_width, "Event",
+          (int)file_width + 6, "File",
+          (int)function_width, "Function");
+
+  for (i = 0; i <= line_width; i++) {
+    printf("=");
+  }
+  printf("\n");
+
+  for (i = 0; i < tc->curr_entry; i++) {
+    entry = &tc->entries[i];
+    offset = entry->timestamp - tc->start_time;
+    if (i > 0) {
+      delta = entry->timestamp - tc->entries[i-1].timestamp;
+    } else {
+      delta = entry->timestamp - tc->start_time;
+    }
+    printf(" %4lld.%6.6lld | %4lld.%6.6lld | %-*s | %*s:%-5d | %-*s\n",
+           offset / PR_USEC_PER_SEC, offset % PR_USEC_PER_SEC,
+           delta / PR_USEC_PER_SEC, delta % PR_USEC_PER_SEC,
+           (int)event_width, entry->event,
+           (int)file_width, entry->file, entry->line,
+           (int)function_width, entry->function);
+  }
+  printf("\n");
+}
new file mode 100644
--- /dev/null
+++ b/media/webrtc/signaling/src/common/time_profiling/timecard.h
@@ -0,0 +1,81 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 timecard_h__
+#define timecard_h__
+
+#include <stdlib.h>
+#include "prtime.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define STAMP_TIMECARD(card,event)                                       \
+  do {                                                                   \
+    if (card) {                                                          \
+      stamp_timecard((card), (event), __FILE__, __LINE__, __FUNCTION__); \
+    }                                                                    \
+  } while (0)
+
+#define TIMECARD_INITIAL_TABLE_SIZE 16
+
+/*
+ * The "const char *" members of this structure point to static strings.
+ * We do not own them, and should not attempt to deallocate them.
+ */
+
+typedef struct {
+  PRTime timestamp;
+  const char *event;
+  const char *file;
+  unsigned int line;
+  const char *function;
+} TimecardEntry;
+
+typedef struct Timecard {
+  size_t curr_entry;
+  size_t entries_allocated;
+  TimecardEntry *entries;
+  PRTime start_time;
+} Timecard;
+
+/**
+ * Creates a new Timecard structure for tracking events.
+ */
+Timecard *
+create_timecard();
+
+/**
+ * Frees the memory associated with a timecard. After returning, the
+ * timecard pointed to by tc is no longer valid.
+ */
+void
+destroy_timecard(Timecard *tc);
+
+/**
+ * Records a new event in the indicated timecard. This should not be
+ * called directly; code should instead use the STAMP_TIMECARD macro,
+ * above.
+ */
+void
+stamp_timecard(Timecard *tc,
+               const char *event,
+               const char *file,
+               unsigned int line,
+               const char *function);
+
+/**
+ * Formats and outputs the contents of a timecard onto stdout.
+ */
+void
+print_timecard(Timecard *tc);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
@@ -2,16 +2,17 @@
  * 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/. */
 
 #include <string>
 
 #include "base/histogram.h"
 #include "vcm.h"
 #include "CSFLog.h"
+#include "timecard.h"
 #include "ccapi_call_info.h"
 #include "CC_SIPCCCallInfo.h"
 #include "ccapi_device_info.h"
 #include "CC_SIPCCDeviceInfo.h"
 #include "cpr_string.h"
 #include "cpr_stdlib.h"
 
 #include "jsapi.h"
@@ -66,16 +67,25 @@ namespace mozilla {
 }
 
 class nsIDOMDataChannel;
 
 static const char* logTag = "PeerConnectionImpl";
 static const int DTLS_FINGERPRINT_LENGTH = 64;
 static const int MEDIA_STREAM_MUTE = 0x80;
 
+PRLogModuleInfo *signalingLogInfo() {
+  static PRLogModuleInfo *logModuleInfo = nullptr;
+  if (!logModuleInfo) {
+    logModuleInfo = PR_NewLogModule("signaling");
+  }
+  return logModuleInfo;
+}
+
+
 namespace sipcc {
 
 void MediaConstraints::setBooleanConstraint(const std::string& constraint, bool enabled, bool mandatory) {
 
   ConstraintInfo booleanconstraint;
   booleanconstraint.mandatory = mandatory;
 
   if (enabled)
@@ -318,17 +328,19 @@ private:
   std::string mStateStr;
   std::string mFsmStateStr;
   nsRefPtr<RemoteSourceStreamInfo> mRemoteStream;
 };
 
 NS_IMPL_ISUPPORTS1(PeerConnectionImpl, IPeerConnection)
 
 PeerConnectionImpl::PeerConnectionImpl()
-: mRole(kRoleUnknown)
+: mTimeCard(PR_LOG_TEST(signalingLogInfo(),PR_LOG_ERROR) ?
+            create_timecard() : nullptr)
+  , mRole(kRoleUnknown)
   , mCall(NULL)
   , mReadyState(kNew)
   , mSignalingState(kSignalingStable)
   , mIceState(kIceGathering)
   , mPCObserver(NULL)
   , mWindow(NULL)
   , mIdentity(NULL)
   , mSTSThread(NULL)
@@ -336,20 +348,27 @@ PeerConnectionImpl::PeerConnectionImpl()
   , mNumAudioStreams(0)
   , mNumVideoStreams(0)
   , mHaveDataStream(false) {
 #ifdef MOZILLA_INTERNAL_API
   MOZ_ASSERT(NS_IsMainThread());
 #endif
   CSFLogInfo(logTag, "%s: PeerConnectionImpl constructor for %s",
              __FUNCTION__, mHandle.c_str());
+  STAMP_TIMECARD(mTimeCard, "Constructor Completed");
 }
 
 PeerConnectionImpl::~PeerConnectionImpl()
 {
+  if (mTimeCard) {
+    STAMP_TIMECARD(mTimeCard, "Destructor Invoked");
+    print_timecard(mTimeCard);
+    destroy_timecard(mTimeCard);
+    mTimeCard = nullptr;
+  }
   // This aborts if not on main thread (in Debug builds)
   PC_AUTO_ENTER_API_CALL_NO_CHECK();
   if (PeerConnectionCtx::isActive()) {
     PeerConnectionCtx::GetInstance()->mPeerConnections.erase(mHandle);
   } else {
     CSFLogError(logTag, "PeerConnectionCtx is already gone. Ignoring...");
   }
 
@@ -582,21 +601,23 @@ PeerConnectionImpl::Initialize(IPeerConn
     handle_bin[3],
     handle_bin[4],
     handle_bin[5],
     handle_bin[6],
     handle_bin[7]);
 
   mHandle = hex;
 
+  STAMP_TIMECARD(mTimeCard, "Initializing PC Ctx");
   res = PeerConnectionCtx::InitializeGlobal(mThread);
   NS_ENSURE_SUCCESS(res, res);
 
   PeerConnectionCtx *pcctx = PeerConnectionCtx::GetInstance();
   MOZ_ASSERT(pcctx);
+  STAMP_TIMECARD(mTimeCard, "Done Initializing PC Ctx");
 
   mCall = pcctx->createCall();
   if(!mCall.get()) {
     CSFLogError(logTag, "%s: Couldn't Create Call Object", __FUNCTION__);
     return NS_ERROR_FAILURE;
   }
 
   IceConfiguration converted;
@@ -623,18 +644,20 @@ PeerConnectionImpl::Initialize(IPeerConn
     CSFLogError(logTag, "%s: Couldn't initialize media object", __FUNCTION__);
     return res;
   }
 
   // Store under mHandle
   mCall->setPeerConnection(mHandle);
   PeerConnectionCtx::GetInstance()->mPeerConnections[mHandle] = this;
 
+  STAMP_TIMECARD(mTimeCard, "Generating DTLS Identity");
   // Create the DTLS Identity
   mIdentity = DtlsIdentity::Generate();
+  STAMP_TIMECARD(mTimeCard, "Done Generating DTLS Identity");
 
   if (!mIdentity) {
     CSFLogError(logTag, "%s: Generate returned NULL", __FUNCTION__);
     return NS_ERROR_FAILURE;
   }
 
   // Set the fingerprint. Right now assume we only have one
   // DTLS identity
@@ -654,24 +677,16 @@ PeerConnectionImpl::Initialize(IPeerConn
   mFingerprint = "sha-1 " + mIdentity->FormatFingerprint(fingerprint,
                                                          fingerprint_length);
   if (NS_FAILED(res)) {
     CSFLogError(logTag, "%s: do_GetService failed: %u",
       __FUNCTION__, static_cast<uint32_t>(res));
     return res;
   }
 
-#ifndef MOZILLA_INTERNAL_API
-  // Busy-wait until we are ready, for C++ unit tests. Remove when tests are fixed.
-  CSFLogDebug(logTag, "%s: Sleeping until kStarted", __FUNCTION__);
-  while(PeerConnectionCtx::GetInstance()->sipcc_state() != kStarted) {
-    PR_Sleep(100);
-  }
-#endif
-
   return NS_OK;
 }
 
 nsresult
 PeerConnectionImpl::CreateFakeMediaStream(uint32_t aHint, nsIDOMMediaStream** aRetval)
 {
   MOZ_ASSERT(aRetval);
   PC_AUTO_ENTER_API_CALL(false);
@@ -998,22 +1013,26 @@ PeerConnectionImpl::CreateOffer(const JS
 }
 
 // Used by unit tests and the IDL CreateOffer.
 NS_IMETHODIMP
 PeerConnectionImpl::CreateOffer(MediaConstraints& constraints)
 {
   PC_AUTO_ENTER_API_CALL(true);
 
+  Timecard *tc = mTimeCard;
+  mTimeCard = nullptr;
+  STAMP_TIMECARD(tc, "Create Offer");
+
   mRole = kRoleOfferer;  // TODO(ekr@rtfm.com): Interrogate SIPCC here?
 
   cc_media_constraints_t* cc_constraints = nullptr;
   constraints.buildArray(&cc_constraints);
 
-  mCall->createOffer(cc_constraints);
+  mCall->createOffer(cc_constraints, tc);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 PeerConnectionImpl::CreateAnswer(const JS::Value& aConstraints, JSContext* aCx)
 {
   PC_AUTO_ENTER_API_CALL(true);
 
@@ -1026,60 +1045,78 @@ PeerConnectionImpl::CreateAnswer(const J
   return CreateAnswer(cs);
 }
 
 NS_IMETHODIMP
 PeerConnectionImpl::CreateAnswer(MediaConstraints& constraints)
 {
   PC_AUTO_ENTER_API_CALL(true);
 
+  Timecard *tc = mTimeCard;
+  mTimeCard = nullptr;
+  STAMP_TIMECARD(tc, "Create Answer");
+
   mRole = kRoleAnswerer;  // TODO(ekr@rtfm.com): Interrogate SIPCC here?
 
   cc_media_constraints_t* cc_constraints = nullptr;
   constraints.buildArray(&cc_constraints);
 
-  mCall->createAnswer(cc_constraints);
+  mCall->createAnswer(cc_constraints, tc);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 PeerConnectionImpl::SetLocalDescription(int32_t aAction, const char* aSDP)
 {
   PC_AUTO_ENTER_API_CALL(true);
 
   if (!aSDP) {
     CSFLogError(logTag, "%s - aSDP is NULL", __FUNCTION__);
     return NS_ERROR_FAILURE;
   }
 
+  Timecard *tc = mTimeCard;
+  mTimeCard = nullptr;
+  STAMP_TIMECARD(tc, "Set Local Description");
+
   mLocalRequestedSDP = aSDP;
-  mCall->setLocalDescription((cc_jsep_action_t)aAction, mLocalRequestedSDP);
+  mCall->setLocalDescription((cc_jsep_action_t)aAction,
+                             mLocalRequestedSDP, tc);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 PeerConnectionImpl::SetRemoteDescription(int32_t action, const char* aSDP)
 {
   PC_AUTO_ENTER_API_CALL(true);
 
   if (!aSDP) {
     CSFLogError(logTag, "%s - aSDP is NULL", __FUNCTION__);
     return NS_ERROR_FAILURE;
   }
 
+  Timecard *tc = mTimeCard;
+  mTimeCard = nullptr;
+  STAMP_TIMECARD(tc, "Set Remote Description");
+
   mRemoteRequestedSDP = aSDP;
-  mCall->setRemoteDescription((cc_jsep_action_t)action, mRemoteRequestedSDP);
+  mCall->setRemoteDescription((cc_jsep_action_t)action,
+                              mRemoteRequestedSDP, tc);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 PeerConnectionImpl::AddIceCandidate(const char* aCandidate, const char* aMid, unsigned short aLevel) {
   PC_AUTO_ENTER_API_CALL(true);
 
-  mCall->addICECandidate(aCandidate, aMid, aLevel);
+  Timecard *tc = mTimeCard;
+  mTimeCard = nullptr;
+  STAMP_TIMECARD(tc, "Add Ice Candidate");
+
+  mCall->addICECandidate(aCandidate, aMid, aLevel, tc);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 PeerConnectionImpl::CloseStreams() {
   PC_AUTO_ENTER_API_CALL(false);
 
   if (mReadyState != PeerConnectionImpl::kClosed)  {
@@ -1362,16 +1399,22 @@ void
 PeerConnectionImpl::onCallEvent(ccapi_call_event_e aCallEvent,
                                 CSF::CC_CallInfoPtr aInfo)
 {
   PC_AUTO_ENTER_API_CALL_NO_CHECK();
   MOZ_ASSERT(aInfo.get());
 
   cc_call_state_t event = aInfo->getCallState();
   std::string statestr = aInfo->callStateToString(event);
+  Timecard *timecard = aInfo->takeTimecard();
+
+  if (timecard) {
+    mTimeCard = timecard;
+    STAMP_TIMECARD(mTimeCard, "Operation Completed");
+  }
 
   if (CCAPI_CALL_EV_CREATED != aCallEvent && CCAPI_CALL_EV_STATE != aCallEvent) {
     CSFLogDebug(logTag, "%s: **** CALL HANDLE IS: %s, **** CALL STATE IS: %s",
       __FUNCTION__, mHandle.c_str(), statestr.c_str());
     return;
   }
 
   switch (event) {
@@ -1511,28 +1554,44 @@ nsresult
 PeerConnectionImpl::IceStateChange_m(IceState aState)
 {
   PC_AUTO_ENTER_API_CALL(false);
 
   CSFLogDebug(logTag, "%s", __FUNCTION__);
 
   mIceState = aState;
 
-#ifdef MOZILLA_INTERNAL_API
+  switch (mIceState) {
+    case kIceGathering:
+      STAMP_TIMECARD(mTimeCard, "Ice state: gathering");
+      break;
+    case kIceWaiting:
+      STAMP_TIMECARD(mTimeCard, "Ice state: waiting");
+      break;
+    case kIceChecking:
+      STAMP_TIMECARD(mTimeCard, "Ice state: checking");
+      break;
+    case kIceConnected:
+      STAMP_TIMECARD(mTimeCard, "Ice state: connected");
+      break;
+    case kIceFailed:
+      STAMP_TIMECARD(mTimeCard, "Ice state: failed");
+      break;
+  }
+
   nsCOMPtr<IPeerConnectionObserver> pco = do_QueryReferent(mPCObserver);
   if (!pco) {
     return NS_OK;
   }
   RUN_ON_THREAD(mThread,
                 WrapRunnable(pco,
                              &IPeerConnectionObserver::OnStateChange,
                              // static_cast required to work around old C++ compiler on Android NDK r5c
                              static_cast<int>(IPeerConnectionObserver::kIceState)),
                 NS_DISPATCH_NORMAL);
-#endif
   return NS_OK;
 }
 
 void
 PeerConnectionImpl::IceStreamReady(NrIceMediaStream *aStream)
 {
   PC_AUTO_ENTER_API_CALL_NO_CHECK();
   MOZ_ASSERT(aStream);
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h
@@ -35,16 +35,22 @@
 #include "VideoSegment.h"
 #include "nsNSSShutDown.h"
 #else
 namespace mozilla {
   class DataChannel;
 }
 #endif
 
+#if defined(__cplusplus) && __cplusplus >= 201103L
+typedef struct Timecard Timecard;
+#else
+#include "timecard.h"
+#endif
+
 using namespace mozilla;
 
 namespace sipcc {
 
 class PeerConnectionWrapper;
 
 struct ConstraintInfo
 {
@@ -323,16 +329,21 @@ private:
 #endif
 
   // Shut down media - called on main thread only
   void ShutdownMedia();
 
   // ICE callbacks run on the right thread.
   nsresult IceStateChange_m(IceState aState);
 
+  // Timecard used to measure processing time. This should be the first class
+  // attribute so that we accurately measure the time required to instantiate
+  // any other attributes of this class.
+  Timecard *mTimeCard;
+
   // The role we are adopting
   Role mRole;
 
   // The call
   CSF::CC_CallPtr mCall;
   ReadyState mReadyState;
   SignalingState mSignalingState;
 
--- a/media/webrtc/signaling/src/sipcc/core/ccapp/CCProvider.h
+++ b/media/webrtc/signaling/src/sipcc/core/ccapp/CCProvider.h
@@ -1,15 +1,21 @@
 /* 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 __CCPROVIDER_H__
 #define __CCPROVIDER_H__
 
+#if defined(__cplusplus) && __cplusplus >= 201103L
+typedef struct Timecard Timecard;
+#else
+#include "timecard.h"
+#endif
+
 #include "cpr_types.h"
 #include "cpr_ipc.h"
 #include "cpr_socket.h"
 #include "cpr_memory.h"
 #include "cpr_errno.h"
 #include "cpr_in.h"
 #include "cpr_rand.h"
 #include "cpr_string.h"
@@ -96,16 +102,17 @@ typedef struct cc_call_info_t_{
     cc_call_log_t call_log;
     cc_boolean    audio_mute;
     cc_boolean    video_mute;
     cc_call_conference_Info_t call_conference;
     cc_string_t   sdp;
     unsigned int  media_stream_track_id;
     unsigned int  media_stream_id;
     cc_media_constraints_t* cc_constraints;
+    Timecard *    timecard;
 } session_data_t;
 
 typedef enum {
     NO_ACTION=0,
     RESET_ACTION,
     RESTART_ACTION,
     RE_REGISTER_ACTION,
     STOP_ACTION,
--- a/media/webrtc/signaling/src/sipcc/core/ccapp/cc_call_feature.c
+++ b/media/webrtc/signaling/src/sipcc/core/ccapp/cc_call_feature.c
@@ -1,12 +1,13 @@
 /* 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/. */
 
+#include "timecard.h"
 #include "cc_call_feature.h"
 #include "CCProvider.h"
 #include "sessionConstants.h"
 #include "sessionTypes.h"
 #include "lsm.h"
 #include "phone_debug.h"
 #include "text_strings.h"
 #include "ccapi.h"
@@ -101,27 +102,29 @@ cc_return_t cc_invokeFeatureSDPMode(cc_c
                                     group_cc_feature_t featureId,
                                     cc_jsep_action_t action,
                                     cc_media_stream_id_t stream_id,
                                     cc_media_track_id_t track_id,
                                     cc_media_type_t media_type,
                                     uint16_t level,
                                     cc_media_constraints_t *constraints,
                                     string_t data,
-                                    string_t data1) {
+                                    string_t data1,
+                                    Timecard *tc) {
     session_feature_t callFeature;
     unsigned int session_id = 0;
     callFeature.session_id = (SESSIONTYPE_CALLCONTROL << CC_SID_TYPE_SHIFT) + call_handle;
     callFeature.featureID = featureId;
     callFeature.featData.ccData.action = action;
     callFeature.featData.ccData.media_type = media_type;
     callFeature.featData.ccData.stream_id = stream_id;
     callFeature.featData.ccData.track_id = track_id;
     callFeature.featData.ccData.level = level;
     callFeature.featData.ccData.constraints = constraints;
+    callFeature.featData.ccData.timecard = tc;
 
     CCAPP_DEBUG(DEB_F_PREFIX"cc_invokeFeatureSDPMode:sid=%d, line=%d, cid=%d, fid=%d, data=%s",
                         DEB_F_PREFIX_ARGS("cc_call_feature", "cc_invokeFeatureSDPMode"),
                         callFeature.session_id,
                         GET_LINE_ID(call_handle),
                         GET_CALL_ID(call_handle),
                         featureId,
                         ((featureId == CC_FEATURE_KEYPRESS) ? "...": data));
@@ -280,88 +283,100 @@ cc_return_t CC_CallFeature_dial(cc_call_
     if (cpr_strcasecmp(numbers, "DIAL") == 0) {
 	    return cc_invokeFeature(call_handle, CC_FEATURE_DIAL, video_pref, numbers);
     }
 
 	return cc_invokeFeature(call_handle, CC_FEATURE_DIALSTR, video_pref, numbers);
 }
 
 cc_return_t CC_CallFeature_CreateOffer(cc_call_handle_t call_handle,
-                                       cc_media_constraints_t *constraints) {
+                                       cc_media_constraints_t *constraints,
+                                       Timecard *tc) {
     CCAPP_DEBUG(DEB_L_C_F_PREFIX, DEB_L_C_F_PREFIX_ARGS(SIP_CC_PROV, GET_CALL_ID(call_handle),
                 GET_LINE_ID(call_handle), __FUNCTION__));
 
     return cc_invokeFeatureSDPMode(call_handle, CC_FEATURE_CREATEOFFER, JSEP_NO_ACTION,
-                                   0, 0, NO_STREAM, 0, constraints, NULL, NULL);
+                                   0, 0, NO_STREAM, 0, constraints, NULL, NULL, tc);
 }
 
 cc_return_t CC_CallFeature_CreateAnswer(cc_call_handle_t call_handle,
-                                        cc_media_constraints_t *constraints) {
+                                        cc_media_constraints_t *constraints,
+                                        Timecard *tc) {
     CCAPP_DEBUG(DEB_L_C_F_PREFIX, DEB_L_C_F_PREFIX_ARGS(SIP_CC_PROV, GET_CALL_ID(call_handle),
                 GET_LINE_ID(call_handle), __FUNCTION__));
 
     return cc_invokeFeatureSDPMode(call_handle, CC_FEATURE_CREATEANSWER, JSEP_NO_ACTION,
-                                   0, 0, NO_STREAM, 0, constraints, NULL, NULL);
+                                   0, 0, NO_STREAM, 0, constraints, NULL, NULL, tc);
 }
 
-cc_return_t CC_CallFeature_SetLocalDescription(cc_call_handle_t call_handle, cc_jsep_action_t action, string_t sdp) {
+cc_return_t CC_CallFeature_SetLocalDescription(cc_call_handle_t call_handle,
+                                               cc_jsep_action_t action,
+                                               string_t sdp,
+                                               Timecard *tc) {
     cc_media_constraints_t *constraints = NULL;
     CCAPP_DEBUG(DEB_L_C_F_PREFIX, DEB_L_C_F_PREFIX_ARGS(SIP_CC_PROV, GET_CALL_ID(call_handle),
             GET_LINE_ID(call_handle), __FUNCTION__));
 
     return cc_invokeFeatureSDPMode(call_handle, CC_FEATURE_SETLOCALDESC, action,
-                                   0, 0, NO_STREAM, 0, constraints, sdp, NULL);
+                                   0, 0, NO_STREAM, 0, constraints, sdp, NULL, tc);
 }
 
-cc_return_t CC_CallFeature_SetRemoteDescription(cc_call_handle_t call_handle, cc_jsep_action_t action, string_t sdp) {
+cc_return_t CC_CallFeature_SetRemoteDescription(cc_call_handle_t call_handle,
+                                                cc_jsep_action_t action,
+                                                string_t sdp,
+                                                Timecard *tc) {
     cc_media_constraints_t *constraints = NULL;
     CCAPP_DEBUG(DEB_L_C_F_PREFIX, DEB_L_C_F_PREFIX_ARGS(SIP_CC_PROV, GET_CALL_ID(call_handle),
             GET_LINE_ID(call_handle), __FUNCTION__));
 
     return cc_invokeFeatureSDPMode(call_handle, CC_FEATURE_SETREMOTEDESC, action,
-                                   0, 0, NO_STREAM, 0, constraints, sdp, NULL);
+                                   0, 0, NO_STREAM, 0, constraints, sdp, NULL, tc);
 }
 
-cc_return_t CC_CallFeature_SetPeerConnection(cc_call_handle_t call_handle, cc_peerconnection_t pc) {
+cc_return_t CC_CallFeature_SetPeerConnection(cc_call_handle_t call_handle,cc_peerconnection_t pc) {
     cc_media_constraints_t *constraints = NULL;
     CCAPP_DEBUG(DEB_L_C_F_PREFIX, DEB_L_C_F_PREFIX_ARGS(SIP_CC_PROV, GET_CALL_ID(call_handle),
                 GET_LINE_ID(call_handle), __FUNCTION__));
 
     return cc_invokeFeatureSDPMode(call_handle, CC_FEATURE_SETPEERCONNECTION, JSEP_NO_ACTION,
-                                   0, 0, NO_STREAM, 0, constraints, pc, NULL);
+                                   0, 0, NO_STREAM, 0, constraints, pc, NULL, NULL);
 }
 
 cc_return_t CC_CallFeature_AddStream(cc_call_handle_t call_handle, cc_media_stream_id_t stream_id,
                                             cc_media_track_id_t track_id, cc_media_type_t media_type) {
     cc_media_constraints_t *constraints = NULL;
     CCAPP_DEBUG(DEB_L_C_F_PREFIX, DEB_L_C_F_PREFIX_ARGS(SIP_CC_PROV, GET_CALL_ID(call_handle),
             GET_LINE_ID(call_handle), __FUNCTION__));
 
     return cc_invokeFeatureSDPMode(call_handle, CC_FEATURE_ADDSTREAM, JSEP_NO_ACTION,
-                                   stream_id, track_id, media_type, 0, constraints, NULL, NULL);
+                                   stream_id, track_id, media_type, 0, constraints, NULL, NULL, NULL);
 }
 
 cc_return_t CC_CallFeature_RemoveStream(cc_call_handle_t call_handle, cc_media_stream_id_t stream_id,
                                                cc_media_track_id_t track_id, cc_media_type_t media_type) {
 
     cc_media_constraints_t *constraints = NULL;
     CCAPP_DEBUG(DEB_L_C_F_PREFIX, DEB_L_C_F_PREFIX_ARGS(SIP_CC_PROV, GET_CALL_ID(call_handle),
                 GET_LINE_ID(call_handle), __FUNCTION__));
 
     return cc_invokeFeatureSDPMode(call_handle, CC_FEATURE_REMOVESTREAM, JSEP_NO_ACTION,
-                                   stream_id, track_id, media_type, 0, constraints, NULL, NULL);
+                                   stream_id, track_id, media_type, 0, constraints, NULL, NULL, NULL);
 }
 
-cc_return_t CC_CallFeature_AddICECandidate(cc_call_handle_t call_handle, const char* candidate, const char *mid, cc_level_t level) {
+cc_return_t CC_CallFeature_AddICECandidate(cc_call_handle_t call_handle,
+                                           const char* candidate,
+                                           const char *mid,
+                                           cc_level_t level,
+                                           Timecard *tc) {
     cc_media_constraints_t *constraints = NULL;
     CCAPP_DEBUG(DEB_L_C_F_PREFIX, DEB_L_C_F_PREFIX_ARGS(SIP_CC_PROV, GET_CALL_ID(call_handle),
             GET_LINE_ID(call_handle), __FUNCTION__));
 
     return cc_invokeFeatureSDPMode(call_handle, CC_FEATURE_ADDICECANDIDATE, JSEP_NO_ACTION,
-                                   0, 0, NO_STREAM, (uint16_t)level, constraints, candidate, mid);
+                                   0, 0, NO_STREAM, (uint16_t)level, constraints, candidate, mid, tc);
 }
 
 /**
  * Initiate a speed dial.
  * @param call handle
  * @param callid call id
  * @param speed dial numbers.
  * @return SUCCESS or FAILURE
--- a/media/webrtc/signaling/src/sipcc/core/ccapp/ccapi_call.c
+++ b/media/webrtc/signaling/src/sipcc/core/ccapp/ccapi_call.c
@@ -1,12 +1,13 @@
 /* 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/. */
 
+#include "timecard.h"
 #include "cpr_stdio.h"
 #include "ccapi_call.h"
 #include "sessionHash.h"
 #include "CCProvider.h"
 #include "cc_call_feature.h"
 #include "cc_info.h"
 #include "lsm.h"
 #include "prot_configmgr.h"
@@ -86,47 +87,59 @@ cc_lineid_t CCAPI_Call_getLine(cc_call_h
  * @param [in] digits - digits to be dialed
  * @return SUCCESS or FAILURE
  */
 cc_return_t CCAPI_Call_originateCall(cc_call_handle_t handle, cc_sdp_direction_t video_pref, cc_string_t digits){
 	return CC_CallFeature_dial(handle, video_pref, digits);
 }
 
 cc_return_t CCAPI_CreateOffer(cc_call_handle_t handle,
-                              cc_media_constraints_t *constraints) {
-	return CC_CallFeature_CreateOffer(handle, constraints);
+                              cc_media_constraints_t *constraints,
+                              Timecard *tc) {
+    return CC_CallFeature_CreateOffer(handle, constraints, tc);
 }
 
 cc_return_t CCAPI_CreateAnswer(cc_call_handle_t handle,
-                               cc_media_constraints_t *constraints) {
-	return CC_CallFeature_CreateAnswer(handle, constraints);
+                               cc_media_constraints_t *constraints,
+                               Timecard *tc) {
+    return CC_CallFeature_CreateAnswer(handle, constraints, tc);
 }
 
-cc_return_t CCAPI_SetLocalDescription(cc_call_handle_t handle, cc_jsep_action_t action, cc_string_t sdp) {
-	return CC_CallFeature_SetLocalDescription(handle, action, sdp);
+cc_return_t CCAPI_SetLocalDescription(cc_call_handle_t handle,
+                                      cc_jsep_action_t action,
+                                      cc_string_t sdp,
+                                      Timecard *tc) {
+    return CC_CallFeature_SetLocalDescription(handle, action, sdp, tc);
 }
 
-cc_return_t CCAPI_SetRemoteDescription(cc_call_handle_t handle, cc_jsep_action_t action, cc_string_t sdp) {
-    return CC_CallFeature_SetRemoteDescription(handle, action, sdp);
+cc_return_t CCAPI_SetRemoteDescription(cc_call_handle_t handle,
+                                       cc_jsep_action_t action,
+                                       cc_string_t sdp,
+                                       Timecard *tc) {
+    return CC_CallFeature_SetRemoteDescription(handle, action, sdp, tc);
 }
 
 cc_return_t CCAPI_SetPeerConnection(cc_call_handle_t handle, cc_peerconnection_t pc) {
   return CC_CallFeature_SetPeerConnection(handle, pc);
 }
 
 cc_return_t CCAPI_AddStream(cc_call_handle_t handle, cc_media_stream_id_t stream_id, cc_media_track_id_t track_id, cc_media_type_t media_type) {
   return CC_CallFeature_AddStream(handle, stream_id, track_id, media_type);
 }
 
 cc_return_t CCAPI_RemoveStream(cc_call_handle_t handle, cc_media_stream_id_t stream_id, cc_media_track_id_t track_id, cc_media_type_t media_type) {
   return CC_CallFeature_RemoveStream(handle, stream_id, track_id, media_type);
 }
 
-cc_return_t CCAPI_AddICECandidate(cc_call_handle_t handle, cc_string_t candidate, cc_string_t mid, cc_level_t level) {
-	return CC_CallFeature_AddICECandidate(handle, candidate, mid, level);
+cc_return_t CCAPI_AddICECandidate(cc_call_handle_t handle,
+                                  cc_string_t candidate,
+                                  cc_string_t mid,
+                                  cc_level_t level,
+                                  Timecard *tc) {
+    return CC_CallFeature_AddICECandidate(handle, candidate, mid, level, tc);
 }
 
 /**
  * Dial digits on the call
  * @param [in] handle - call handle
  * @paraqm [in] digits - digits to be dialed
  * @return SUCCESS or FAILURE
  */
--- a/media/webrtc/signaling/src/sipcc/core/ccapp/ccapi_call_info.c
+++ b/media/webrtc/signaling/src/sipcc/core/ccapp/ccapi_call_info.c
@@ -796,8 +796,28 @@ MediaStreamTable*  CCAPI_CallInfo_getMed
 	   track.track_id = cc_table->track[0].ref_id;
 	   table->track[0] = track;
 	*/
     return table;
   }
 
   return table;
 }
+
+/**
+ * Assume ownership of timecard
+ * @param handle - call handle
+ * @return Timecard pointer
+ */
+Timecard*  CCAPI_CallInfo_takeTimecard(cc_callinfo_ref_t handle) {
+  session_data_t *data = (session_data_t *)handle;
+  Timecard *timecard = NULL;
+
+  CCAPP_DEBUG(DEB_F_PREFIX"Entering", DEB_F_PREFIX_ARGS(SIP_CC_PROV,
+              __FUNCTION__));
+
+  if (data) {
+    timecard = data->timecard;
+    data->timecard = NULL;
+  }
+
+  return timecard;
+}
--- a/media/webrtc/signaling/src/sipcc/core/ccapp/ccprovider.c
+++ b/media/webrtc/signaling/src/sipcc/core/ccapp/ccprovider.c
@@ -1,14 +1,15 @@
 /* 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/. */
 
 #include <limits.h>
 
+#include "timecard.h"
 #include "CCProvider.h"
 #include "ccSession.h"
 #include "ccsip_task.h"
 #include "cpr.h"
 #include "cpr_stdlib.h"
 #include "cpr_ipc.h"
 #include "phone.h"
 #include "phntask.h"
@@ -626,18 +627,20 @@ processSessionEvent (line_t line_id, cal
     int instance = line_id;
     boolean incoming = FALSE;
     session_data_t * sess_data_p;
     char digits[CC_MAX_DIALSTRING_LEN];
     char* data = (char*)ccData.info;
     char* data1 =(char*)ccData.info1;
     long strtol_result;
     char *strtol_end;
+    Timecard *timecard = ccData.timecard;
     int sdpmode = 0;
 
+
     CCAPP_DEBUG(DEB_L_C_F_PREFIX"event=%d data=%s",
                 DEB_L_C_F_PREFIX_ARGS(SIP_CC_PROV, call_id, line_id, fname), event,
                 ((event == CC_FEATURE_KEYPRESS) ? "..." : data));
 
     memset(&featdata, 0, sizeof(cc_feature_data_t));
 
 
     config_get_value(CFGID_SDPMODE, &sdpmode, sizeof(sdpmode));
@@ -655,57 +658,62 @@ processSessionEvent (line_t line_id, cal
 	     break;
          case CC_FEATURE_KEYPRESS:
              dp_int_update_keypress(line_id, call_id, (unsigned char)*data);
              break;
          case CC_FEATURE_BKSPACE:
              dp_int_update_keypress(line_id, call_id, BKSP_KEY);
              break;
          case CC_FEATURE_CREATEOFFER:
+             STAMP_TIMECARD(timecard, "Processing create offer event");
              featdata.session.constraints = ccData.constraints;
-             cc_createoffer (CC_SRC_UI, CC_SRC_GSM, call_id, (line_t)instance, CC_FEATURE_CREATEOFFER, &featdata);
+             cc_createoffer (CC_SRC_UI, CC_SRC_GSM, call_id, (line_t)instance, CC_FEATURE_CREATEOFFER, &featdata, timecard);
              break;
          case CC_FEATURE_CREATEANSWER:
+             STAMP_TIMECARD(timecard, "Processing create answer event");
              featdata.session.constraints = ccData.constraints;
-             cc_createanswer (CC_SRC_UI, CC_SRC_GSM, call_id, (line_t)instance, CC_FEATURE_CREATEANSWER, data, &featdata);
+             cc_createanswer (CC_SRC_UI, CC_SRC_GSM, call_id, (line_t)instance, CC_FEATURE_CREATEANSWER, data, &featdata, timecard);
              break;
          case CC_FEATURE_SETLOCALDESC:
-             cc_setlocaldesc (CC_SRC_UI, CC_SRC_GSM, call_id, (line_t)instance, CC_FEATURE_SETLOCALDESC, ccData.action, data, &featdata);
+             STAMP_TIMECARD(timecard, "Processing set local event");
+             cc_setlocaldesc (CC_SRC_UI, CC_SRC_GSM, call_id, (line_t)instance, CC_FEATURE_SETLOCALDESC, ccData.action, data, &featdata, timecard);
              break;
          case CC_FEATURE_SETREMOTEDESC:
-             cc_setremotedesc (CC_SRC_UI, CC_SRC_GSM, call_id, (line_t)instance, CC_FEATURE_SETREMOTEDESC, ccData.action, data, &featdata);
+             STAMP_TIMECARD(timecard, "Processing set remote event");
+             cc_setremotedesc (CC_SRC_UI, CC_SRC_GSM, call_id, (line_t)instance, CC_FEATURE_SETREMOTEDESC, ccData.action, data, &featdata, timecard);
              break;
          case CC_FEATURE_SETPEERCONNECTION:
            PR_ASSERT(strlen(data) < PC_HANDLE_SIZE);
            if (strlen(data) >= PC_HANDLE_SIZE)
              return;
 
            sstrncpy(featdata.pc.pc_handle, data, sizeof(featdata.pc.pc_handle));
 
            cc_int_feature2(CC_MSG_SETPEERCONNECTION, CC_SRC_UI, CC_SRC_GSM,
              call_id, (line_t)instance,
-             CC_FEATURE_SETPEERCONNECTION, &featdata);
+             CC_FEATURE_SETPEERCONNECTION, &featdata, NULL);
            break;
          case CC_FEATURE_ADDSTREAM:
            featdata.track.stream_id = ccData.stream_id;
            featdata.track.track_id = ccData.track_id;
            featdata.track.media_type = ccData.media_type;
-           cc_int_feature2(CC_MSG_ADDSTREAM, CC_SRC_UI, CC_SRC_GSM, call_id, (line_t)instance, CC_FEATURE_ADDSTREAM, &featdata);
+           cc_int_feature2(CC_MSG_ADDSTREAM, CC_SRC_UI, CC_SRC_GSM, call_id, (line_t)instance, CC_FEATURE_ADDSTREAM, &featdata, timecard);
            break;
          case CC_FEATURE_REMOVESTREAM:
            featdata.track.stream_id = ccData.stream_id;
            featdata.track.track_id = ccData.track_id;
            featdata.track.media_type = ccData.media_type;
-           cc_int_feature2(CC_MSG_REMOVESTREAM, CC_SRC_UI, CC_SRC_GSM, call_id, (line_t)instance, CC_FEATURE_REMOVESTREAM, &featdata);
+           cc_int_feature2(CC_MSG_REMOVESTREAM, CC_SRC_UI, CC_SRC_GSM, call_id, (line_t)instance, CC_FEATURE_REMOVESTREAM, &featdata, timecard);
            break;
          case CC_FEATURE_ADDICECANDIDATE:
+           STAMP_TIMECARD(timecard, "Processing add candidate event");
            featdata.candidate.level = ccData.level;
            sstrncpy(featdata.candidate.candidate, data, sizeof(featdata.candidate.candidate)-1);
            sstrncpy(featdata.candidate.mid, data1, sizeof(featdata.candidate.mid)-1);
-           cc_int_feature2(CC_MSG_ADDCANDIDATE, CC_SRC_UI, CC_SRC_GSM, call_id, (line_t)instance, CC_FEATURE_ADDICECANDIDATE, &featdata);
+           cc_int_feature2(CC_MSG_ADDCANDIDATE, CC_SRC_UI, CC_SRC_GSM, call_id, (line_t)instance, CC_FEATURE_ADDICECANDIDATE, &featdata, timecard);
            break;
          case CC_FEATURE_DIALSTR:
              if (CheckAndGetAvailableLine(&line_id, &call_id) == TRUE) {
                  getDigits(data, digits, sizeof(digits));
                  if (strlen(digits) == 0) {
                     //if dial string is empty then go offhook
                     cc_offhook(CC_SRC_UI, call_id, line_id);
                  } else {
@@ -1048,16 +1056,21 @@ session_data_t * getDeepCopyOfSessionDat
            newData->orig_called_name =  strlib_copy(data->orig_called_name);
            newData->orig_called_number =  strlib_copy(data->orig_called_number);
            newData->last_redir_name =  strlib_copy(data->last_redir_name);
            newData->last_redir_number =  strlib_copy(data->last_redir_number);
            newData->plcd_name =  strlib_copy(data->plcd_name);
            newData->plcd_number =  strlib_copy(data->plcd_number);
            newData->status =  strlib_copy(data->status);
            newData->sdp = strlib_copy(data->sdp);
+
+           /* The timecard can have only one owner */
+           newData->timecard = data->timecard;
+           data->timecard = NULL;
+
            calllogger_copy_call_log(&newData->call_log, &data->call_log);
        } else {
            newData->ref_count = 1;
            newData->state = ONHOOK;
            newData->security = CC_SECURITY_NONE;
            newData->policy = CC_POLICY_NONE;
            newData->clg_name =  strlib_empty();
            newData->clg_number = strlib_empty();
@@ -1067,16 +1080,17 @@ session_data_t * getDeepCopyOfSessionDat
            newData->orig_called_name = strlib_empty();
            newData->orig_called_number = strlib_empty();
            newData->last_redir_name = strlib_empty();
            newData->last_redir_number = strlib_empty();
            newData->plcd_name =  strlib_empty();
            newData->plcd_number =  strlib_empty();
            newData->status = strlib_empty();
            newData->sdp = strlib_empty();
+           newData->timecard = NULL;
            calllogger_init_call_log(&newData->call_log);
        }
 
    }
    return newData;
 }
 
 
@@ -1113,16 +1127,17 @@ void cleanSessionData(session_data_t *da
         strlib_free(data->plcd_name);
         data->plcd_name = strlib_empty();
         strlib_free(data->plcd_number);
         data->plcd_number = strlib_empty();
         strlib_free(data->status);
         data->status = strlib_empty();
         strlib_free(data->sdp);
         data->sdp = strlib_empty();
+        data->timecard = NULL;
         calllogger_free_call_log(&data->call_log);
     }
 }
 
 /**
  *
  * CCApp Provider check for CONNECTED calls for preservation
  *
@@ -1451,27 +1466,30 @@ static void ccappUpdateSessionData (sess
         data->last_redir_number = strlib_empty();
         data->plcd_name = strlib_empty();
         data->plcd_number = strlib_empty();
         data->status = strlib_empty();
         data->gci[0] = 0;
 	data->vid_dir = SDP_DIRECTION_INACTIVE;
         data->callref = 0;
         data->sdp = strlib_empty();
+        data->timecard = NULL;
         calllogger_init_call_log(&data->call_log);
 
         switch (sessUpd->eventID) {
             case CREATE_OFFER:
             case CREATE_ANSWER:
             case SET_LOCAL_DESC:
             case SET_REMOTE_DESC:
             case UPDATE_LOCAL_DESC:
             case UPDATE_REMOTE_DESC:
             case ICE_CANDIDATE_ADD:
                 data->sdp = sessUpd->update.ccSessionUpd.data.state_data.sdp;
+                data->timecard =
+                    sessUpd->update.ccSessionUpd.data.state_data.timecard;
                 /* Fall through to the next case... */
             case REMOTE_STREAM_ADD:
                 data->cause =
                     sessUpd->update.ccSessionUpd.data.state_data.cause;
                 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;
@@ -1812,16 +1830,17 @@ static void ccappUpdateSessionData (sess
     case SET_LOCAL_DESC:
     case SET_REMOTE_DESC:
     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->timecard = sessUpd->update.ccSessionUpd.data.state_data.timecard;
         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;
--- a/media/webrtc/signaling/src/sipcc/core/common/ui.c
+++ b/media/webrtc/signaling/src/sipcc/core/common/ui.c
@@ -2,16 +2,17 @@
  * 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/. */
 
 /** @file tnp_ui.c
  * API provided by Platform to the Call Control for User Interface activities
  */
 
 #include <stdarg.h>
+#include "timecard.h"
 #include "cpr.h"
 #include "cpr_in.h"
 #include "phone.h"
 #include "time2.h"
 #include "debug.h"
 #include "phone_debug.h"
 #include "dialplan.h"
 #include "ccapi.h"
@@ -1550,38 +1551,42 @@ ui_control_feature (line_t line_id, call
  */
 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,
+                                Timecard *timecard,
                                 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;
     }
 
+    STAMP_TIMECARD(timecard, "Posting message to PC");
+
     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;
+    msg.update.ccSessionUpd.data.state_data.timecard = timecard;
 
     if (format) {
       flex_string_init(&fs);
       flex_string_vsprintf(&fs, format, args);
       msg.update.ccSessionUpd.data.state_data.reason_text =
         strlib_malloc(fs.buffer, -1);
       flex_string_free(&fs);
     } else {
@@ -1604,29 +1609,30 @@ static void post_message_helper(group_ca
  *  @return none
  */
 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,
+                     Timecard *timecard,
                      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, new_state, nLine, nCallID,
-                        call_instance_id, sdp, error, format, ap);
+                        call_instance_id, sdp, timecard, error, format, ap);
     va_end(ap);
 
     return;
 }
 
 /**
  *  Send data from createAnswer to the UI, can send success with SDP string
  *  or can send error
@@ -1634,26 +1640,27 @@ void ui_create_offer(call_events event,
  *  @return none
  */
 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,
+                      Timecard *timecard,
                       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, new_state, nLine, nCallID,
-                        call_instance_id, sdp, error, format, ap);
+                        call_instance_id, sdp, timecard, error, format, ap);
     va_end(ap);
 
     return;
 }
 
 /**
  *  Send data from setLocalDescription to the UI
  *
@@ -1661,26 +1668,27 @@ void ui_create_answer(call_events event,
  */
 
 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,
+                              Timecard *timecard,
                               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, new_state, nLine, nCallID,
-                        call_instance_id, sdp, error, format, ap);
+                        call_instance_id, sdp, timecard, error, format, ap);
     va_end(ap);
 
     return;
 }
 
 /**
  *  Send data from setRemoteDescription to the UI
  *
@@ -1688,26 +1696,27 @@ void ui_set_local_description(call_event
  */
 
 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,
+                               Timecard *timecard,
                                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, new_state, nLine, nCallID,
-                        call_instance_id, sdp, error, format, ap);
+                        call_instance_id, sdp, timecard, error, format, ap);
     va_end(ap);
 
     return;
 }
 
 /**
  *  Let PeerConnection know about an updated local session description
  *
@@ -1715,27 +1724,28 @@ void ui_set_remote_description(call_even
  */
 
 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,
+                                 Timecard *timecard,
                                  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, new_state, nLine, nCallID,
-                        call_instance_id, sdp, error, format, ap);
+                        call_instance_id, sdp, timecard, error, format, ap);
     va_end(ap);
 
     return;
 }
 
 /**
  * Send data from addIceCandidate to the UI
  *
@@ -1743,26 +1753,27 @@ void ui_update_local_description(call_ev
  */
 
 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,
+                          Timecard *timecard,
                           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, new_state, nLine, nCallID,
-                        call_instance_id, sdp, error, format, ap);
+                        call_instance_id, sdp, timecard, error, format, ap);
     va_end(ap);
 }
 
 /**
  *  Send Remote Stream data to the UI
  *
  *  @return none
  */
@@ -1792,15 +1803,16 @@ void ui_on_remote_stream_added(call_even
     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;
+    msg.update.ccSessionUpd.data.state_data.timecard = NULL;
 
     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);
     }
 
     return;
 }
--- a/media/webrtc/signaling/src/sipcc/core/gsm/ccapi.c
+++ b/media/webrtc/signaling/src/sipcc/core/gsm/ccapi.c
@@ -1,12 +1,13 @@
 /* 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/. */
 
+#include "timecard.h"
 #include "cpr_types.h"
 #include "cpr_stdio.h"
 #include "cpr_string.h"
 #include "cpr_memory.h"
 #include "cpr_stdlib.h"
 #include "ccapi.h"
 #include "ccsip_task.h"
 #include "debug.h"
@@ -1105,17 +1106,17 @@ cc_int_release_complete (cc_srcs_t src_i
     }
     return;
 }
 
 
 void
 cc_int_feature2 (cc_msgs_t msg_id, cc_srcs_t src_id, cc_srcs_t dst_id,
                  callid_t call_id, line_t line, cc_features_t feature_id,
-                 cc_feature_data_t *data)
+                 cc_feature_data_t *data, Timecard *timecard)
 {
     cc_feature_t *pmsg;
     cc_msgbody_info_t *msg_body;
 
     pmsg = (cc_feature_t *) cc_get_msg_buf(sizeof(*pmsg));
     if (!pmsg) {
         // nobody checks, CC_RC_ERROR, so generate error message
         GSM_ERR_MSG("%s: no buffer available for feat=%s", __FUNCTION__,
@@ -1124,16 +1125,17 @@ cc_int_feature2 (cc_msgs_t msg_id, cc_sr
     }
 
     pmsg->msg_id     = msg_id;
     pmsg->src_id     = src_id;
     pmsg->call_id    = call_id;
     pmsg->line       = line;
     pmsg->feature_id = feature_id;
     pmsg->data_valid = (data == NULL) ? (FALSE) : (TRUE);
+    pmsg->timecard   = timecard;
 
     if (pmsg->data_valid == TRUE) {
         pmsg->data = *data;
         /*
          * For call Info feature, need to copy the caller ID
          */
         if (feature_id == CC_FEATURE_CALLINFO) {
             /* Copy the caller ID */
@@ -1178,17 +1180,18 @@ static void send_message_helper(
     cc_msgs_t msg_id,
     cc_srcs_t src_id,
     cc_srcs_t dst_id,
     callid_t call_id,
     line_t line,
     cc_features_t feature_id,
     cc_feature_data_t *data,
     string_t sdp,
-    cc_jsep_action_t action)
+    cc_jsep_action_t action,
+    Timecard *timecard)
 {
     cc_feature_t *pmsg;
     cc_msgbody_info_t *msg_body;
 
     pmsg = (cc_feature_t *) cc_get_msg_buf(sizeof(*pmsg));
     if (!pmsg) {
         GSM_ERR_MSG("%s: no buffer available for feat=%s", __FUNCTION__,
                     cc_feature_name(feature_id));
@@ -1196,16 +1199,17 @@ static void send_message_helper(
     }
 
     pmsg->msg_id     = msg_id;
     pmsg->src_id     = src_id;
     pmsg->call_id    = call_id;
     pmsg->line       = line;
     pmsg->feature_id = feature_id;
     pmsg->data_valid = (data == NULL) ? (FALSE) : (TRUE);
+    pmsg->timecard   = timecard;
 
     if (msg_id == CC_MSG_SETLOCALDESC || msg_id == CC_MSG_SETREMOTEDESC) {
         pmsg->action = action;
     }
 
     if (msg_id == CC_MSG_CREATEANSWER || msg_id == CC_MSG_SETLOCALDESC || msg_id == CC_MSG_SETREMOTEDESC) {
         sstrncpy(pmsg->sdp, sdp, sizeof(pmsg->sdp));
     }
@@ -1226,61 +1230,66 @@ static void send_message_helper(
          */
         msg_body = cc_get_msg_body_info_ptr_from_feature_data(feature_id, data);
         cc_initialize_msg_body_parts_info(msg_body);
     }
 
     CC_DEBUG_ENTRY(__FUNCTION__, src_id, dst_id, call_id, line, cc_feature_name(feature_id));
     CC_DEBUG(DEB_L_C_F_PREFIX "feature= %s, data= %p",
         DEB_L_C_F_PREFIX_ARGS(CC_API, line, call_id, __FUNCTION__), cc_feature_name(feature_id), data);
+    STAMP_TIMECARD(timecard, "Sending message to queue");
     if (cc_send_msg((cprBuffer_t) pmsg, sizeof(*pmsg), dst_id) != CC_RC_SUCCESS) {
         // nobody checks the return code, so generate error message
         GSM_ERR_MSG("%s: unable to send msg for feat=%s", __FUNCTION__,
                     cc_feature_name(feature_id));
     }
 
     return;
 }
 
 void
 cc_createoffer (cc_srcs_t src_id, cc_srcs_t dst_id, callid_t call_id,
-                line_t line, cc_features_t feature_id, cc_feature_data_t *data)
+                line_t line, cc_features_t feature_id, cc_feature_data_t *data,
+                Timecard *tc)
 {
     send_message_helper(CC_MSG_CREATEOFFER, src_id, dst_id, call_id, line,
-        feature_id, data, NULL, 0);
+        feature_id, data, NULL, 0, tc);
 
     return;
 }
 
 
 void
 cc_createanswer (cc_srcs_t src_id, cc_srcs_t dst_id, callid_t call_id,
-                line_t line, cc_features_t feature_id, string_t sdp, cc_feature_data_t *data)
+                line_t line, cc_features_t feature_id, string_t sdp, cc_feature_data_t *data,
+                Timecard *tc)
 {
     send_message_helper(CC_MSG_CREATEANSWER, src_id, dst_id, call_id, line,
-        feature_id, data, sdp, 0);
+        feature_id, data, sdp, 0, tc);
 
     return;
 }
 
 
 void cc_setlocaldesc (cc_srcs_t src_id, cc_srcs_t dst_id, callid_t call_id, line_t line,
-                    cc_features_t feature_id, cc_jsep_action_t action, string_t sdp,  cc_feature_data_t *data)
+                    cc_features_t feature_id, cc_jsep_action_t action, string_t sdp,  cc_feature_data_t *data,
+                    Timecard *tc)
 {
     send_message_helper(CC_MSG_SETLOCALDESC, src_id, dst_id, call_id, line,
-        feature_id, data, sdp, action);
+        feature_id, data, sdp, action, tc);
 
     return;
 }
 
 void cc_setremotedesc (cc_srcs_t src_id, cc_srcs_t dst_id, callid_t call_id, line_t line,
-                    cc_features_t feature_id, cc_jsep_action_t action, string_t sdp, cc_feature_data_t *data)
+                    cc_features_t feature_id, cc_jsep_action_t action, string_t sdp, cc_feature_data_t *data,
+                    Timecard *tc)
 {
     send_message_helper(CC_MSG_SETREMOTEDESC, src_id, dst_id, call_id, line,
-        feature_id, data, sdp, action);
+        feature_id, data, sdp, action, tc);
 
     return;
 }
 
 void
 cc_int_feature_ack (cc_srcs_t src_id, cc_srcs_t dst_id, callid_t call_id,
                     line_t line, cc_features_t feature_id,
                     cc_feature_data_t *data, cc_causes_t cause)
--- a/media/webrtc/signaling/src/sipcc/core/gsm/fsmdef.c
+++ b/media/webrtc/signaling/src/sipcc/core/gsm/fsmdef.c
@@ -2413,45 +2413,49 @@ fsmdef_ev_default (sm_event_t *event)
      * 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, fcb->state, msg->line,
               msg->call_id, dcb->caller_id.call_instance_id, strlib_empty(),
+              msg->timecard,
               PC_INVALID_STATE, "Cannot create offer in state %s",
               fsmdef_state_name(fcb->state));
         break;
 
       case CC_MSG_CREATEANSWER:
           ui_create_answer(evCreateAnswerError, fcb->state, msg->line,
               msg->call_id, dcb->caller_id.call_instance_id, strlib_empty(),
+              msg->timecard,
               PC_INVALID_STATE, "Cannot create answer in state %s",
               fsmdef_state_name(fcb->state));
         break;
 
       case CC_MSG_SETLOCALDESC:
           ui_set_local_description(evSetLocalDescError, fcb->state, msg->line,
               msg->call_id, dcb->caller_id.call_instance_id, strlib_empty(),
+              msg->timecard,
               PC_INVALID_STATE, "Cannot set local description in state %s",
               fsmdef_state_name(fcb->state));
         break;
 
       case CC_MSG_SETREMOTEDESC:
           ui_set_remote_description(evSetRemoteDescError, fcb->state,
               msg->line, msg->call_id, dcb->caller_id.call_instance_id,
-              strlib_empty(), PC_INVALID_STATE,
+              strlib_empty(), msg->timecard, 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, fcb->state, msg->line,
               msg->call_id, dcb->caller_id.call_instance_id, strlib_empty(),
+              msg->timecard,
               PC_INVALID_STATE, "Cannot add ICE candidate in state %s",
               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
@@ -3148,24 +3152,25 @@ fsmdef_ev_createoffer (sm_event_t *event
     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, fcb->state, line, call_id,
                 dcb->caller_id.call_instance_id, strlib_empty(),
+                msg->timecard,
                 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, fcb->state, line, call_id,
-            dcb->caller_id.call_instance_id,
-            strlib_malloc(local_sdp,-1), PC_NO_ERROR, NULL);
+            dcb->caller_id.call_instance_id, strlib_malloc(local_sdp,-1),
+            msg->timecard, PC_NO_ERROR, NULL);
         free(local_sdp);
         return (SM_RC_END);
     }
 
     dcb->inbound = FALSE;
 
     if (msg->data.session.constraints) {
        gsmsdp_process_cap_constraints(dcb, msg->data.session.constraints);
@@ -3177,26 +3182,28 @@ fsmdef_ev_createoffer (sm_event_t *event
         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, fcb->state, line, call_id,
           dcb->caller_id.call_instance_id, strlib_empty(),
+          msg->timecard,
           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, fcb->state, line, call_id,
           dcb->caller_id.call_instance_id, strlib_empty(),
+          msg->timecard,
           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;
 
@@ -3219,38 +3226,41 @@ fsmdef_ev_createoffer (sm_event_t *event
     	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, fcb->state, line, call_id,
             dcb->caller_id.call_instance_id, strlib_empty(),
+            msg->timecard,
             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, fcb->state, line, call_id,
             dcb->caller_id.call_instance_id, strlib_empty(),
+            msg->timecard,
             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, fcb->state, line, call_id,
         dcb->caller_id.call_instance_id,
-        strlib_malloc(msg_body.parts[0].body, -1), PC_NO_ERROR, NULL);
+        strlib_malloc(msg_body.parts[0].body, -1),
+        msg->timecard, PC_NO_ERROR, NULL);
     cc_free_msg_body_parts(&msg_body);
 
     return (SM_RC_END);
 }
 
 
 /**
  * fsmdef_ev_createanswer
@@ -3303,24 +3313,25 @@ fsmdef_ev_createanswer (sm_event_t *even
     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, fcb->state, line, call_id,
                 dcb->caller_id.call_instance_id, strlib_empty(),
+                msg->timecard,
                 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, fcb->state, line, call_id,
-            dcb->caller_id.call_instance_id,
-            strlib_malloc(local_sdp,-1), PC_NO_ERROR, NULL);
+            dcb->caller_id.call_instance_id, strlib_malloc(local_sdp,-1),
+            msg->timecard, PC_NO_ERROR, NULL);
         free(local_sdp);
         return (SM_RC_END);
     }
 
     dcb->inbound = TRUE;
 
     if (msg->data.session.constraints) {
        gsmsdp_process_cap_constraints(dcb, msg->data.session.constraints);
@@ -3329,16 +3340,17 @@ fsmdef_ev_createanswer (sm_event_t *even
     }
 
     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, fcb->state, line, call_id,
           dcb->caller_id.call_instance_id, strlib_empty(),
+          msg->timecard,
           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;
 
@@ -3371,16 +3383,17 @@ fsmdef_ev_createanswer (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_create_answer(evCreateAnswerError, fcb->state, line, call_id,
             dcb->caller_id.call_instance_id, strlib_empty(),
+            msg->timecard,
             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));
     }
 
     /* TODO(ekr@rtfm.com): The second true is because we are acting as if we are
@@ -3390,37 +3403,40 @@ fsmdef_ev_createanswer (sm_event_t *even
             /* initial_offer */       TRUE,
             /* offer */               TRUE,
             /* notify_stream_added */ FALSE,
             /* create_answer */       TRUE);
 
     if (cause != CC_CAUSE_OK) {
         ui_create_answer(evCreateAnswerError, fcb->state, line, call_id,
             dcb->caller_id.call_instance_id, strlib_empty(),
+            msg->timecard,
             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, fcb->state, line, call_id,
             dcb->caller_id.call_instance_id, strlib_empty(),
+            msg->timecard,
             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, fcb->state, line, call_id,
         dcb->caller_id.call_instance_id,
-        strlib_malloc(msg_body.parts[0].body, -1), PC_NO_ERROR, NULL);
+        strlib_malloc(msg_body.parts[0].body, -1),
+        msg->timecard, PC_NO_ERROR, NULL);
     cc_free_msg_body_parts(&msg_body);
 
     return (SM_RC_END);
 }
 
 
 /**
  * SetLocalDescription
@@ -3447,58 +3463,63 @@ fsmdef_ev_setlocaldesc(sm_event_t *event
     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__));
         fsm_change_state(fcb, __LINE__, FSMDEF_S_CLOSED);
         ui_set_local_description(evSetLocalDescError, fcb->state, line, call_id,
             0, strlib_empty(),
+            msg->timecard,
             PC_INTERNAL_ERROR, "Unrecoverable error: dcb is NULL.");
         return (SM_RC_CLEANUP);
     }
 
     config_get_value(CFGID_SDPMODE, &sdpmode, sizeof(sdpmode));
     if (!sdpmode) {
         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(),
+            msg->timecard,
             PC_INTERNAL_ERROR, "'sdpmode' configuration is false. This should "
             "never ever happen. Run for your lives!");
         return (SM_RC_END);
     }
 
     if (!dcb->sdp) {
         ui_set_local_description(evSetLocalDescError, fcb->state, line, call_id,
            dcb->caller_id.call_instance_id, strlib_empty(),
+           msg->timecard,
            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 (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(),
+                msg->timecard,
                 PC_INVALID_STATE, "Cannot set local offer in state %s",
                 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 (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(),
+                call_id, dcb->caller_id.call_instance_id, strlib_empty(),
+                msg->timecard,
                 PC_INVALID_STATE, "Cannot set local answer in state %s",
                 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,
@@ -3506,78 +3527,86 @@ fsmdef_ev_setlocaldesc(sm_event_t *event
 
         /*
          * 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, fcb->state, line,
-            call_id, dcb->caller_id.call_instance_id, strlib_empty(),
+                call_id, dcb->caller_id.call_instance_id, strlib_empty(),
+                msg->timecard,
                 PC_INTERNAL_ERROR, "Could not configure local ICE state"
                 " from SDP; cause = %s", cc_cause_name(cause));
             return (SM_RC_END);
         }
 
+        STAMP_TIMECARD(msg->timecard, "ICE Attributes Installed");
+
         /* 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, fcb->state, line,
                 call_id, dcb->caller_id.call_instance_id, strlib_empty(),
+                msg->timecard,
                 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 (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(),
+                msg->timecard,
                 PC_INVALID_STATE, "Cannot set local pranswer in state %s",
                 fsmdef_state_name(fcb->state));
             return (SM_RC_END);
         }
         ui_set_local_description(evSetLocalDescError, fcb->state, msg->line,
             msg->call_id, dcb->caller_id.call_instance_id, strlib_empty(),
+            msg->timecard,
             PC_INTERNAL_ERROR, "Provisional answers are not yet supported");
         return (SM_RC_END);
 
     default:
         ui_set_local_description(evSetLocalDescError, fcb->state, msg->line,
             msg->call_id, dcb->caller_id.call_instance_id, strlib_empty(),
+            msg->timecard,
             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, fcb->state, msg->line,
             msg->call_id, dcb->caller_id.call_instance_id, strlib_empty(),
+            msg->timecard,
             PC_INTERNAL_ERROR, "Could not encode local SDP for local "
             "description");
         return (SM_RC_END);
     }
 
     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);
+        strlib_malloc(local_sdp,-1), msg->timecard, PC_NO_ERROR, NULL);
 
     free(local_sdp);
     return (SM_RC_END);
 }
 
 /**
  * SetRemoteDescription
  *
@@ -3608,38 +3637,41 @@ fsmdef_ev_setremotedesc(sm_event_t *even
     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__));
         fsm_change_state(fcb, __LINE__, FSMDEF_S_CLOSED);
         ui_set_remote_description(evSetRemoteDescError, fcb->state, line,
             call_id, 0, strlib_empty(),
+            msg->timecard,
             PC_INTERNAL_ERROR, "Unrecoverable error: dcb is NULL.");
         return (SM_RC_CLEANUP);
     }
 
     config_get_value(CFGID_SDPMODE, &sdpmode, sizeof(sdpmode));
     if (!sdpmode) {
         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(),
+            msg->timecard,
             PC_INTERNAL_ERROR, "'sdpmode' configuration is false. This should "
             "never ever happen. Run for your lives!");
         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, fcb->state, line,
             call_id, dcb->caller_id.call_instance_id, strlib_empty(),
+            msg->timecard,
             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
@@ -3660,25 +3692,27 @@ fsmdef_ev_setremotedesc(sm_event_t *even
     part->content_disposition.disposition = cc_disposition_session;
     part->content_id = NULL;
 
     switch (action) {
     case JSEP_OFFER:
         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(),
+                call_id, dcb->caller_id.call_instance_id, strlib_empty(),
+                msg->timecard,
                 PC_INVALID_STATE, "Cannot set remote offer in state %s",
                 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, fcb->state, line,
-            call_id, dcb->caller_id.call_instance_id, strlib_empty(),
+                call_id, dcb->caller_id.call_instance_id, strlib_empty(),
+                msg->timecard,
                 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.
@@ -3689,132 +3723,144 @@ 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, 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));
+                  call_id, dcb->caller_id.call_instance_id, strlib_empty(),
+                  msg->timecard,
+                  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, fcb->state, line,
-            call_id, dcb->caller_id.call_instance_id, strlib_empty(),
+              call_id, dcb->caller_id.call_instance_id, strlib_empty(),
+              msg->timecard,
               PC_INTERNAL_ERROR, "Could not negotiate media lines; cause = %s",
               cc_cause_name(cause));
             return (fsmdef_release(fcb, cause, FALSE));
         }
 
         /* Now that the SDP is digested we need to sanity check
            for ICE parameters */
         cause = gsmsdp_check_ice_attributes_exist(fcb);
         if (cause != CC_CAUSE_OK) {
             ui_set_remote_description(evSetRemoteDescError, fcb->state, line,
-            call_id, dcb->caller_id.call_instance_id, strlib_empty(),
+              call_id, dcb->caller_id.call_instance_id, strlib_empty(),
+              msg->timecard,
               PC_INTERNAL_ERROR, "ICE attributes missing; cause = %s",
               cc_cause_name(cause));
             return (SM_RC_END);
         }
 
         gsmsdp_clean_media_list(dcb);
 
         fsm_change_state(fcb, __LINE__, FSMDEF_S_HAVE_REMOTE_OFFER);
         break;
 
     case JSEP_ANSWER:
         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(),
+                call_id, dcb->caller_id.call_instance_id, strlib_empty(),
+                msg->timecard,
                 PC_INVALID_STATE, "Cannot set remote answer in state %s",
                 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, fcb->state, line,
                 call_id, dcb->caller_id.call_instance_id, strlib_empty(),
+                msg->timecard,
                 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, fcb->state, line,
                 call_id, dcb->caller_id.call_instance_id, strlib_empty(),
+                msg->timecard,
                 PC_INTERNAL_ERROR, "Could not configure local ICE state"
                 " from SDP; cause = %s", cc_cause_name(cause));
             return (SM_RC_END);
         }
 
+        STAMP_TIMECARD(msg->timecard, "ICE Attributes Installed");
+
         cc_call_state(dcb->call_id, dcb->line, CC_STATE_CONNECTED,
             FSMDEF_CC_CALLER_ID);
 
         /* 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 (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(),
+                msg->timecard,
                 PC_INVALID_STATE, "Cannot set remote pranswer in state %s",
                 fsmdef_state_name(fcb->state));
             return (SM_RC_END);
         }
-        ui_set_local_description(evSetLocalDescError, fcb->state, msg->line,
+        ui_set_remote_description(evSetRemoteDescError, fcb->state, msg->line,
             msg->call_id, dcb->caller_id.call_instance_id, strlib_empty(),
+            msg->timecard,
             PC_INTERNAL_ERROR, "Provisional answers are not yet supported");
         return (SM_RC_END);
 
     default:
-        ui_set_local_description(evSetLocalDescError, fcb->state, msg->line,
+        ui_set_remote_description(evSetRemoteDescError, fcb->state, msg->line,
             msg->call_id, dcb->caller_id.call_instance_id, strlib_empty(),
+            msg->timecard,
             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, fcb->state, line,
-        call_id, dcb->caller_id.call_instance_id, strlib_empty(),
+            call_id, dcb->caller_id.call_instance_id, strlib_empty(),
+            msg->timecard,
             PC_INTERNAL_ERROR, "Could not serialize remote description;"
             " cause = %s",  cc_cause_name(cause));
         return (SM_RC_END);
     }
 
     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);
+        msg->timecard, PC_NO_ERROR, NULL);
 
     free(remote_sdp);
 
     return (SM_RC_END);
 }
 
 
 static sm_rcs_t
@@ -3987,36 +4033,39 @@ fsmdef_ev_addcandidate(sm_event_t *event
     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, fcb->state, line, call_id,
-            0, strlib_empty(), PC_INTERNAL_ERROR, "DCB has not been created.");
+            0, strlib_empty(), msg->timecard, 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, fcb->state, line, call_id,
             dcb->caller_id.call_instance_id, strlib_empty(),
+            msg->timecard,
             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, fcb->state, line, call_id,
             dcb->caller_id.call_instance_id, strlib_empty(),
+            msg->timecard,
             PC_INVALID_STATE, "Cannot add remote ICE candidates before "
                               "setting remote SDP.");
 
         return SM_RC_END;
     }
 
     /* Perform level lookup based on mid value */
     /* comment until mid is properly updated
@@ -4054,24 +4103,25 @@ 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, fcb->state, line, call_id,
             dcb->caller_id.call_instance_id, strlib_empty(),
+            msg->timecard,
             PC_INTERNAL_ERROR, "Could not serialize new SDP after adding ICE "
             "candidate.");
         return (SM_RC_END);
     }
 
     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);
+        msg->timecard, PC_NO_ERROR, NULL);
 
     free(remote_sdp);
     return (SM_RC_END);
 }
 
 static void
 fsmdef_check_active_feature (fsmdef_dcb_t *dcb, cc_features_t ftr_id)
 {
--- a/media/webrtc/signaling/src/sipcc/core/includes/ccapi.h
+++ b/media/webrtc/signaling/src/sipcc/core/includes/ccapi.h
@@ -3,16 +3,22 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef _CCAPI_H_
 #define _CCAPI_H_
 
 #include "mozilla/Assertions.h"
 #include "mozilla/Util.h"
 
+#if defined(__cplusplus) && __cplusplus >= 201103L
+typedef struct Timecard Timecard;
+#else
+#include "timecard.h"
+#endif
+
 #include "cpr_types.h"
 #include "cpr_memory.h"
 #include "phone_types.h"
 #include "string_lib.h"
 #include "vcm.h"
 #include "sdp.h"
 #include "cc_constants.h"
 
@@ -950,16 +956,17 @@ typedef struct cc_feature_t_ {
     cc_srcs_t            src_id;
     callid_t             call_id;
     line_t               line;
     cc_features_t        feature_id;
     cc_feature_data_t    data;
     boolean              data_valid;
     cc_jsep_action_t     action;
     char                 sdp[SDP_SIZE];
+    Timecard            *timecard;
 } cc_feature_t;
 
 typedef struct cc_feature_ack_t_ {
     cc_msgs_t         msg_id;
     cc_srcs_t         src_id;
     callid_t          call_id;
     line_t            line;
     cc_features_t     feature_id;
@@ -1169,29 +1176,33 @@ void cc_int_release(cc_srcs_t src_id, cc
                     cc_kfact_t *kfactor);
 
 void cc_int_release_complete(cc_srcs_t src_id, cc_srcs_t dst_id,
                              callid_t call_id, line_t line, cc_causes_t cause,
                              cc_kfact_t *kfactor);
 
 void cc_int_feature2(cc_msgs_t msg_id, cc_srcs_t src_id, cc_srcs_t dst_id,
                     callid_t call_id, line_t line, cc_features_t feature_id,
-                    cc_feature_data_t *data);
+                    cc_feature_data_t *data, Timecard *);
 
 void cc_createoffer(cc_srcs_t src_id, cc_srcs_t dst_id, callid_t call_id,
-                    line_t line, cc_features_t feature_id, cc_feature_data_t *data);
+                    line_t line, cc_features_t feature_id, cc_feature_data_t *data,
+                    Timecard *);
 
 void cc_createanswer (cc_srcs_t src_id, cc_srcs_t dst_id, callid_t call_id,
-                    line_t line, cc_features_t feature_id, string_t sdp, cc_feature_data_t *data);
+                    line_t line, cc_features_t feature_id, string_t sdp, cc_feature_data_t *data,
+                    Timecard *);
 
 void cc_setlocaldesc (cc_srcs_t src_id, cc_srcs_t dst_id, callid_t call_id, line_t line,
-                    cc_features_t feature_id, cc_jsep_action_t action, string_t sdp, cc_feature_data_t *data);
+                    cc_features_t feature_id, cc_jsep_action_t action, string_t sdp, cc_feature_data_t *data,
+                    Timecard *);
 
 void cc_setremotedesc (cc_srcs_t src_id, cc_srcs_t dst_id, callid_t call_id, line_t line,
-                    cc_features_t feature_id, cc_jsep_action_t action, string_t sdp, cc_feature_data_t *data);
+                    cc_features_t feature_id, cc_jsep_action_t action, string_t sdp, cc_feature_data_t *data,
+                    Timecard *);
 
 void cc_int_feature_ack(cc_srcs_t src_id, cc_srcs_t dst_id, callid_t call_id,
                         line_t line, cc_features_t feature_id,
                         cc_feature_data_t *data, cc_causes_t cause);
 
 void cc_int_offhook(cc_srcs_t src_id, cc_srcs_t dst_id, callid_t prim_call_id,
                     cc_hold_resume_reason_e consult_reason, callid_t call_id,
                     line_t line, char *global_call_id,
@@ -1250,18 +1261,18 @@ void cc_int_fail_fallback(cc_srcs_t src_
         cc_int_alerting(a, CC_SRC_GSM, b, c, d, e, f)
 #define cc_connected(a, b, c, d, e, f) \
         cc_int_connected(a, CC_SRC_GSM, b, c, d, e, f)
 #define cc_connected_ack(a, b, c, d, e) \
         cc_int_connected_ack(a, CC_SRC_GSM, b, c, d, e)
 #define cc_release(a, b, c, d, e, f)     cc_int_release(a, CC_SRC_GSM, b, c, d, e, f)
 #define cc_release_complete(a, b, c, d, e) \
         cc_int_release_complete(a, CC_SRC_GSM, b, c, d, e)
-#define cc_feature(a, b, c, d, e)     cc_int_feature2(CC_MSG_FEATURE, a, CC_SRC_GSM, b, c, d, e)
-#define cc_int_feature(a, b, c, d, e, f)     cc_int_feature2(CC_MSG_FEATURE, a, b, c, d, e, f)
+#define cc_feature(a, b, c, d, e)     cc_int_feature2(CC_MSG_FEATURE, a, CC_SRC_GSM, b, c, d, e, NULL)
+#define cc_int_feature(a, b, c, d, e, f)     cc_int_feature2(CC_MSG_FEATURE, a, b, c, d, e, f, NULL)
 #define cc_feature_ack(a, b, c, d, e, f) \
         cc_int_feature_ack(a, CC_SRC_GSM, b, c, d, e, f)
 #define cc_offhook(a, b, c)           cc_int_offhook(a, CC_SRC_GSM, CC_NO_CALL_ID, CC_REASON_NONE, b, c, NULL, CC_MONITOR_NONE,CFWDALL_NONE)
 #define cc_offhook_ext(a, b, c, d, e) cc_int_offhook(a, CC_SRC_GSM, CC_NO_CALL_ID, CC_REASON_NONE, b, c, d, e,CFWDALL_NONE)
 #define cc_onhook(a, b, c, d)         cc_int_onhook(a, CC_SRC_GSM, CC_NO_CALL_ID, CC_REASON_NONE, b, c, d, CC_REASON_NULL,__FILE__,__LINE__)
 #define cc_onhook_ext(a, b, c, d, e)  cc_int_onhook(a, CC_SRC_GSM, CC_NO_CALL_ID, CC_REASON_NONE, b, c, d, e,__FILE__,__LINE__)
 #define cc_line(a, b, c)              cc_int_line(a, CC_SRC_GSM, b, c)
 #define cc_digit_begin(a, b, c, d)    cc_int_digit_begin(a, CC_SRC_GSM, b, c, d)
--- a/media/webrtc/signaling/src/sipcc/core/includes/sessionTypes.h
+++ b/media/webrtc/signaling/src/sipcc/core/includes/sessionTypes.h
@@ -1,15 +1,21 @@
 /* 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 _SESSIONTYPES_H_
 #define _SESSIONTYPES_H_
 
+#if defined(__cplusplus) && __cplusplus >= 201103L
+typedef struct Timecard Timecard;
+#else
+#include "timecard.h"
+#endif
+
 #include "string_lib.h"
 #include "sessionConstants.h"
 #include "ccsip_pmh.h"
 #include "cc_constants.h"
 #include "sip_ccm_transport.h"
 #include "plat_api.h"
 
 /*********************** SESSION ID *****************/
@@ -43,29 +49,31 @@ typedef struct {
   string_t                  info1;
   unsigned int              state;
   cc_jsep_action_t          action;
   cc_media_stream_id_t      stream_id;
   cc_media_track_id_t       track_id;
   cc_media_type_t           media_type;
   cc_level_t                level;
   cc_media_constraints_t *  constraints;
+  Timecard *                timecard;
 } 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;
+  Timecard *   timecard;
 } cc_call_state_data_t;
 /* CALL_SESSION_CREATED shall use the call_state as data*/
 
 typedef struct
 {
   string_t      cldNum;
   string_t      cldName;
 } cc_placed_call_info_t;
--- a/media/webrtc/signaling/src/sipcc/core/includes/uiapi.h
+++ b/media/webrtc/signaling/src/sipcc/core/includes/uiapi.h
@@ -181,61 +181,67 @@ void ui_update_media_interface_change(li
 /* 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,
+                     Timecard *timecard,
                      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,
+                      Timecard *timecard,
                       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,
+                              Timecard *timecard,
                               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,
+                               Timecard *timecard,
                                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,
+                                 Timecard *timecard,
                                  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,
+                          Timecard *timecard,
                           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,
--- a/media/webrtc/signaling/src/sipcc/include/cc_call_feature.h
+++ b/media/webrtc/signaling/src/sipcc/include/cc_call_feature.h
@@ -82,16 +82,22 @@
  *    @li plat_api.h  @par
  *      APIs that must be implemented by the platform for platform specific functionality
  *
  */
 
 #ifndef _CC_CALL_FEATURE_H_
 #define _CC_CALL_FEATURE_H_
 
+#if defined(__cplusplus) && __cplusplus >= 201103L
+typedef struct Timecard Timecard;
+#else
+#include "timecard.h"
+#endif
+
 #include "cc_constants.h"
 
 /***********************************Basic Call Feature Control Methods************************************
  * This section defines all the call related methods that an upper layer can use to control
  * a call in progress.
  */
 
 /**
@@ -147,32 +153,44 @@ cc_return_t CC_CallFeature_backSpace(cc_
  * @param call_handle call handle
  * @param video_pref the sdp direction
  * @param numbers dialed string
  * @return SUCCESS or FAILURE
  */
 cc_return_t CC_CallFeature_dial(cc_call_handle_t call_handle, cc_sdp_direction_t video_pref, const cc_string_t numbers);
 
 cc_return_t CC_CallFeature_CreateOffer(cc_call_handle_t call_handle,
-                                       cc_media_constraints_t *constraints);
+                                       cc_media_constraints_t *constraints,
+                                       Timecard *tc);
 
 cc_return_t CC_CallFeature_CreateAnswer(cc_call_handle_t call_handle,
-                                        cc_media_constraints_t *constraints);
+                                        cc_media_constraints_t *constraints,
+                                        Timecard *tc);
 
-cc_return_t CC_CallFeature_SetLocalDescription(cc_call_handle_t call_handle, cc_jsep_action_t action, const char* sdp);
+cc_return_t CC_CallFeature_SetLocalDescription(cc_call_handle_t call_handle,
+                                               cc_jsep_action_t action,
+                                               const char* sdp,
+                                               Timecard *tc);
 
-cc_return_t CC_CallFeature_SetRemoteDescription(cc_call_handle_t call_handle, cc_jsep_action_t action, const char* sdp);
+cc_return_t CC_CallFeature_SetRemoteDescription(cc_call_handle_t call_handle,
+                                                cc_jsep_action_t action,
+                                                const char* sdp,
+                                                Timecard *tc);
 
 cc_return_t CC_CallFeature_SetPeerConnection(cc_call_handle_t call_handle, cc_peerconnection_t pc);
 
 cc_return_t CC_CallFeature_AddStream(cc_call_handle_t call_handle, cc_media_stream_id_t stream_id, cc_media_track_id_t id, cc_media_type_t media_type);
 
 cc_return_t CC_CallFeature_RemoveStream(cc_call_handle_t call_handle, cc_media_stream_id_t stream_id, cc_media_track_id_t id, cc_media_type_t media_type);
 
-cc_return_t CC_CallFeature_AddICECandidate(cc_call_handle_t call_handle, const char* candidate, const char *mid, cc_level_t level);
+cc_return_t CC_CallFeature_AddICECandidate(cc_call_handle_t call_handle,
+                                           const char* candidate,
+                                           const char *mid,
+                                           cc_level_t level,
+                                           Timecard *tc);
 
 /**
  * Initiate a speed dial.
  * @param call_handle call handle
  * @param speed_dial_number speed dial number to be dialed.
  * @param video_pref the sdp direction
  * @return SUCCESS or FAILURE
  */
--- a/media/webrtc/signaling/src/sipcc/include/ccapi_call.h
+++ b/media/webrtc/signaling/src/sipcc/include/ccapi_call.h
@@ -1,15 +1,21 @@
 /* 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 _CCAPI_CALL_H_
 #define _CCAPI_CALL_H_
 
+#if defined(__cplusplus) && __cplusplus >= 201103L
+typedef struct Timecard Timecard;
+#else
+#include "timecard.h"
+#endif
+
 #include "ccapi_types.h"
 
 /**
  * Get reference to call info
  * @param [in] handle - call handle
  * @return cc_call_info_snap_t
  * NOTE: The info returned by this method must be released using CCAPI_Call_releaseCallInfo()
  */
@@ -44,32 +50,44 @@ cc_lineid_t CCAPI_Call_getLine(cc_call_h
  * @param [in] video_pref - video direction desired on call
  * @param [in] digits - digits to be dialed. can be empty then this API simply goes offhook
  * @return SUCCESS or FAILURE
  */
 cc_return_t CCAPI_Call_originateCall(cc_call_handle_t handle, cc_sdp_direction_t video_pref, cc_string_t digits);
 
 
 cc_return_t CCAPI_CreateOffer(cc_call_handle_t handle,
-                              cc_media_constraints_t *constraints);
+                              cc_media_constraints_t *constraints,
+                              Timecard *tc);
 
 cc_return_t CCAPI_CreateAnswer(cc_call_handle_t handle,
-                               cc_media_constraints_t *constraints);
+                               cc_media_constraints_t *constraints,
+                               Timecard *tc);
 
-cc_return_t CCAPI_SetLocalDescription(cc_call_handle_t handle, cc_jsep_action_t action, cc_string_t sdp);
+cc_return_t CCAPI_SetLocalDescription(cc_call_handle_t handle,
+                                      cc_jsep_action_t action,
+                                      cc_string_t sdp,
+                                      Timecard *tc);
 
-cc_return_t CCAPI_SetRemoteDescription(cc_call_handle_t handle, cc_jsep_action_t action, cc_string_t sdp);
+cc_return_t CCAPI_SetRemoteDescription(cc_call_handle_t handle,
+                                       cc_jsep_action_t action,
+                                       cc_string_t sdp,
+                                       Timecard *tc);
 
 cc_return_t CCAPI_SetPeerConnection(cc_call_handle_t handle, cc_peerconnection_t pc);
 
 cc_return_t CCAPI_AddStream(cc_call_handle_t handle, cc_media_stream_id_t stream_id, cc_media_track_id_t track_id, cc_media_type_t media_type);
 
 cc_return_t CCAPI_RemoveStream(cc_call_handle_t handle, cc_media_stream_id_t stream_id, cc_media_track_id_t track_id, cc_media_type_t media_type);
 
-cc_return_t CCAPI_AddICECandidate(cc_call_handle_t handle, cc_string_t candidate, cc_string_t mid, cc_level_t level);
+cc_return_t CCAPI_AddICECandidate(cc_call_handle_t handle,
+                                  cc_string_t candidate,
+                                  cc_string_t mid,
+                                  cc_level_t level,
+                                  Timecard *tc);
 
 /**
  * Send digits on the call - can be invoked either to dial additional digits or send DTMF
  * @param [in] handle - call handle
  * @param [in] digit - digit to be dialed
  * @return SUCCESS or FAILURE
  */
 cc_return_t CCAPI_Call_sendDigit(cc_call_handle_t handle, cc_digit_t digit);
--- a/media/webrtc/signaling/src/sipcc/include/ccapi_call_info.h
+++ b/media/webrtc/signaling/src/sipcc/include/ccapi_call_info.h
@@ -1,15 +1,21 @@
 /* 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 _CCAPI_CALL_INFO_H_
 #define _CCAPI_CALL_INFO_H_
 
+#if defined(__cplusplus) && __cplusplus >= 201103L
+typedef struct Timecard Timecard;
+#else
+#include "timecard.h"
+#endif
+
 #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
@@ -305,9 +311,16 @@ cc_int32_t  CCAPI_CallInfo_getStatusCode
 
 /**
  * get media stream table
  * @param [in] handle - call info handle
  * @return media track table
  */
 MediaStreamTable* CCAPI_CallInfo_getMediaStreams(cc_callinfo_ref_t handle);
 
+/**
+ * Take posession of timecard
+ * @param [in] handle - call info handle
+ * @return timecard pointer
+ */
+Timecard* CCAPI_CallInfo_takeTimecard(cc_callinfo_ref_t handle);
+
 #endif /* _CCAPIAPI_CALL_INFO_H_ */
--- a/media/webrtc/signaling/src/softphonewrapper/CC_SIPCCCall.cpp
+++ b/media/webrtc/signaling/src/softphonewrapper/CC_SIPCCCall.cpp
@@ -1,13 +1,14 @@
 /* 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/. */
 
 #include "CSFLog.h"
+#include "timecard.h"
 
 #include "CC_Common.h"
 
 #include "CC_SIPCCCall.h"
 #include "CC_SIPCCCallInfo.h"
 #include "VcmSIPCCBinding.h"
 #include "CSFVideoTermination.h"
 #include "CSFAudioTermination.h"
@@ -526,33 +527,39 @@ void CC_SIPCCCall::originateP2PCall (cc_
 {
     CCAPI_Config_set_server_address(ip.c_str());
     CCAPI_Call_originateCall(callHandle, video_pref, digits.c_str());
 }
 
 /*
  * This method works asynchronously, is an onCallEvent with the resulting SDP
  */
-void CC_SIPCCCall::createOffer (cc_media_constraints_t *constraints) {
-    CCAPI_CreateOffer(callHandle, constraints);
+void CC_SIPCCCall::createOffer (cc_media_constraints_t *constraints,
+                                Timecard *tc) {
+    CCAPI_CreateOffer(callHandle, constraints, tc);
 }
 /*
  * This method works asynchronously, there is onCallEvent with the resulting SDP
  */
-void CC_SIPCCCall::createAnswer (cc_media_constraints_t *constraints) {
-    CCAPI_CreateAnswer(callHandle, constraints);
+void CC_SIPCCCall::createAnswer (cc_media_constraints_t *constraints,
+                                 Timecard *tc) {
+    CCAPI_CreateAnswer(callHandle, constraints, tc);
 
 }
 
-void CC_SIPCCCall::setLocalDescription(cc_jsep_action_t action, const std::string & sdp) {
-    CCAPI_SetLocalDescription(callHandle, action, sdp.c_str());
+void CC_SIPCCCall::setLocalDescription(cc_jsep_action_t action,
+                                       const std::string & sdp,
+                                       Timecard *tc) {
+    CCAPI_SetLocalDescription(callHandle, action, sdp.c_str(), tc);
 }
 
-void CC_SIPCCCall::setRemoteDescription(cc_jsep_action_t action, const std::string & sdp) {
-    CCAPI_SetRemoteDescription(callHandle, action, sdp.c_str());
+void CC_SIPCCCall::setRemoteDescription(cc_jsep_action_t action,
+                                       const std::string & sdp,
+                                       Timecard *tc) {
+    CCAPI_SetRemoteDescription(callHandle, action, sdp.c_str(), tc);
 }
 
 void CC_SIPCCCall::setPeerConnection(const std::string& handle)
 {
   CSFLogDebug(logTag, "setPeerConnection");
 
   peerconnection = handle;  // Cache this here. we need it to make the CC_SIPCCCallInfo
   CCAPI_SetPeerConnection(callHandle, handle.c_str());
@@ -565,11 +572,15 @@ const std::string& CC_SIPCCCall::getPeer
 void CC_SIPCCCall::addStream(cc_media_stream_id_t stream_id, cc_media_track_id_t track_id, cc_media_type_t media_type) {
   CCAPI_AddStream(callHandle, stream_id, track_id, media_type);
 }
 
 void CC_SIPCCCall::removeStream(cc_media_stream_id_t stream_id, cc_media_track_id_t track_id, cc_media_type_t media_type) {
   CCAPI_RemoveStream(callHandle, stream_id, track_id, media_type);
 }
 
-void CC_SIPCCCall::addICECandidate(const std::string & candidate, const std::string & mid, unsigned short level) {
-  CCAPI_AddICECandidate(callHandle, candidate.c_str(), mid.c_str(), (cc_level_t) level);
+void CC_SIPCCCall::addICECandidate(const std::string & candidate,
+                                   const std::string & mid,
+                                   unsigned short level,
+                                   Timecard *tc) {
+  CCAPI_AddICECandidate(callHandle, candidate.c_str(), mid.c_str(),
+                        (cc_level_t) level, tc);
 }
--- a/media/webrtc/signaling/src/softphonewrapper/CC_SIPCCCall.h
+++ b/media/webrtc/signaling/src/softphonewrapper/CC_SIPCCCall.h
@@ -4,16 +4,22 @@
 
 #ifndef _CC_SIPCCCALL_H
 #define _CC_SIPCCCALL_H
 
 #include "CC_Call.h"
 
 #include <map>
 
+#if defined(__cplusplus) && __cplusplus >= 201103L
+typedef struct Timecard Timecard;
+#else
+#include "timecard.h"
+#endif
+
 #include "common/Wrapper.h"
 #include "common/csf_common.h"
 #include "mozilla/Mutex.h"
 #include "base/lock.h"
 
 namespace CSF
 {
     struct StreamInfo
@@ -106,26 +112,26 @@ namespace CSF
         virtual bool muteAudio();
         virtual bool unmuteAudio();
         virtual bool muteVideo();
         virtual bool unmuteVideo();
         virtual void addStream(int streamId, bool isVideo);
         virtual void removeStream(int streamId);
         virtual bool setVolume(int volume);
         virtual void originateP2PCall (cc_sdp_direction_t video_pref, const std::string & digits, const std::string & ip);
-        virtual void createOffer(cc_media_constraints_t *constraints);
-        virtual void createAnswer(cc_media_constraints_t *constraints);
-        virtual void setLocalDescription(cc_jsep_action_t action, const std::string & sdp);
-        virtual void setRemoteDescription(cc_jsep_action_t action, const std::string & sdp);
+        virtual void createOffer(cc_media_constraints_t *constraints, Timecard *);
+        virtual void createAnswer(cc_media_constraints_t *constraints, Timecard *);
+        virtual void setLocalDescription(cc_jsep_action_t action, const std::string & sdp, Timecard *);
+        virtual void setRemoteDescription(cc_jsep_action_t action, const std::string & sdp, Timecard *);
         virtual void setPeerConnection(const std::string& handle);
         virtual const std::string& getPeerConnection() const;
         virtual void addStream(cc_media_stream_id_t stream_id, cc_media_track_id_t track_id, cc_media_type_t media_type);
         virtual void removeStream(cc_media_stream_id_t stream_id, cc_media_track_id_t track_id, cc_media_type_t media_type);
         virtual CC_SIPCCCallMediaDataPtr getMediaData();
-        virtual void addICECandidate(const std::string & candidate, const std::string & mid, unsigned short level);
+        virtual void addICECandidate(const std::string & candidate, const std::string & mid, unsigned short level, Timecard *);
 
     private:
         virtual bool setAudioMute(bool mute);
         virtual bool setVideoMute(bool mute);
 
         mozilla::Mutex m_lock;
     };
 
--- a/media/webrtc/signaling/src/softphonewrapper/CC_SIPCCCallInfo.cpp
+++ b/media/webrtc/signaling/src/softphonewrapper/CC_SIPCCCallInfo.cpp
@@ -444,16 +444,21 @@ cc_int32_t CC_SIPCCCallInfo::getStatusCo
     return CCAPI_CallInfo_getStatusCode(callinfo_ref);
 }
 
 MediaStreamTable* CC_SIPCCCallInfo::getMediaStreams() const
 {
 	return CCAPI_CallInfo_getMediaStreams(callinfo_ref);
 }
 
+Timecard *CC_SIPCCCallInfo::takeTimecard()
+{
+    return CCAPI_CallInfo_takeTimecard(callinfo_ref);
+}
+
 bool CC_SIPCCCallInfo::isMediaStateAvailable()
 {
     // for softphone it will always be possible to query the mute state and video direction
     return true;
 }
 
 
 #define PRINT_IF_CC_CAP_TRUE(cap) ((hasFeature(cap)) ? string(#cap) + ",": "")
--- a/media/webrtc/signaling/src/softphonewrapper/CC_SIPCCCallInfo.h
+++ b/media/webrtc/signaling/src/softphonewrapper/CC_SIPCCCallInfo.h
@@ -77,16 +77,17 @@ namespace CSF
         virtual cc_sdp_direction_t getVideoDirection();
         virtual int getVolume();
         virtual bool isMediaStateAvailable();
         virtual bool isAudioMuted();
         virtual bool isVideoMuted();
         virtual std::string getSDP();
         virtual cc_int32_t getStatusCode();
         virtual MediaStreamTable* getMediaStreams() const;
+        virtual Timecard *takeTimecard();
 
         virtual void setMediaData(CC_SIPCCCallMediaDataPtr  pMediaData);
 
     private:
         // Helper to generate the caps once - then we serve then from this cache.
         // This is fine because the info object is a snapshot, and there's no use generating this
         // multiple times if the client makes successive calls to hasCapability().
         void generateCapabilities();
--- a/media/webrtc/signaling/test/Makefile.in
+++ b/media/webrtc/signaling/test/Makefile.in
@@ -135,16 +135,17 @@ LOCAL_INCLUDES += \
  -I$(topsrcdir)/ipc/chromium/src \
  -I$(topsrcdir)/media/mtransport \
  -I$(topsrcdir)/media/mtransport/test \
  -I$(topsrcdir)/media/webrtc/signaling/include \
  -I$(topsrcdir)/media/webrtc/signaling/src/sipcc/core/sdp \
  -I$(topsrcdir)/media/webrtc/signaling/src/sipcc/cpr/include \
  -I$(topsrcdir)/media/webrtc/signaling/src/sipcc/core/includes \
  -I$(topsrcdir)/media/webrtc/signaling/src/common/browser_logging \
+ -I$(topsrcdir)/media/webrtc/signaling/src/common/time_profiling \
  -I$(topsrcdir)/media/webrtc/signaling/src/media \
  -I$(topsrcdir)/media/webrtc/signaling/src/media-conduit \
  -I$(topsrcdir)/media/webrtc/signaling/src/mediapipeline \
  -I$(topsrcdir)/media/webrtc/signaling/src/sipcc/include \
  -I$(topsrcdir)/media/webrtc/signaling/src/peerconnection \
  -I$(topsrcdir)/media/webrtc/signaling/media-conduit\
  -I$(topsrcdir)/media/webrtc/trunk/third_party/libjingle/source/ \
  -I$(topsrcdir)/xpcom/base/ \
--- a/media/webrtc/signaling/test/sdp_unittests.cpp
+++ b/media/webrtc/signaling/test/sdp_unittests.cpp
@@ -1,12 +1,14 @@
 /* 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/. */
 
+#include "timecard.h"
+
 #include "CSFLog.h"
 
 #include <string>
 
 #define GTEST_HAS_RTTI 0
 #include "gtest/gtest.h"
 #include "gtest_utils.h"