* #36737: switch back to svn repo, remove assert in sip_transaction.c
diff --git a/jni/pjproject-android/.svn/pristine/ed/ed028ee59c0680bee68151f275332eb149b17cf8.svn-base b/jni/pjproject-android/.svn/pristine/ed/ed028ee59c0680bee68151f275332eb149b17cf8.svn-base
new file mode 100644
index 0000000..cb46d1f
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/ed/ed028ee59c0680bee68151f275332eb149b17cf8.svn-base
@@ -0,0 +1,27 @@
+# $Id$
+import inc_sip as sip
+import inc_sdp as sdp
+
+sdp = \
+"""
+v=0
+o=- 0 0 IN IP4 127.0.0.1
+s=pjmedia
+c=IN IP4 127.0.0.1
+t=0 0
+m=audio 4000 RTP/AVP 0 101
+a=rtpmap:0 PCMU/8000
+a=sendrecv
+a=rtpmap:101 telephone-event/8000
+a=fmtp:101 0-15
+"""
+
+pjsua_args = "--null-audio --auto-answer 200"
+extra_headers = "Require: xxx-my-extension\n"
+include = ["Unsupported: xxx-my-extension"]
+exclude = []
+sendto_cfg = sip.SendtoCfg("Bad extension", pjsua_args, sdp, 420, 
+			   extra_headers=extra_headers,
+			   resp_inc=include, resp_exc=exclude) 
+			   
+
diff --git a/jni/pjproject-android/.svn/pristine/ed/ed3616cd19aa6b23d2a0e37745acd1a92eb75991.svn-base b/jni/pjproject-android/.svn/pristine/ed/ed3616cd19aa6b23d2a0e37745acd1a92eb75991.svn-base
new file mode 100644
index 0000000..cdc53c8
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/ed/ed3616cd19aa6b23d2a0e37745acd1a92eb75991.svn-base
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.0">
+<context>
+    <name>main</name>
+    <message>
+        <location filename="../assets/main.qml" line="11"/>
+        <source></source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../assets/main.qml" line="23"/>
+        <source>pjsua BB10</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../assets/main.qml" line="32"/>
+        <source>Starting..</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+</TS>
diff --git a/jni/pjproject-android/.svn/pristine/ed/ed401f66f29a7207130b2e2253addc5a44a16426.svn-base b/jni/pjproject-android/.svn/pristine/ed/ed401f66f29a7207130b2e2253addc5a44a16426.svn-base
new file mode 100644
index 0000000..a106d2d
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/ed/ed401f66f29a7207130b2e2253addc5a44a16426.svn-base
@@ -0,0 +1,78 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pjmedia/bidirectional.h>
+#include <pj/pool.h>
+
+
+#define THIS_FILE   "bidirectional.c"
+#define SIGNATURE   PJMEDIA_SIG_PORT_BIDIR
+
+struct bidir_port
+{
+    pjmedia_port     base;
+    pjmedia_port    *get_port;
+    pjmedia_port    *put_port;
+};
+
+
+static pj_status_t put_frame(pjmedia_port *this_port, 
+			     pjmedia_frame *frame)
+{
+    struct bidir_port *p = (struct bidir_port*)this_port;
+    return pjmedia_port_put_frame(p->put_port, frame);
+}
+
+
+static pj_status_t get_frame(pjmedia_port *this_port, 
+			     pjmedia_frame *frame)
+{
+    struct bidir_port *p = (struct bidir_port*)this_port;
+    return pjmedia_port_get_frame(p->get_port, frame);
+}
+
+
+PJ_DEF(pj_status_t) pjmedia_bidirectional_port_create( pj_pool_t *pool,
+						       pjmedia_port *get_port,
+						       pjmedia_port *put_port,
+						       pjmedia_port **p_port )
+{
+    struct bidir_port *port;
+    const pjmedia_audio_format_detail *gafd;
+
+    port = PJ_POOL_ZALLOC_T(pool, struct bidir_port);
+    gafd = pjmedia_format_get_audio_format_detail(&get_port->info.fmt, 1);
+
+    pjmedia_port_info_init(&port->base.info, &get_port->info.name, SIGNATURE,
+			   gafd->clock_rate,
+			   gafd->channel_count,
+			   gafd->bits_per_sample,
+			   PJMEDIA_AFD_SPF(gafd));
+
+    port->get_port = get_port;
+    port->put_port = put_port;
+
+    port->base.get_frame = &get_frame;
+    port->base.put_frame = &put_frame;
+
+    *p_port = &port->base;
+
+    return PJ_SUCCESS;
+}
+
diff --git a/jni/pjproject-android/.svn/pristine/ed/eda6bef560d5fdfd25b3a3131357191b8d6d7eea.svn-base b/jni/pjproject-android/.svn/pristine/ed/eda6bef560d5fdfd25b3a3131357191b8d6d7eea.svn-base
new file mode 100644
index 0000000..a025d87
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/ed/eda6bef560d5fdfd25b3a3131357191b8d6d7eea.svn-base
@@ -0,0 +1,3302 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pjsip/sip_transaction.h>
+#include <pjsip/sip_util.h>
+#include <pjsip/sip_module.h>
+#include <pjsip/sip_endpoint.h>
+#include <pjsip/sip_errno.h>
+#include <pjsip/sip_event.h>
+#include <pjlib-util/errno.h>
+#include <pj/hash.h>
+#include <pj/pool.h>
+#include <pj/os.h>
+#include <pj/rand.h>
+#include <pj/string.h>
+#include <pj/assert.h>
+#include <pj/guid.h>
+#include <pj/log.h>
+
+#define THIS_FILE   "sip_transaction.c"
+
+#if 0
+#define TSX_TRACE_(expr)    PJ_LOG(3,expr)
+#else
+#define TSX_TRACE_(expr)
+#endif
+
+/* When this macro is set, transaction will keep the hashed value
+ * so that future lookup (to unregister transaction) does not need
+ * to recalculate the hash again. It should gains a little bit of
+ * performance, so generally we'd want this.
+ */
+#define PRECALC_HASH
+
+
+/* Defined in sip_util_statefull.c */
+extern pjsip_module mod_stateful_util;
+
+
+/*****************************************************************************
+ **
+ ** Declarations and static variable definitions section.
+ **
+ *****************************************************************************
+ **/
+/* Prototypes. */
+static pj_status_t mod_tsx_layer_load(pjsip_endpoint *endpt);
+static pj_status_t mod_tsx_layer_start(void);
+static pj_status_t mod_tsx_layer_stop(void);
+static pj_status_t mod_tsx_layer_unload(void);
+static pj_bool_t   mod_tsx_layer_on_rx_request(pjsip_rx_data *rdata);
+static pj_bool_t   mod_tsx_layer_on_rx_response(pjsip_rx_data *rdata);
+
+/* Transaction layer module definition. */
+static struct mod_tsx_layer
+{
+    struct pjsip_module  mod;
+    pj_pool_t		*pool;
+    pjsip_endpoint	*endpt;
+    pj_mutex_t		*mutex;
+    pj_hash_table_t	*htable;
+} mod_tsx_layer = 
+{   {
+	NULL, NULL,			/* List's prev and next.    */
+	{ "mod-tsx-layer", 13 },	/* Module name.		    */
+	-1,				/* Module ID		    */
+	PJSIP_MOD_PRIORITY_TSX_LAYER,	/* Priority.		    */
+	mod_tsx_layer_load,		/* load().		    */
+	mod_tsx_layer_start,		/* start()		    */
+	mod_tsx_layer_stop,		/* stop()		    */
+	mod_tsx_layer_unload,		/* unload()		    */
+	mod_tsx_layer_on_rx_request,	/* on_rx_request()	    */
+	mod_tsx_layer_on_rx_response,	/* on_rx_response()	    */
+	NULL
+    }
+};
+
+/* Transaction state names */
+static const char *state_str[] = 
+{
+    "Null",
+    "Calling",
+    "Trying",
+    "Proceeding",
+    "Completed",
+    "Confirmed",
+    "Terminated",
+    "Destroyed",
+};
+
+/* Role names */
+static const char *role_name[] = 
+{
+    "UAC",
+    "UAS"
+};
+
+/* Transport flag. */
+enum
+{
+    TSX_HAS_PENDING_TRANSPORT	= 1,
+    TSX_HAS_PENDING_RESCHED	= 2,
+    TSX_HAS_PENDING_SEND	= 4,
+    TSX_HAS_PENDING_DESTROY	= 8,
+    TSX_HAS_RESOLVED_SERVER	= 16,
+};
+
+/* Timer timeout value constants */
+static pj_time_val t1_timer_val = { PJSIP_T1_TIMEOUT/1000, 
+                                    PJSIP_T1_TIMEOUT%1000 };
+static pj_time_val t2_timer_val = { PJSIP_T2_TIMEOUT/1000, 
+                                    PJSIP_T2_TIMEOUT%1000 };
+static pj_time_val t4_timer_val = { PJSIP_T4_TIMEOUT/1000, 
+                                    PJSIP_T4_TIMEOUT%1000 };
+static pj_time_val td_timer_val = { PJSIP_TD_TIMEOUT/1000, 
+                                    PJSIP_TD_TIMEOUT%1000 };
+static pj_time_val timeout_timer_val = { (64*PJSIP_T1_TIMEOUT)/1000,
+					 (64*PJSIP_T1_TIMEOUT)%1000 };
+
+#define TIMER_INACTIVE		0
+#define RETRANSMIT_TIMER	1
+#define TIMEOUT_TIMER		2
+#define TRANSPORT_ERR_TIMER	3
+
+
+/* Prototypes. */
+static pj_status_t tsx_on_state_null(		pjsip_transaction *tsx, 
+				                pjsip_event *event);
+static pj_status_t tsx_on_state_calling(	pjsip_transaction *tsx, 
+				                pjsip_event *event);
+static pj_status_t tsx_on_state_trying(		pjsip_transaction *tsx, 
+				                pjsip_event *event);
+static pj_status_t tsx_on_state_proceeding_uas( pjsip_transaction *tsx, 
+					        pjsip_event *event);
+static pj_status_t tsx_on_state_proceeding_uac( pjsip_transaction *tsx,
+					        pjsip_event *event);
+static pj_status_t tsx_on_state_completed_uas(	pjsip_transaction *tsx, 
+					        pjsip_event *event);
+static pj_status_t tsx_on_state_completed_uac(	pjsip_transaction *tsx,
+					        pjsip_event *event);
+static pj_status_t tsx_on_state_confirmed(	pjsip_transaction *tsx, 
+					        pjsip_event *event);
+static pj_status_t tsx_on_state_terminated(	pjsip_transaction *tsx, 
+					        pjsip_event *event);
+static pj_status_t tsx_on_state_destroyed(	pjsip_transaction *tsx, 
+					        pjsip_event *event);
+static void        tsx_timer_callback( pj_timer_heap_t *theap, 
+			               pj_timer_entry *entry);
+static void	   tsx_tp_state_callback(
+				       pjsip_transport *tp,
+				       pjsip_transport_state state,
+				       const pjsip_transport_state_info *info);
+static void 	   tsx_set_state( pjsip_transaction *tsx,
+            	                  pjsip_tsx_state_e state,
+            	                  pjsip_event_id_e event_src_type,
+            	                  void *event_src );
+static void 	   tsx_set_state_no_notify( pjsip_transaction *tsx,
+            	                            pjsip_tsx_state_e state,
+            	                            pjsip_event_id_e event_src_type,
+            	                            void *event_src );
+static void 	   tsx_set_status_code(pjsip_transaction *tsx,
+            	                       int code, const pj_str_t *reason);
+static pj_status_t tsx_create( pjsip_module *tsx_user,
+                               pj_grp_lock_t *grp_lock,
+			       pjsip_transaction **p_tsx);
+static void	   tsx_on_destroy(void *arg);
+static pj_status_t tsx_shutdown( pjsip_transaction *tsx );
+static void	   tsx_resched_retransmission( pjsip_transaction *tsx );
+static pj_status_t tsx_retransmit( pjsip_transaction *tsx, int resched);
+static int         tsx_send_msg( pjsip_transaction *tsx, 
+                                 pjsip_tx_data *tdata);
+static void        tsx_update_transport( pjsip_transaction *tsx, 
+					 pjsip_transport *tp);
+
+
+/* State handlers for UAC, indexed by state */
+static int  (*tsx_state_handler_uac[PJSIP_TSX_STATE_MAX])(pjsip_transaction *,
+							  pjsip_event *) = 
+{
+    &tsx_on_state_null,
+    &tsx_on_state_calling,
+    NULL,
+    &tsx_on_state_proceeding_uac,
+    &tsx_on_state_completed_uac,
+    &tsx_on_state_confirmed,
+    &tsx_on_state_terminated,
+    &tsx_on_state_destroyed,
+};
+
+/* State handlers for UAS */
+static int  (*tsx_state_handler_uas[PJSIP_TSX_STATE_MAX])(pjsip_transaction *, 
+							  pjsip_event *) = 
+{
+    &tsx_on_state_null,
+    NULL,
+    &tsx_on_state_trying,
+    &tsx_on_state_proceeding_uas,
+    &tsx_on_state_completed_uas,
+    &tsx_on_state_confirmed,
+    &tsx_on_state_terminated,
+    &tsx_on_state_destroyed,
+};
+
+/*****************************************************************************
+ **
+ ** Utilities
+ **
+ *****************************************************************************
+ */
+/*
+ * Get transaction state name.
+ */
+PJ_DEF(const char *) pjsip_tsx_state_str(pjsip_tsx_state_e state)
+{
+    return state_str[state];
+}
+
+/*
+ * Get the role name.
+ */
+PJ_DEF(const char *) pjsip_role_name(pjsip_role_e role)
+{
+    return role_name[role];
+}
+
+
+/*
+ * Create transaction key for RFC2543 compliant messages, which don't have
+ * unique branch parameter in the top most Via header.
+ *
+ * INVITE requests matches a transaction if the following attributes
+ * match the original request:
+ *	- Request-URI
+ *	- To tag
+ *	- From tag
+ *	- Call-ID
+ *	- CSeq
+ *	- top Via header
+ *
+ * CANCEL matching is done similarly as INVITE, except:
+ *	- CSeq method will differ
+ *	- To tag is not matched.
+ *
+ * ACK matching is done similarly, except that:
+ *	- method of the CSeq will differ,
+ *	- To tag is matched to the response sent by the server transaction.
+ *
+ * The transaction key is constructed from the common components of above
+ * components. Additional comparison is needed to fully match a transaction.
+ */
+static pj_status_t create_tsx_key_2543( pj_pool_t *pool,
+			                pj_str_t *str,
+			                pjsip_role_e role,
+			                const pjsip_method *method,
+			                const pjsip_rx_data *rdata )
+{
+#define SEPARATOR   '$'
+    char *key, *p;
+    pj_ssize_t len;
+    pj_size_t len_required;
+    pj_str_t *host;
+
+    PJ_ASSERT_RETURN(pool && str && method && rdata, PJ_EINVAL);
+    PJ_ASSERT_RETURN(rdata->msg_info.msg, PJ_EINVAL);
+    PJ_ASSERT_RETURN(rdata->msg_info.via, PJSIP_EMISSINGHDR);
+    PJ_ASSERT_RETURN(rdata->msg_info.cseq, PJSIP_EMISSINGHDR);
+    PJ_ASSERT_RETURN(rdata->msg_info.from, PJSIP_EMISSINGHDR);
+
+    host = &rdata->msg_info.via->sent_by.host;
+
+    /* Calculate length required. */
+    len_required = 9 +			    /* CSeq number */
+		   rdata->msg_info.from->tag.slen +   /* From tag. */
+		   rdata->msg_info.cid->id.slen +    /* Call-ID */
+		   host->slen +		    /* Via host. */
+		   9 +			    /* Via port. */
+		   16;			    /* Separator+Allowance. */
+    key = p = (char*) pj_pool_alloc(pool, len_required);
+
+    /* Add role. */
+    *p++ = (char)(role==PJSIP_ROLE_UAC ? 'c' : 's');
+    *p++ = SEPARATOR;
+
+    /* Add method, except when method is INVITE or ACK. */
+    if (method->id != PJSIP_INVITE_METHOD && method->id != PJSIP_ACK_METHOD) {
+	pj_memcpy(p, method->name.ptr, method->name.slen);
+	p += method->name.slen;
+	*p++ = '$';
+    }
+
+    /* Add CSeq (only the number). */
+    len = pj_utoa(rdata->msg_info.cseq->cseq, p);
+    p += len;
+    *p++ = SEPARATOR;
+
+    /* Add From tag. */
+    len = rdata->msg_info.from->tag.slen;
+    pj_memcpy( p, rdata->msg_info.from->tag.ptr, len);
+    p += len;
+    *p++ = SEPARATOR;
+
+    /* Add Call-ID. */
+    len = rdata->msg_info.cid->id.slen;
+    pj_memcpy( p, rdata->msg_info.cid->id.ptr, len );
+    p += len;
+    *p++ = SEPARATOR;
+
+    /* Add top Via header. 
+     * We don't really care whether the port contains the real port (because
+     * it can be omited if default port is used). Anyway this function is 
+     * only used to match request retransmission, and we expect that the 
+     * request retransmissions will contain the same port.
+     */
+    pj_memcpy(p, host->ptr, host->slen);
+    p += host->slen;
+    *p++ = ':';
+
+    len = pj_utoa(rdata->msg_info.via->sent_by.port, p);
+    p += len;
+    *p++ = SEPARATOR;
+    
+    *p++ = '\0';
+
+    /* Done. */
+    str->ptr = key;
+    str->slen = p-key;
+
+    return PJ_SUCCESS;
+}
+
+/*
+ * Create transaction key for RFC3161 compliant system.
+ */
+static pj_status_t create_tsx_key_3261( pj_pool_t *pool,
+		                        pj_str_t *key,
+		                        pjsip_role_e role,
+		                        const pjsip_method *method,
+		                        const pj_str_t *branch)
+{
+    char *p;
+
+    PJ_ASSERT_RETURN(pool && key && method && branch, PJ_EINVAL);
+
+    p = key->ptr = (char*) 
+    		   pj_pool_alloc(pool, branch->slen + method->name.slen + 4 );
+    
+    /* Add role. */
+    *p++ = (char)(role==PJSIP_ROLE_UAC ? 'c' : 's');
+    *p++ = SEPARATOR;
+
+    /* Add method, except when method is INVITE or ACK. */
+    if (method->id != PJSIP_INVITE_METHOD && method->id != PJSIP_ACK_METHOD) {
+	pj_memcpy(p, method->name.ptr, method->name.slen);
+	p += method->name.slen;
+	*p++ = '$';
+    }
+
+    /* Add branch ID. */
+    pj_memcpy(p, branch->ptr, branch->slen);
+    p += branch->slen;
+
+    /* Set length */
+    key->slen = p - key->ptr;
+
+    return PJ_SUCCESS;
+}
+
+/*
+ * Create key from the incoming data, to be used to search the transaction
+ * in the transaction hash table.
+ */
+PJ_DEF(pj_status_t) pjsip_tsx_create_key( pj_pool_t *pool, pj_str_t *key, 
+				          pjsip_role_e role, 
+				          const pjsip_method *method, 
+				          const pjsip_rx_data *rdata)
+{
+    pj_str_t rfc3261_branch = {PJSIP_RFC3261_BRANCH_ID, 
+                               PJSIP_RFC3261_BRANCH_LEN};
+
+
+    /* Get the branch parameter in the top-most Via.
+     * If branch parameter is started with "z9hG4bK", then the message was
+     * generated by agent compliant with RFC3261. Otherwise, it will be
+     * handled as RFC2543.
+     */
+    const pj_str_t *branch = &rdata->msg_info.via->branch_param;
+
+    if (pj_strnicmp(branch,&rfc3261_branch,PJSIP_RFC3261_BRANCH_LEN)==0) {
+
+	/* Create transaction key. */
+	return create_tsx_key_3261(pool, key, role, method, branch);
+
+    } else {
+	/* Create the key for the message. This key will be matched up 
+         * with the transaction key. For RFC2563 transactions, the 
+         * transaction key was created by the same function, so it will 
+         * match the message.
+	 */
+	return create_tsx_key_2543( pool, key, role, method, rdata );
+    }
+}
+
+/*****************************************************************************
+ **
+ ** Transaction layer module
+ **
+ *****************************************************************************
+ **/
+/*
+ * Create transaction layer module and registers it to the endpoint.
+ */
+PJ_DEF(pj_status_t) pjsip_tsx_layer_init_module(pjsip_endpoint *endpt)
+{
+    pj_pool_t *pool;
+    pj_status_t status;
+
+
+    PJ_ASSERT_RETURN(mod_tsx_layer.endpt==NULL, PJ_EINVALIDOP);
+
+    /* Initialize timer values */
+    t1_timer_val.sec  = pjsip_cfg()->tsx.t1 / 1000;
+    t1_timer_val.msec = pjsip_cfg()->tsx.t1 % 1000;
+    t2_timer_val.sec  = pjsip_cfg()->tsx.t2 / 1000;
+    t2_timer_val.msec = pjsip_cfg()->tsx.t2 % 1000;
+    t4_timer_val.sec  = pjsip_cfg()->tsx.t4 / 1000;
+    t4_timer_val.msec = pjsip_cfg()->tsx.t4 % 1000;
+    td_timer_val.sec  = pjsip_cfg()->tsx.td / 1000;
+    td_timer_val.msec = pjsip_cfg()->tsx.td % 1000;
+    /* Changed the initialization below to use td_timer_val instead, to enable
+     * customization to the timeout value.
+     */
+    //timeout_timer_val.sec  = (64 * pjsip_cfg()->tsx.t1) / 1000;
+    //timeout_timer_val.msec = (64 * pjsip_cfg()->tsx.t1) % 1000;
+    timeout_timer_val = td_timer_val;
+
+    /*
+     * Initialize transaction layer structure.
+     */
+
+    /* Create pool for the module. */
+    pool = pjsip_endpt_create_pool(endpt, "tsxlayer", 
+				   PJSIP_POOL_TSX_LAYER_LEN,
+				   PJSIP_POOL_TSX_LAYER_INC );
+    if (!pool)
+	return PJ_ENOMEM;
+
+    
+    /* Initialize some attributes. */
+    mod_tsx_layer.pool = pool;
+    mod_tsx_layer.endpt = endpt;
+
+
+    /* Create hash table. */
+    mod_tsx_layer.htable = pj_hash_create( pool, pjsip_cfg()->tsx.max_count );
+    if (!mod_tsx_layer.htable) {
+	pjsip_endpt_release_pool(endpt, pool);
+	return PJ_ENOMEM;
+    }
+
+    /* Create group lock. */
+    status = pj_mutex_create_recursive(pool, "tsxlayer", &mod_tsx_layer.mutex);
+    if (status != PJ_SUCCESS) {
+	pjsip_endpt_release_pool(endpt, pool);
+	return status;
+    }
+
+    /*
+     * Register transaction layer module to endpoint.
+     */
+    status = pjsip_endpt_register_module( endpt, &mod_tsx_layer.mod );
+    if (status != PJ_SUCCESS) {
+	pj_mutex_destroy(mod_tsx_layer.mutex);
+	pjsip_endpt_release_pool(endpt, pool);
+	return status;
+    }
+
+    /* Register mod_stateful_util module (sip_util_statefull.c) */
+    status = pjsip_endpt_register_module(endpt, &mod_stateful_util);
+    if (status != PJ_SUCCESS) {
+	return status;
+    }
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Get the instance of transaction layer module.
+ */
+PJ_DEF(pjsip_module*) pjsip_tsx_layer_instance(void)
+{
+    return &mod_tsx_layer.mod;
+}
+
+
+/*
+ * Unregister and destroy transaction layer module.
+ */
+PJ_DEF(pj_status_t) pjsip_tsx_layer_destroy(void)
+{
+    /* Are we registered? */
+    PJ_ASSERT_RETURN(mod_tsx_layer.endpt!=NULL, PJ_EINVALIDOP);
+
+    /* Unregister from endpoint. 
+     * Clean-ups will be done in the unload() module callback.
+     */
+    return pjsip_endpt_unregister_module( mod_tsx_layer.endpt, 
+					  &mod_tsx_layer.mod);
+}
+
+
+/*
+ * Register the transaction to the hash table.
+ */
+static pj_status_t mod_tsx_layer_register_tsx( pjsip_transaction *tsx)
+{
+    pj_assert(tsx->transaction_key.slen != 0);
+
+    /* Lock hash table mutex. */
+    pj_mutex_lock(mod_tsx_layer.mutex);
+
+    /* Check if no transaction with the same key exists. 
+     * Do not use PJ_ASSERT_RETURN since it evaluates the expression
+     * twice!
+     */
+    if(pj_hash_get_lower(mod_tsx_layer.htable, 
+		         tsx->transaction_key.ptr,
+		         (unsigned)tsx->transaction_key.slen, 
+		         NULL))
+    {
+	pj_mutex_unlock(mod_tsx_layer.mutex);
+	PJ_LOG(2,(THIS_FILE, 
+		  "Unable to register %.*s transaction (key exists)",
+		  (int)tsx->method.name.slen,
+		  tsx->method.name.ptr));
+	return PJ_EEXISTS;
+    }
+
+    TSX_TRACE_((THIS_FILE, 
+		"Transaction %p registered with hkey=0x%p and key=%.*s",
+		tsx, tsx->hashed_key, tsx->transaction_key.slen,
+		tsx->transaction_key.ptr));
+
+    /* Register the transaction to the hash table. */
+#ifdef PRECALC_HASH
+    pj_hash_set_lower( tsx->pool, mod_tsx_layer.htable,
+                       tsx->transaction_key.ptr,
+    		       (unsigned)tsx->transaction_key.slen, 
+		       tsx->hashed_key, tsx);
+#else
+    pj_hash_set_lower( tsx->pool, mod_tsx_layer.htable,
+                       tsx->transaction_key.ptr,
+    		       tsx->transaction_key.slen, 0, tsx);
+#endif
+
+    /* Unlock mutex. */
+    pj_mutex_unlock(mod_tsx_layer.mutex);
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Unregister the transaction from the hash table.
+ */
+static void mod_tsx_layer_unregister_tsx( pjsip_transaction *tsx)
+{
+    if (mod_tsx_layer.mod.id == -1) {
+	/* The transaction layer has been unregistered. This could happen
+	 * if the transaction was pending on transport and the application
+	 * is shutdown. See http://trac.pjsip.org/repos/ticket/1033. In
+	 * this case just do nothing.
+	 */
+	return;
+    }
+
+    pj_assert(tsx->transaction_key.slen != 0);
+    //pj_assert(tsx->state != PJSIP_TSX_STATE_NULL);
+
+    /* Lock hash table mutex. */
+    pj_mutex_lock(mod_tsx_layer.mutex);
+
+    /* Register the transaction to the hash table. */
+#ifdef PRECALC_HASH
+    pj_hash_set_lower( NULL, mod_tsx_layer.htable, tsx->transaction_key.ptr,
+    		       (unsigned)tsx->transaction_key.slen, tsx->hashed_key, 
+		       NULL);
+#else
+    pj_hash_set_lower( NULL, mod_tsx_layer.htable, tsx->transaction_key.ptr,
+    		       tsx->transaction_key.slen, 0, NULL);
+#endif
+
+    TSX_TRACE_((THIS_FILE, 
+		"Transaction %p unregistered, hkey=0x%p and key=%.*s",
+		tsx, tsx->hashed_key, tsx->transaction_key.slen,
+		tsx->transaction_key.ptr));
+
+    /* Unlock mutex. */
+    pj_mutex_unlock(mod_tsx_layer.mutex);
+}
+
+
+/*
+ * Retrieve the current number of transactions currently registered in 
+ * the hash table.
+ */
+PJ_DEF(unsigned) pjsip_tsx_layer_get_tsx_count(void)
+{
+    unsigned count;
+
+    /* Are we registered? */
+    PJ_ASSERT_RETURN(mod_tsx_layer.endpt!=NULL, 0);
+
+    pj_mutex_lock(mod_tsx_layer.mutex);
+    count = pj_hash_count(mod_tsx_layer.htable);
+    pj_mutex_unlock(mod_tsx_layer.mutex);
+
+    return count;
+}
+
+
+/*
+ * Find a transaction.
+ */
+PJ_DEF(pjsip_transaction*) pjsip_tsx_layer_find_tsx( const pj_str_t *key,
+						     pj_bool_t lock )
+{
+    pjsip_transaction *tsx;
+    pj_uint32_t hval = 0;
+
+    pj_mutex_lock(mod_tsx_layer.mutex);
+    tsx = (pjsip_transaction*)
+    	  pj_hash_get_lower( mod_tsx_layer.htable, key->ptr, 
+			     (unsigned)key->slen, &hval );
+    
+    /* Prevent the transaction to get deleted before we have chance to lock it.
+     */
+    if (tsx && lock)
+        pj_grp_lock_add_ref(tsx->grp_lock);
+    
+    pj_mutex_unlock(mod_tsx_layer.mutex);
+
+    TSX_TRACE_((THIS_FILE, 
+		"Finding tsx with hkey=0x%p and key=%.*s: found %p",
+		hval, key->slen, key->ptr, tsx));
+
+    /* Simulate race condition! */
+    PJ_RACE_ME(5);
+
+    if (tsx && lock) {
+	pj_grp_lock_acquire(tsx->grp_lock);
+        pj_grp_lock_dec_ref(tsx->grp_lock);
+    }
+
+    return tsx;
+}
+
+
+/* This module callback is called when module is being loaded by
+ * endpoint. It does nothing for this module.
+ */
+static pj_status_t mod_tsx_layer_load(pjsip_endpoint *endpt)
+{
+    PJ_UNUSED_ARG(endpt);
+    return PJ_SUCCESS;
+}
+
+
+/* This module callback is called when module is being started by
+ * endpoint. It does nothing for this module.
+ */
+static pj_status_t mod_tsx_layer_start(void)
+{
+    return PJ_SUCCESS;
+}
+
+
+/* This module callback is called when module is being stopped by
+ * endpoint. 
+ */
+static pj_status_t mod_tsx_layer_stop(void)
+{
+    pj_hash_iterator_t it_buf, *it;
+
+    PJ_LOG(4,(THIS_FILE, "Stopping transaction layer module"));
+
+    pj_mutex_lock(mod_tsx_layer.mutex);
+
+    /* Destroy all transactions. */
+    it = pj_hash_first(mod_tsx_layer.htable, &it_buf);
+    while (it) {
+	pjsip_transaction *tsx = (pjsip_transaction*) 
+				 pj_hash_this(mod_tsx_layer.htable, it);
+	pj_hash_iterator_t *next = pj_hash_next(mod_tsx_layer.htable, it);
+	if (tsx) {
+	    pjsip_tsx_terminate(tsx, PJSIP_SC_SERVICE_UNAVAILABLE);
+	    mod_tsx_layer_unregister_tsx(tsx);
+	    tsx_shutdown(tsx);
+	}
+	it = next;
+    }
+
+    pj_mutex_unlock(mod_tsx_layer.mutex);
+
+    PJ_LOG(4,(THIS_FILE, "Stopped transaction layer module"));
+
+    return PJ_SUCCESS;
+}
+
+
+/* Destroy this module */
+static void tsx_layer_destroy(pjsip_endpoint *endpt)
+{
+    PJ_UNUSED_ARG(endpt);
+
+    /* Destroy mutex. */
+    pj_mutex_destroy(mod_tsx_layer.mutex);
+
+    /* Release pool. */
+    pjsip_endpt_release_pool(mod_tsx_layer.endpt, mod_tsx_layer.pool);
+
+    /* Mark as unregistered. */
+    mod_tsx_layer.endpt = NULL;
+
+    PJ_LOG(4,(THIS_FILE, "Transaction layer module destroyed"));
+}
+
+
+/* This module callback is called when module is being unloaded by
+ * endpoint.
+ */
+static pj_status_t mod_tsx_layer_unload(void)
+{
+    /* Only self destroy when there's no transaction in the table.
+     * Transaction may refuse to destroy when it has pending
+     * transmission. If we destroy the module now, application will
+     * crash when the pending transaction finally got error response
+     * from transport and when it tries to unregister itself.
+     */
+    if (pj_hash_count(mod_tsx_layer.htable) != 0) {
+	if (pjsip_endpt_atexit(mod_tsx_layer.endpt, &tsx_layer_destroy) !=
+	    PJ_SUCCESS)
+	{
+	    PJ_LOG(3,(THIS_FILE, "Failed to register transaction layer "
+				 "module destroy."));
+	}
+	return PJ_EBUSY;
+    }
+
+    tsx_layer_destroy(mod_tsx_layer.endpt);
+
+    return PJ_SUCCESS;
+}
+
+
+/* This module callback is called when endpoint has received an
+ * incoming request message.
+ */
+static pj_bool_t mod_tsx_layer_on_rx_request(pjsip_rx_data *rdata)
+{
+    pj_str_t key;
+    pj_uint32_t hval = 0;
+    pjsip_transaction *tsx;
+
+    pjsip_tsx_create_key(rdata->tp_info.pool, &key, PJSIP_ROLE_UAS,
+			 &rdata->msg_info.cseq->method, rdata);
+
+    /* Find transaction. */
+    pj_mutex_lock( mod_tsx_layer.mutex );
+
+    tsx = (pjsip_transaction*) 
+    	  pj_hash_get_lower( mod_tsx_layer.htable, key.ptr, (unsigned)key.slen, 
+			     &hval );
+
+
+    TSX_TRACE_((THIS_FILE, 
+		"Finding tsx for request, hkey=0x%p and key=%.*s, found %p",
+		hval, key.slen, key.ptr, tsx));
+
+
+    if (tsx == NULL || tsx->state == PJSIP_TSX_STATE_TERMINATED) {
+	/* Transaction not found.
+	 * Reject the request so that endpoint passes the request to
+	 * upper layer modules.
+	 */
+	pj_mutex_unlock( mod_tsx_layer.mutex);
+	return PJ_FALSE;
+    }
+
+    /* Prevent the transaction to get deleted before we have chance to lock it
+     * in pjsip_tsx_recv_msg().
+     */
+    pj_grp_lock_add_ref(tsx->grp_lock);
+    
+    /* Unlock hash table. */
+    pj_mutex_unlock( mod_tsx_layer.mutex );
+
+    /* Simulate race condition! */
+    PJ_RACE_ME(5);
+
+    /* Pass the message to the transaction. */
+    pjsip_tsx_recv_msg(tsx, rdata );
+    
+    pj_grp_lock_dec_ref(tsx->grp_lock);
+
+    return PJ_TRUE;
+}
+
+
+/* This module callback is called when endpoint has received an
+ * incoming response message.
+ */
+static pj_bool_t mod_tsx_layer_on_rx_response(pjsip_rx_data *rdata)
+{
+    pj_str_t key;
+    pj_uint32_t hval = 0;
+    pjsip_transaction *tsx;
+
+    pjsip_tsx_create_key(rdata->tp_info.pool, &key, PJSIP_ROLE_UAC,
+			 &rdata->msg_info.cseq->method, rdata);
+
+    /* Find transaction. */
+    pj_mutex_lock( mod_tsx_layer.mutex );
+
+    tsx = (pjsip_transaction*) 
+    	  pj_hash_get_lower( mod_tsx_layer.htable, key.ptr, (unsigned)key.slen, 
+			     &hval );
+
+
+    TSX_TRACE_((THIS_FILE, 
+		"Finding tsx for response, hkey=0x%p and key=%.*s, found %p",
+		hval, key.slen, key.ptr, tsx));
+
+
+    if (tsx == NULL || tsx->state == PJSIP_TSX_STATE_TERMINATED) {
+	/* Transaction not found.
+	 * Reject the request so that endpoint passes the request to
+	 * upper layer modules.
+	 */
+	pj_mutex_unlock( mod_tsx_layer.mutex);
+	return PJ_FALSE;
+    }
+
+    /* Prevent the transaction to get deleted before we have chance to lock it
+     * in pjsip_tsx_recv_msg().
+     */
+    pj_grp_lock_add_ref(tsx->grp_lock);
+
+    /* Unlock hash table. */
+    pj_mutex_unlock( mod_tsx_layer.mutex );
+
+    /* Simulate race condition! */
+    PJ_RACE_ME(5);
+
+    /* Pass the message to the transaction. */
+    pjsip_tsx_recv_msg(tsx, rdata );
+    
+    pj_grp_lock_dec_ref(tsx->grp_lock);
+
+    return PJ_TRUE;
+}
+
+
+/*
+ * Get transaction instance in the rdata.
+ */
+PJ_DEF(pjsip_transaction*) pjsip_rdata_get_tsx( pjsip_rx_data *rdata )
+{
+    return (pjsip_transaction*) 
+    	   rdata->endpt_info.mod_data[mod_tsx_layer.mod.id];
+}
+
+
+/*
+ * Dump transaction layer.
+ */
+PJ_DEF(void) pjsip_tsx_layer_dump(pj_bool_t detail)
+{
+#if PJ_LOG_MAX_LEVEL >= 3
+    pj_hash_iterator_t itbuf, *it;
+
+    /* Lock mutex. */
+    pj_mutex_lock(mod_tsx_layer.mutex);
+
+    PJ_LOG(3, (THIS_FILE, "Dumping transaction table:"));
+    PJ_LOG(3, (THIS_FILE, " Total %d transactions", 
+			  pj_hash_count(mod_tsx_layer.htable)));
+
+    if (detail) {
+	it = pj_hash_first(mod_tsx_layer.htable, &itbuf);
+	if (it == NULL) {
+	    PJ_LOG(3, (THIS_FILE, " - none - "));
+	} else {
+	    while (it != NULL) {
+		pjsip_transaction *tsx = (pjsip_transaction*) 
+					 pj_hash_this(mod_tsx_layer.htable,it);
+
+		PJ_LOG(3, (THIS_FILE, " %s %s|%d|%s",
+			   tsx->obj_name,
+			   (tsx->last_tx? 
+				pjsip_tx_data_get_info(tsx->last_tx): 
+				"none"),
+			   tsx->status_code,
+			   pjsip_tsx_state_str(tsx->state)));
+
+		it = pj_hash_next(mod_tsx_layer.htable, it);
+	    }
+	}
+    }
+
+    /* Unlock mutex. */
+    pj_mutex_unlock(mod_tsx_layer.mutex);
+#endif
+}
+
+/*****************************************************************************
+ **
+ ** Transaction
+ **
+ *****************************************************************************
+ **/
+/* Lock transaction for accessing the timeout timer only. */
+static void lock_timer(pjsip_transaction *tsx)
+{
+    pj_mutex_lock(tsx->mutex_b);
+}
+
+/* Unlock timer */
+static void unlock_timer(pjsip_transaction *tsx)
+{
+    pj_mutex_unlock(tsx->mutex_b);
+}
+
+/* Utility: schedule a timer */
+static pj_status_t tsx_schedule_timer(pjsip_transaction *tsx,
+                                      pj_timer_entry *entry,
+                                      const pj_time_val *delay,
+                                      int active_id)
+{
+    pj_timer_heap_t *timer_heap = pjsip_endpt_get_timer_heap(tsx->endpt);
+    pj_status_t status;
+
+    pj_assert(active_id != 0);
+    status = pj_timer_heap_schedule_w_grp_lock(timer_heap, entry,
+                                               delay, active_id,
+                                               tsx->grp_lock);
+
+    return status;
+}
+
+/* Utility: cancel a timer */
+static int tsx_cancel_timer(pjsip_transaction *tsx,
+                            pj_timer_entry *entry)
+{
+    pj_timer_heap_t *timer_heap = pjsip_endpt_get_timer_heap(tsx->endpt);
+    return pj_timer_heap_cancel_if_active(timer_heap, entry, TIMER_INACTIVE);
+}
+
+/* Create and initialize basic transaction structure.
+ * This function is called by both UAC and UAS creation.
+ */
+static pj_status_t tsx_create( pjsip_module *tsx_user,
+                               pj_grp_lock_t *grp_lock,
+			       pjsip_transaction **p_tsx)
+{
+    pj_pool_t *pool;
+    pjsip_transaction *tsx;
+    pj_status_t status;
+
+    pool = pjsip_endpt_create_pool( mod_tsx_layer.endpt, "tsx", 
+				    PJSIP_POOL_TSX_LEN, PJSIP_POOL_TSX_INC );
+    if (!pool)
+	return PJ_ENOMEM;
+
+    tsx = PJ_POOL_ZALLOC_T(pool, pjsip_transaction);
+    tsx->pool = pool;
+    tsx->tsx_user = tsx_user;
+    tsx->endpt = mod_tsx_layer.endpt;
+
+    pj_ansi_snprintf(tsx->obj_name, sizeof(tsx->obj_name), 
+		     "tsx%p", tsx);
+    pj_memcpy(pool->obj_name, tsx->obj_name, sizeof(pool->obj_name));
+
+    tsx->handle_200resp = 1;
+    tsx->retransmit_timer.id = TIMER_INACTIVE;
+    tsx->retransmit_timer.user_data = tsx;
+    tsx->retransmit_timer.cb = &tsx_timer_callback;
+    tsx->timeout_timer.id = TIMER_INACTIVE;
+    tsx->timeout_timer.user_data = tsx;
+    tsx->timeout_timer.cb = &tsx_timer_callback;
+    
+    if (grp_lock) {
+	tsx->grp_lock = grp_lock;
+    } else {
+	status = pj_grp_lock_create(pool, NULL, &tsx->grp_lock);
+	if (status != PJ_SUCCESS) {
+	    pjsip_endpt_release_pool(mod_tsx_layer.endpt, pool);
+	    return status;
+	}
+    }
+
+    pj_grp_lock_add_ref(tsx->grp_lock);
+    pj_grp_lock_add_handler(tsx->grp_lock, tsx->pool, tsx, &tsx_on_destroy);
+
+    status = pj_mutex_create_simple(pool, tsx->obj_name, &tsx->mutex_b);
+    if (status != PJ_SUCCESS) {
+	tsx_shutdown(tsx);
+	return status;
+    }
+
+    *p_tsx = tsx;
+    return PJ_SUCCESS;
+}
+
+/* Really destroy transaction, when grp_lock reference is zero */
+static void tsx_on_destroy( void *arg )
+{
+    pjsip_transaction *tsx = (pjsip_transaction*)arg;
+
+    PJ_LOG(5,(tsx->obj_name, "Transaction destroyed!"));
+
+    pj_mutex_destroy(tsx->mutex_b);
+    pjsip_endpt_release_pool(tsx->endpt, tsx->pool);
+}
+
+/* Shutdown transaction. */
+static pj_status_t tsx_shutdown( pjsip_transaction *tsx )
+{
+    /* Release the transport */
+    tsx_update_transport(tsx, NULL);
+
+    /* Decrement reference counter in transport selector, only if
+     * we haven't been called before */
+    if (!tsx->terminating) {
+	pjsip_tpselector_dec_ref(&tsx->tp_sel);
+    }
+
+    /* Free last transmitted message. */
+    if (tsx->last_tx) {
+	pjsip_tx_data_dec_ref( tsx->last_tx );
+	tsx->last_tx = NULL;
+    }
+    /* Cancel timeout timer. */
+    tsx_cancel_timer(tsx, &tsx->timeout_timer);
+
+    /* Cancel retransmission timer. */
+    tsx_cancel_timer(tsx, &tsx->retransmit_timer);
+
+    /* Clear some pending flags. */
+    tsx->transport_flag &= ~(TSX_HAS_PENDING_RESCHED | TSX_HAS_PENDING_SEND);
+
+
+    /* Refuse to destroy transaction if it has pending resolving. */
+    if (tsx->transport_flag & TSX_HAS_PENDING_TRANSPORT) {
+	tsx->transport_flag |= TSX_HAS_PENDING_DESTROY;
+	tsx->tsx_user = NULL;
+	PJ_LOG(4,(tsx->obj_name, "Will destroy later because transport is "
+				 "in progress"));
+    }
+
+    if (!tsx->terminating) {
+	tsx->terminating = PJ_TRUE;
+	pj_grp_lock_dec_ref(tsx->grp_lock);
+    }
+
+    /* No acccess to tsx after this, it may have been destroyed */
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Callback when timer expires. Transport error also piggybacks this event
+ * to avoid deadlock (https://trac.pjsip.org/repos/ticket/1646).
+ */
+static void tsx_timer_callback( pj_timer_heap_t *theap, pj_timer_entry *entry)
+{
+    pjsip_transaction *tsx = (pjsip_transaction*) entry->user_data;
+
+    PJ_UNUSED_ARG(theap);
+
+    if (entry->id == TRANSPORT_ERR_TIMER) {
+	/* Posted transport error event */
+	entry->id = 0;
+	if (tsx->state < PJSIP_TSX_STATE_TERMINATED) {
+	    pjsip_tsx_state_e prev_state;
+
+	    pj_grp_lock_acquire(tsx->grp_lock);
+	    prev_state = tsx->state;
+
+	    /* Release transport as it's no longer working. */
+	    tsx_update_transport(tsx, NULL);
+
+	    if (tsx->status_code < 200) {
+		pj_str_t err;
+		char errmsg[PJ_ERR_MSG_SIZE];
+
+		err = pj_strerror(tsx->transport_err, errmsg, sizeof(errmsg));
+		tsx_set_status_code(tsx, PJSIP_SC_TSX_TRANSPORT_ERROR, &err);
+	    }
+
+	    /* Set transaction state etc, but don't notify TU now,
+	     * otherwise we'll get a deadlock. See:
+	     * https://trac.pjsip.org/repos/ticket/1646
+	     */
+	    tsx_set_state_no_notify( tsx, PJSIP_TSX_STATE_TERMINATED,
+	                             PJSIP_EVENT_TRANSPORT_ERROR, NULL);
+	    pj_grp_lock_release(tsx->grp_lock);
+
+	    /* Now notify TU about state change, WITHOUT holding the
+	     * group lock. It should be safe to do so; transaction will
+	     * not get destroyed because group lock reference counter
+	     * has been incremented by the timer heap.
+	     */
+	    if (tsx->tsx_user && tsx->tsx_user->on_tsx_state) {
+		pjsip_event e;
+		PJSIP_EVENT_INIT_TSX_STATE(e, tsx,
+		                           PJSIP_EVENT_TRANSPORT_ERROR, NULL,
+					   prev_state);
+		(*tsx->tsx_user->on_tsx_state)(tsx, &e);
+	    }
+	}
+    } else {
+	pjsip_event event;
+
+	entry->id = 0;
+
+	PJ_LOG(5,(tsx->obj_name, "%s timer event",
+		 (entry==&tsx->retransmit_timer ? "Retransmit":"Timeout")));
+	pj_log_push_indent();
+
+
+	PJSIP_EVENT_INIT_TIMER(event, entry);
+
+	/* Dispatch event to transaction. */
+	pj_grp_lock_acquire(tsx->grp_lock);
+	(*tsx->state_handler)(tsx, &event);
+	pj_grp_lock_release(tsx->grp_lock);
+
+	pj_log_pop_indent();
+    }
+}
+
+
+/*
+ * Set transaction state, and inform TU about the transaction state change.
+ */
+static void tsx_set_state( pjsip_transaction *tsx,
+			   pjsip_tsx_state_e state,
+			   pjsip_event_id_e event_src_type,
+                           void *event_src )
+{
+    pjsip_tsx_state_e prev_state = tsx->state;
+
+    /* New state must be greater than previous state */
+    pj_assert(state >= tsx->state);
+
+    PJ_LOG(5, (tsx->obj_name, "State changed from %s to %s, event=%s",
+	       state_str[tsx->state], state_str[state], 
+               pjsip_event_str(event_src_type)));
+    pj_log_push_indent();
+
+    /* Change state. */
+    tsx->state = state;
+
+    /* Update the state handlers. */
+    if (tsx->role == PJSIP_ROLE_UAC) {
+	tsx->state_handler = tsx_state_handler_uac[state];
+    } else {
+	tsx->state_handler = tsx_state_handler_uas[state];
+    }
+
+    /* Before informing TU about state changed, inform TU about
+     * rx event.
+     */
+    if (event_src_type==PJSIP_EVENT_RX_MSG && tsx->tsx_user) {
+	pjsip_rx_data *rdata = (pjsip_rx_data*) event_src;
+
+	pj_assert(rdata != NULL);
+
+	if (rdata->msg_info.msg->type == PJSIP_RESPONSE_MSG &&
+		   tsx->tsx_user->on_rx_response)
+	{
+	    (*tsx->tsx_user->on_rx_response)(rdata);
+	}
+
+    }
+
+    /* Inform TU about state changed. */
+    if (tsx->tsx_user && tsx->tsx_user->on_tsx_state) {
+	pjsip_event e;
+	PJSIP_EVENT_INIT_TSX_STATE(e, tsx, event_src_type, event_src,
+				   prev_state);
+	(*tsx->tsx_user->on_tsx_state)(tsx, &e);
+    }
+    
+
+    /* When the transaction is terminated, release transport, and free the
+     * saved last transmitted message.
+     */
+    if (state == PJSIP_TSX_STATE_TERMINATED) {
+	pj_time_val timeout = {0, 0};
+
+	/* If we're still waiting for a message to be sent.. */
+	if (tsx->transport_flag & TSX_HAS_PENDING_TRANSPORT) {
+	    /* Disassociate ourselves from the outstanding transmit data
+	     * so that when the send callback is called we will be able
+	     * to ignore that (otherwise we'll get assertion, see
+	     * http://trac.pjsip.org/repos/ticket/1033)
+	     */
+	    if (tsx->pending_tx) {
+		tsx->pending_tx->mod_data[mod_tsx_layer.mod.id] = NULL;
+		tsx->pending_tx = NULL;
+	    }
+	    tsx->transport_flag &= ~(TSX_HAS_PENDING_TRANSPORT);
+	}
+
+	lock_timer(tsx);
+	tsx_cancel_timer(tsx, &tsx->timeout_timer);
+	tsx_schedule_timer( tsx, &tsx->timeout_timer, &timeout, TIMEOUT_TIMER);
+	unlock_timer(tsx);
+
+    } else if (state == PJSIP_TSX_STATE_DESTROYED) {
+
+	/* Unregister transaction. */
+	mod_tsx_layer_unregister_tsx(tsx);
+
+	/* Destroy transaction. */
+	tsx_shutdown(tsx);
+    }
+
+    pj_log_pop_indent();
+}
+
+/* Set transaction state without notifying tsx_user */
+static void tsx_set_state_no_notify( pjsip_transaction *tsx,
+                                     pjsip_tsx_state_e state,
+                                     pjsip_event_id_e event_src_type,
+                                     void *event_src )
+{
+    pjsip_module *tsx_user = tsx->tsx_user;
+    tsx->tsx_user = NULL;
+    tsx_set_state(tsx, state, event_src_type, event_src);
+    tsx->tsx_user = tsx_user;
+}
+
+/*
+ * Create, initialize, and register UAC transaction.
+ */
+PJ_DEF(pj_status_t) pjsip_tsx_create_uac( pjsip_module *tsx_user,
+					  pjsip_tx_data *tdata,
+					  pjsip_transaction **p_tsx)
+{
+    return pjsip_tsx_create_uac2(tsx_user, tdata, NULL, p_tsx);
+}
+
+PJ_DEF(pj_status_t) pjsip_tsx_create_uac2(pjsip_module *tsx_user,
+					  pjsip_tx_data *tdata,
+					  pj_grp_lock_t *grp_lock,
+					  pjsip_transaction **p_tsx)
+{
+    pjsip_transaction *tsx;
+    pjsip_msg *msg;
+    pjsip_cseq_hdr *cseq;
+    pjsip_via_hdr *via;
+    pjsip_host_info dst_info;
+    pj_status_t status;
+
+    /* Validate arguments. */
+    PJ_ASSERT_RETURN(tdata && tdata->msg && p_tsx, PJ_EINVAL);
+    PJ_ASSERT_RETURN(tdata->msg->type == PJSIP_REQUEST_MSG,
+		     PJSIP_ENOTREQUESTMSG);
+
+    /* Method MUST NOT be ACK! */
+    PJ_ASSERT_RETURN(tdata->msg->line.req.method.id != PJSIP_ACK_METHOD,
+		     PJ_EINVALIDOP);
+
+    /* Keep shortcut */
+    msg = tdata->msg;
+
+    /* Make sure CSeq header is present. */
+    cseq = (pjsip_cseq_hdr*) pjsip_msg_find_hdr(msg, PJSIP_H_CSEQ, NULL);
+    if (!cseq) {
+	pj_assert(!"CSeq header not present in outgoing message!");
+	return PJSIP_EMISSINGHDR;
+    }
+
+
+    /* Create transaction instance. */
+    status = tsx_create( tsx_user, grp_lock, &tsx);
+    if (status != PJ_SUCCESS)
+	return status;
+
+
+    /* Lock transaction. */
+    pj_grp_lock_acquire(tsx->grp_lock);
+
+    /* Role is UAC. */
+    tsx->role = PJSIP_ROLE_UAC;
+
+    /* Save method. */
+    pjsip_method_copy( tsx->pool, &tsx->method, &msg->line.req.method);
+
+    /* Save CSeq. */
+    tsx->cseq = cseq->cseq;
+
+    /* Generate Via header if it doesn't exist. */
+    via = (pjsip_via_hdr*) pjsip_msg_find_hdr(msg, PJSIP_H_VIA, NULL);
+    if (via == NULL) {
+	via = pjsip_via_hdr_create(tdata->pool);
+	pjsip_msg_insert_first_hdr(msg, (pjsip_hdr*) via);
+    }
+
+    /* Generate branch parameter if it doesn't exist. */
+    if (via->branch_param.slen == 0) {
+	pj_str_t tmp;
+	via->branch_param.ptr = (char*)
+				pj_pool_alloc(tsx->pool, PJSIP_MAX_BRANCH_LEN);
+	via->branch_param.slen = PJSIP_MAX_BRANCH_LEN;
+	pj_memcpy(via->branch_param.ptr, PJSIP_RFC3261_BRANCH_ID, 
+		  PJSIP_RFC3261_BRANCH_LEN);
+	tmp.ptr = via->branch_param.ptr + PJSIP_RFC3261_BRANCH_LEN + 2;
+	*(tmp.ptr-2) = 80; *(tmp.ptr-1) = 106;
+	pj_generate_unique_string( &tmp );
+
+        /* Save branch parameter. */
+        tsx->branch = via->branch_param;
+
+    } else {
+        /* Copy branch parameter. */
+        pj_strdup(tsx->pool, &tsx->branch, &via->branch_param);
+    }
+
+   /* Generate transaction key. */
+    create_tsx_key_3261( tsx->pool, &tsx->transaction_key,
+			 PJSIP_ROLE_UAC, &tsx->method, 
+			 &via->branch_param);
+
+    /* Calculate hashed key value. */
+#ifdef PRECALC_HASH
+    tsx->hashed_key = pj_hash_calc_tolower(0, NULL, &tsx->transaction_key);
+#endif
+
+    PJ_LOG(6, (tsx->obj_name, "tsx_key=%.*s", tsx->transaction_key.slen,
+	       tsx->transaction_key.ptr));
+
+    /* Begin with State_Null.
+     * Manually set-up the state becase we don't want to call the callback.
+     */
+    tsx->state = PJSIP_TSX_STATE_NULL;
+    tsx->state_handler = &tsx_on_state_null;
+
+    /* Save the message. */
+    tsx->last_tx = tdata;
+    pjsip_tx_data_add_ref(tsx->last_tx);
+
+    /* Determine whether reliable transport should be used initially.
+     * This will be updated whenever transport has changed.
+     */
+    status = pjsip_get_request_dest(tdata, &dst_info);
+    if (status != PJ_SUCCESS) {
+	pj_grp_lock_release(tsx->grp_lock);
+	tsx_shutdown(tsx);
+	return status;
+    }
+    tsx->is_reliable = (dst_info.flag & PJSIP_TRANSPORT_RELIABLE);
+
+    /* Register transaction to hash table. */
+    status = mod_tsx_layer_register_tsx(tsx);
+    if (status != PJ_SUCCESS) {
+	/* The assertion is removed by #1090:
+	pj_assert(!"Bug in branch_param generator (i.e. not unique)");
+	*/
+	pj_grp_lock_release(tsx->grp_lock);
+	tsx_shutdown(tsx);
+	return status;
+    }
+
+
+    /* Unlock transaction and return. */
+    pj_grp_lock_release(tsx->grp_lock);
+
+    pj_log_push_indent();
+    PJ_LOG(5,(tsx->obj_name, "Transaction created for %s",
+	      pjsip_tx_data_get_info(tdata)));
+    pj_log_pop_indent();
+
+    *p_tsx = tsx;
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Create, initialize, and register UAS transaction.
+ */
+PJ_DEF(pj_status_t) pjsip_tsx_create_uas( pjsip_module *tsx_user,
+					  pjsip_rx_data *rdata,
+					  pjsip_transaction **p_tsx)
+{
+    return pjsip_tsx_create_uas2(tsx_user, rdata, NULL, p_tsx);
+}
+
+PJ_DEF(pj_status_t) pjsip_tsx_create_uas2(pjsip_module *tsx_user,
+					  pjsip_rx_data *rdata,
+					  pj_grp_lock_t *grp_lock,
+					  pjsip_transaction **p_tsx)
+{
+    pjsip_transaction *tsx;
+    pjsip_msg *msg;
+    pj_str_t *branch;
+    pjsip_cseq_hdr *cseq;
+    pj_status_t status;
+
+    /* Validate arguments. */
+    PJ_ASSERT_RETURN(rdata && rdata->msg_info.msg && p_tsx, PJ_EINVAL);
+
+    /* Keep shortcut to message */
+    msg = rdata->msg_info.msg;
+    
+    /* Make sure this is a request message. */
+    PJ_ASSERT_RETURN(msg->type == PJSIP_REQUEST_MSG, PJSIP_ENOTREQUESTMSG);
+
+    /* Make sure method is not ACK */
+    PJ_ASSERT_RETURN(msg->line.req.method.id != PJSIP_ACK_METHOD,
+		     PJ_EINVALIDOP);
+
+    /* Make sure CSeq header is present. */
+    cseq = rdata->msg_info.cseq;
+    if (!cseq)
+	return PJSIP_EMISSINGHDR;
+
+    /* Make sure Via header is present. */
+    if (rdata->msg_info.via == NULL)
+	return PJSIP_EMISSINGHDR;
+
+    /* Check that method in CSeq header match request method.
+     * Reference: PROTOS #1922
+     */
+    if (pjsip_method_cmp(&msg->line.req.method, 
+			 &rdata->msg_info.cseq->method) != 0)
+    {
+	PJ_LOG(4,(THIS_FILE, "Error: CSeq header contains different "
+			     "method than the request line"));
+	return PJSIP_EINVALIDHDR;
+    }
+
+    /* 
+     * Create transaction instance. 
+     */
+    status = tsx_create( tsx_user, grp_lock, &tsx);
+    if (status != PJ_SUCCESS)
+	return status;
+
+
+    /* Lock transaction. */
+    pj_grp_lock_acquire(tsx->grp_lock);
+
+    /* Role is UAS */
+    tsx->role = PJSIP_ROLE_UAS;
+
+    /* Save method. */
+    pjsip_method_copy( tsx->pool, &tsx->method, &msg->line.req.method);
+
+    /* Save CSeq */
+    tsx->cseq = cseq->cseq;
+
+    /* Get transaction key either from branch for RFC3261 message, or
+     * create transaction key.
+     */
+    status = pjsip_tsx_create_key(tsx->pool, &tsx->transaction_key, 
+                                  PJSIP_ROLE_UAS, &tsx->method, rdata);
+    if (status != PJ_SUCCESS) {
+	pj_grp_lock_release(tsx->grp_lock);
+	tsx_shutdown(tsx);
+        return status;
+    }
+
+    /* Calculate hashed key value. */
+#ifdef PRECALC_HASH
+    tsx->hashed_key = pj_hash_calc_tolower(0, NULL, &tsx->transaction_key);
+#endif
+
+    /* Duplicate branch parameter for transaction. */
+    branch = &rdata->msg_info.via->branch_param;
+    pj_strdup(tsx->pool, &tsx->branch, branch);
+
+    PJ_LOG(6, (tsx->obj_name, "tsx_key=%.*s", tsx->transaction_key.slen,
+	       tsx->transaction_key.ptr));
+
+
+    /* Begin with state NULL.
+     * Manually set-up the state becase we don't want to call the callback.
+     */
+    tsx->state = PJSIP_TSX_STATE_NULL; 
+    tsx->state_handler = &tsx_on_state_null;
+
+    /* Get response address. */
+    status = pjsip_get_response_addr( tsx->pool, rdata, &tsx->res_addr );
+    if (status != PJ_SUCCESS) {
+	pj_grp_lock_release(tsx->grp_lock);
+	tsx_shutdown(tsx);
+	return status;
+    }
+
+    /* If it's decided that we should use current transport, keep the
+     * transport.
+     */
+    if (tsx->res_addr.transport) {
+	tsx_update_transport(tsx, tsx->res_addr.transport);
+	pj_memcpy(&tsx->addr, &tsx->res_addr.addr, tsx->res_addr.addr_len);
+	tsx->addr_len = tsx->res_addr.addr_len;
+	tsx->is_reliable = PJSIP_TRANSPORT_IS_RELIABLE(tsx->transport);
+    } else {
+	tsx->is_reliable = 
+	    (tsx->res_addr.dst_host.flag & PJSIP_TRANSPORT_RELIABLE);
+    }
+
+
+    /* Register the transaction. */
+    status = mod_tsx_layer_register_tsx(tsx);
+    if (status != PJ_SUCCESS) {
+	pj_grp_lock_release(tsx->grp_lock);
+	tsx_shutdown(tsx);
+	return status;
+    }
+
+    /* Put this transaction in rdata's mod_data. */
+    rdata->endpt_info.mod_data[mod_tsx_layer.mod.id] = tsx;
+
+    /* Unlock transaction and return. */
+    pj_grp_lock_release(tsx->grp_lock);
+
+    pj_log_push_indent();
+    PJ_LOG(5,(tsx->obj_name, "Transaction created for %s",
+	      pjsip_rx_data_get_info(rdata)));
+    pj_log_pop_indent();
+
+
+    *p_tsx = tsx;
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Bind transaction to a specific transport/listener. 
+ */
+PJ_DEF(pj_status_t) pjsip_tsx_set_transport(pjsip_transaction *tsx,
+					    const pjsip_tpselector *sel)
+{
+    /* Must be UAC transaction */
+    PJ_ASSERT_RETURN(tsx && sel, PJ_EINVAL);
+
+    /* Start locking the transaction. */
+    pj_grp_lock_acquire(tsx->grp_lock);
+
+    /* Decrement reference counter of previous transport selector */
+    pjsip_tpselector_dec_ref(&tsx->tp_sel);
+
+    /* Copy transport selector structure .*/
+    pj_memcpy(&tsx->tp_sel, sel, sizeof(*sel));
+
+    /* Increment reference counter */
+    pjsip_tpselector_add_ref(&tsx->tp_sel);
+
+    /* Unlock transaction. */
+    pj_grp_lock_release(tsx->grp_lock);
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Set transaction status code and reason.
+ */
+static void tsx_set_status_code(pjsip_transaction *tsx,
+			   	int code, const pj_str_t *reason)
+{
+    tsx->status_code = code;
+    if (reason)
+	pj_strdup(tsx->pool, &tsx->status_text, reason);
+    else
+	tsx->status_text = *pjsip_get_status_text(code);
+}
+
+
+/*
+ * Forcely terminate transaction.
+ */
+PJ_DEF(pj_status_t) pjsip_tsx_terminate( pjsip_transaction *tsx, int code )
+{
+    PJ_ASSERT_RETURN(tsx != NULL, PJ_EINVAL);
+
+    PJ_LOG(5,(tsx->obj_name, "Request to terminate transaction"));
+
+    PJ_ASSERT_RETURN(code >= 200, PJ_EINVAL);
+
+    if (tsx->state >= PJSIP_TSX_STATE_TERMINATED)
+	return PJ_SUCCESS;
+
+    pj_log_push_indent();
+
+    pj_grp_lock_acquire(tsx->grp_lock);
+    tsx_set_status_code(tsx, code, NULL);
+    tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, PJSIP_EVENT_USER, NULL);
+    pj_grp_lock_release(tsx->grp_lock);
+
+    pj_log_pop_indent();
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Cease retransmission on the UAC transaction. The UAC transaction is
+ * still considered running, and it will complete when either final
+ * response is received or the transaction times out.
+ */
+PJ_DEF(pj_status_t) pjsip_tsx_stop_retransmit(pjsip_transaction *tsx)
+{
+    PJ_ASSERT_RETURN(tsx != NULL, PJ_EINVAL);
+    PJ_ASSERT_RETURN(tsx->role == PJSIP_ROLE_UAC &&
+		     tsx->method.id == PJSIP_INVITE_METHOD,
+		     PJ_EINVALIDOP);
+
+    PJ_LOG(5,(tsx->obj_name, "Request to stop retransmission"));
+
+    pj_log_push_indent();
+
+    pj_grp_lock_acquire(tsx->grp_lock);
+    /* Cancel retransmission timer. */
+    tsx_cancel_timer(tsx, &tsx->retransmit_timer);
+    pj_grp_lock_release(tsx->grp_lock);
+
+    pj_log_pop_indent();
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Start a timer to terminate transaction after the specified time
+ * has elapsed. 
+ */
+PJ_DEF(pj_status_t) pjsip_tsx_set_timeout( pjsip_transaction *tsx,
+					   unsigned millisec)
+{
+    pj_time_val timeout;
+
+    PJ_ASSERT_RETURN(tsx != NULL, PJ_EINVAL);
+    PJ_ASSERT_RETURN(tsx->role == PJSIP_ROLE_UAC &&
+		     tsx->method.id == PJSIP_INVITE_METHOD,
+		     PJ_EINVALIDOP);
+
+    /* Note: must not call pj_grp_lock_acquire(tsx->grp_lock) as
+     * that would introduce deadlock. See #1121.
+     */
+    lock_timer(tsx);
+
+    /* Transaction should normally not have final response, but as
+     * #1121 says there is a (tolerable) window of race condition
+     * where this might happen.
+     */
+    if (tsx->status_code >= 200 && tsx->timeout_timer.id != 0) {
+	/* Timeout is already set */
+	unlock_timer(tsx);
+	return PJ_EEXISTS;
+    }
+
+    tsx_cancel_timer(tsx, &tsx->timeout_timer);
+
+    timeout.sec = 0;
+    timeout.msec = millisec;
+    pj_time_val_normalize(&timeout);
+
+    tsx_schedule_timer(tsx, &tsx->timeout_timer, &timeout, TIMEOUT_TIMER);
+
+    unlock_timer(tsx);
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * This function is called by TU to send a message.
+ */
+PJ_DEF(pj_status_t) pjsip_tsx_send_msg( pjsip_transaction *tsx, 
+				        pjsip_tx_data *tdata )
+{
+    pjsip_event event;
+    pj_status_t status;
+
+    if (tdata == NULL)
+	tdata = tsx->last_tx;
+
+    PJ_ASSERT_RETURN(tdata != NULL, PJ_EINVALIDOP);
+
+    PJ_LOG(5,(tsx->obj_name, "Sending %s in state %s",
+                             pjsip_tx_data_get_info(tdata),
+			     state_str[tsx->state]));
+    pj_log_push_indent();
+
+    PJSIP_EVENT_INIT_TX_MSG(event, tdata);
+
+    /* Dispatch to transaction. */
+    pj_grp_lock_acquire(tsx->grp_lock);
+
+    /* Set transport selector to tdata */
+    pjsip_tx_data_set_transport(tdata, &tsx->tp_sel);
+
+    /* Dispatch to state handler */
+    status = (*tsx->state_handler)(tsx, &event);
+
+    pj_grp_lock_release(tsx->grp_lock);
+
+    /* Only decrement reference counter when it returns success.
+     * (This is the specification from the .PDF design document).
+     */
+    if (status == PJ_SUCCESS) {
+	pjsip_tx_data_dec_ref(tdata);
+    }
+
+    pj_log_pop_indent();
+
+    return status;
+}
+
+
+/*
+ * This function is called by endpoint when incoming message for the 
+ * transaction is received.
+ */
+PJ_DEF(void) pjsip_tsx_recv_msg( pjsip_transaction *tsx, 
+				 pjsip_rx_data *rdata)
+{
+    pjsip_event event;
+
+    PJ_LOG(5,(tsx->obj_name, "Incoming %s in state %s", 
+	      pjsip_rx_data_get_info(rdata), state_str[tsx->state]));
+    pj_log_push_indent();
+
+    /* Put the transaction in the rdata's mod_data. */
+    rdata->endpt_info.mod_data[mod_tsx_layer.mod.id] = tsx;
+
+    /* Init event. */
+    PJSIP_EVENT_INIT_RX_MSG(event, rdata);
+
+    /* Dispatch to transaction. */
+    pj_grp_lock_acquire(tsx->grp_lock);
+    (*tsx->state_handler)(tsx, &event);
+    pj_grp_lock_release(tsx->grp_lock);
+
+    pj_log_pop_indent();
+}
+
+
+/* Callback called by send message framework */
+static void send_msg_callback( pjsip_send_state *send_state,
+			       pj_ssize_t sent, pj_bool_t *cont )
+{
+    pjsip_transaction *tsx = (pjsip_transaction*) send_state->token;
+    pjsip_tx_data *tdata = send_state->tdata;
+
+    /* Check if transaction has cancelled itself from this transmit
+     * notification (https://trac.pjsip.org/repos/ticket/1033).
+     * Also check if the transaction layer itself may have been shutdown
+     * (https://trac.pjsip.org/repos/ticket/1535)
+     */
+    if (mod_tsx_layer.mod.id < 0 ||
+	tdata->mod_data[mod_tsx_layer.mod.id] == NULL)
+    {
+	*cont = PJ_FALSE;
+	return;
+    }
+
+    pj_grp_lock_acquire(tsx->grp_lock);
+
+    /* Reset */
+    tdata->mod_data[mod_tsx_layer.mod.id] = NULL;
+    tsx->pending_tx = NULL;
+
+    if (sent > 0) {
+	/* Successfully sent! */
+	pj_assert(send_state->cur_transport != NULL);
+
+	if (tsx->transport != send_state->cur_transport) {
+	    /* Update transport. */
+	    tsx_update_transport(tsx, send_state->cur_transport);
+
+	    /* Update remote address. */
+	    tsx->addr_len = tdata->dest_info.addr.entry[tdata->dest_info.cur_addr].addr_len;
+	    pj_memcpy(&tsx->addr, 
+		      &tdata->dest_info.addr.entry[tdata->dest_info.cur_addr].addr,
+		      tsx->addr_len);
+
+	    /* Update is_reliable flag. */
+	    tsx->is_reliable = PJSIP_TRANSPORT_IS_RELIABLE(tsx->transport);
+	}
+
+	/* Clear pending transport flag. */
+	tsx->transport_flag &= ~(TSX_HAS_PENDING_TRANSPORT);
+
+	/* Mark that we have resolved the addresses. */
+	tsx->transport_flag |= TSX_HAS_RESOLVED_SERVER;
+
+	/* Pending destroy? */
+	if (tsx->transport_flag & TSX_HAS_PENDING_DESTROY) {
+	    tsx_set_state( tsx, PJSIP_TSX_STATE_DESTROYED, 
+			   PJSIP_EVENT_UNKNOWN, NULL );
+	    pj_grp_lock_release(tsx->grp_lock);
+	    return;
+	}
+
+	/* Need to transmit a message? */
+	if (tsx->transport_flag & TSX_HAS_PENDING_SEND) {
+	    tsx->transport_flag &= ~(TSX_HAS_PENDING_SEND);
+	    tsx_send_msg(tsx, tsx->last_tx);
+	}
+
+	/* Need to reschedule retransmission? */
+	if (tsx->transport_flag & TSX_HAS_PENDING_RESCHED) {
+	    tsx->transport_flag &= ~(TSX_HAS_PENDING_RESCHED);
+
+	    /* Only update when transport turns out to be unreliable. */
+	    if (!tsx->is_reliable) {
+		tsx_resched_retransmission(tsx);
+	    }
+	}
+
+    } else {
+	/* Failed to send! */
+	pj_assert(sent != 0);
+
+	/* If transaction is using the same transport as the failed one, 
+	 * release the transport.
+	 */
+	if (send_state->cur_transport==tsx->transport)
+	    tsx_update_transport(tsx, NULL);
+
+	/* Also stop processing if transaction has been flagged with
+	 * pending destroy (http://trac.pjsip.org/repos/ticket/906)
+	 */
+	if ((!*cont) || (tsx->transport_flag & TSX_HAS_PENDING_DESTROY)) {
+	    char errmsg[PJ_ERR_MSG_SIZE];
+	    pjsip_status_code sc;
+	    pj_str_t err;
+
+	    tsx->transport_err = (pj_status_t)-sent;
+
+	    err =pj_strerror((pj_status_t)-sent, errmsg, sizeof(errmsg));
+
+	    PJ_LOG(2,(tsx->obj_name,
+		      "Failed to send %s! err=%d (%s)",
+		      pjsip_tx_data_get_info(send_state->tdata), -sent,
+		      errmsg));
+
+	    /* Clear pending transport flag. */
+	    tsx->transport_flag &= ~(TSX_HAS_PENDING_TRANSPORT);
+
+	    /* Mark that we have resolved the addresses. */
+	    tsx->transport_flag |= TSX_HAS_RESOLVED_SERVER;
+
+	    /* Server resolution error is now mapped to 502 instead of 503,
+	     * since with 503 normally client should try again.
+	     * See http://trac.pjsip.org/repos/ticket/870
+	     */
+	    if (-sent==PJ_ERESOLVE || -sent==PJLIB_UTIL_EDNS_NXDOMAIN)
+		sc = PJSIP_SC_BAD_GATEWAY;
+	    else
+		sc = PJSIP_SC_TSX_TRANSPORT_ERROR;
+
+	    /* Terminate transaction, if it's not already terminated. */
+	    tsx_set_status_code(tsx, sc, &err);
+	    if (tsx->state != PJSIP_TSX_STATE_TERMINATED &&
+		tsx->state != PJSIP_TSX_STATE_DESTROYED)
+	    {
+		tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, 
+			       PJSIP_EVENT_TRANSPORT_ERROR, send_state->tdata);
+	    } 
+	    /* Don't forget to destroy if we have pending destroy flag
+	     * (http://trac.pjsip.org/repos/ticket/906)
+	     */
+	    else if (tsx->transport_flag & TSX_HAS_PENDING_DESTROY)
+	    {
+		tsx_set_state( tsx, PJSIP_TSX_STATE_DESTROYED, 
+			       PJSIP_EVENT_TRANSPORT_ERROR, send_state->tdata);
+	    }
+
+	} else {
+	    PJ_PERROR(2,(tsx->obj_name, (pj_status_t)-sent,
+		         "Temporary failure in sending %s, "
+		         "will try next server",
+		         pjsip_tx_data_get_info(send_state->tdata)));
+
+	    /* Reset retransmission count */
+	    tsx->retransmit_count = 0;
+
+	    /* And reset timeout timer */
+	    if (tsx->timeout_timer.id) {
+		lock_timer(tsx);
+		tsx_cancel_timer(tsx, &tsx->timeout_timer);
+		tsx_schedule_timer( tsx, &tsx->timeout_timer,
+				    &timeout_timer_val, TIMEOUT_TIMER);
+		unlock_timer(tsx);
+	    }
+
+	    /* Put again pending tdata */
+	    tdata->mod_data[mod_tsx_layer.mod.id] = tsx;
+	    tsx->pending_tx = tdata;
+	}
+    }
+
+    pj_grp_lock_release(tsx->grp_lock);
+}
+
+
+/* Transport callback. */
+static void transport_callback(void *token, pjsip_tx_data *tdata,
+			       pj_ssize_t sent)
+{
+    pjsip_transaction *tsx = (pjsip_transaction*) token;
+
+    /* In other circumstances, locking tsx->grp_lock AFTER transport mutex
+     * will introduce deadlock if another thread is currently sending a
+     * SIP message to the transport. But this should be safe as there should
+     * be no way this callback could be called while another thread is
+     * sending a message.
+     */
+    pj_grp_lock_acquire(tsx->grp_lock);
+    tsx->transport_flag &= ~(TSX_HAS_PENDING_TRANSPORT);
+    pj_grp_lock_release(tsx->grp_lock);
+
+    if (sent < 0) {
+	pj_time_val delay = {0, 0};
+	char errmsg[PJ_ERR_MSG_SIZE];
+
+	pj_strerror((pj_status_t)-sent, errmsg, sizeof(errmsg));
+
+	PJ_LOG(2,(tsx->obj_name, "Transport failed to send %s! Err=%d (%s)",
+		pjsip_tx_data_get_info(tdata), -sent, errmsg));
+
+	/* Post the event for later processing, to avoid deadlock.
+	 * See https://trac.pjsip.org/repos/ticket/1646
+	 */
+	lock_timer(tsx);
+	tsx->transport_err = (pj_status_t)-sent;
+	tsx_cancel_timer(tsx, &tsx->timeout_timer);
+	tsx_schedule_timer(tsx, &tsx->timeout_timer, &delay,
+	                   TRANSPORT_ERR_TIMER);
+	unlock_timer(tsx);
+   }
+
+    /* Decrease pending send counter */
+    pj_grp_lock_dec_ref(tsx->grp_lock);
+}
+
+
+/*
+ * Callback when transport state changes.
+ */
+static void tsx_tp_state_callback( pjsip_transport *tp,
+				   pjsip_transport_state state,
+				   const pjsip_transport_state_info *info)
+{
+    PJ_UNUSED_ARG(tp);
+
+    if (state == PJSIP_TP_STATE_DISCONNECTED) {
+	pjsip_transaction *tsx;
+	pj_time_val delay = {0, 0};
+
+	pj_assert(tp && info && info->user_data);
+
+	tsx = (pjsip_transaction*)info->user_data;
+
+	/* Post the event for later processing, to avoid deadlock.
+	 * See https://trac.pjsip.org/repos/ticket/1646
+	 */
+	lock_timer(tsx);
+	tsx->transport_err = info->status;
+	tsx_cancel_timer(tsx, &tsx->timeout_timer);
+	tsx_schedule_timer(tsx, &tsx->timeout_timer, &delay,
+	                   TRANSPORT_ERR_TIMER);
+	unlock_timer(tsx);
+    }
+}
+
+
+/*
+ * Send message to the transport.
+ */
+static pj_status_t tsx_send_msg( pjsip_transaction *tsx, 
+                                 pjsip_tx_data *tdata)
+{
+    pj_status_t status = PJ_SUCCESS;
+
+    PJ_ASSERT_RETURN(tsx && tdata, PJ_EINVAL);
+
+    /* Send later if transport is still pending. */
+    if (tsx->transport_flag & TSX_HAS_PENDING_TRANSPORT) {
+	tsx->transport_flag |= TSX_HAS_PENDING_SEND;
+	return PJ_SUCCESS;
+    }
+
+    /* Skip send if previous tdata transmission is pending (see #1665). */
+    if (tdata->is_pending) {
+	PJ_LOG(2,(THIS_FILE, "Unable to send %s: message is pending", 
+			     pjsip_tx_data_get_info(tdata)));
+	return PJ_SUCCESS;
+    }
+
+    /* If we have the transport, send the message using that transport.
+     * Otherwise perform full transport resolution.
+     */
+    if (tsx->transport) {
+	/* Increment group lock while waiting for send operation to complete,
+	 * to prevent us from being destroyed prematurely. See
+	 * https://trac.pjsip.org/repos/ticket/1646
+	 */
+	pj_grp_lock_add_ref(tsx->grp_lock);
+	tsx->transport_flag |= TSX_HAS_PENDING_TRANSPORT;
+
+	status = pjsip_transport_send( tsx->transport, tdata, &tsx->addr,
+				       tsx->addr_len, tsx, 
+				       &transport_callback);
+	if (status == PJ_EPENDING)
+	    status = PJ_SUCCESS;
+	else {
+	    /* Operation completes immediately */
+	    tsx->transport_flag &= ~(TSX_HAS_PENDING_TRANSPORT);
+	    pj_grp_lock_dec_ref(tsx->grp_lock);
+	}
+
+	if (status != PJ_SUCCESS) {
+	    PJ_PERROR(2,(tsx->obj_name, status,
+		         "Error sending %s",
+		         pjsip_tx_data_get_info(tdata)));
+
+	    /* On error, release transport to force using full transport
+	     * resolution procedure.
+	     */
+	    tsx_update_transport(tsx, NULL);
+
+	    tsx->addr_len = 0;
+	    tsx->res_addr.transport = NULL;
+	    tsx->res_addr.addr_len = 0;
+	} else {
+	    return PJ_SUCCESS;
+	}
+    }
+
+    /* We are here because we don't have transport, or we failed to send
+     * the message using existing transport. If we haven't resolved the
+     * server before, then begin the long process of resolving the server
+     * and send the message with possibly new server.
+     */
+    pj_assert(status != PJ_SUCCESS || tsx->transport == NULL);
+
+    /* If we have resolved the server, we treat the error as permanent error.
+     * Terminate transaction with transport error failure.
+     */
+    if (tsx->transport_flag & TSX_HAS_RESOLVED_SERVER) {
+	
+	char errmsg[PJ_ERR_MSG_SIZE];
+	pj_str_t err;
+
+	if (status == PJ_SUCCESS) {
+	    pj_assert(!"Unexpected status!");
+	    status = PJ_EUNKNOWN;
+	}
+
+	/* We have resolved the server!.
+	 * Treat this as permanent transport error.
+	 */
+	err = pj_strerror(status, errmsg, sizeof(errmsg));
+
+	PJ_LOG(2,(tsx->obj_name, 
+		  "Transport error, terminating transaction. "
+		  "Err=%d (%s)",
+		  status, errmsg));
+
+	tsx_set_status_code(tsx, PJSIP_SC_TSX_TRANSPORT_ERROR, &err);
+	tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, 
+		       PJSIP_EVENT_TRANSPORT_ERROR, NULL );
+
+	return status;
+    }
+
+    /* Must add reference counter because the send request functions
+     * decrement the reference counter.
+     */
+    pjsip_tx_data_add_ref(tdata);
+
+    /* Also attach ourselves to the transmit data so that we'll be able
+     * to unregister ourselves from the send notification of this
+     * transmit data.
+     */
+    tdata->mod_data[mod_tsx_layer.mod.id] = tsx;
+    tsx->pending_tx = tdata;
+
+    /* Begin resolving destination etc to send the message. */
+    if (tdata->msg->type == PJSIP_REQUEST_MSG) {
+
+	tsx->transport_flag |= TSX_HAS_PENDING_TRANSPORT;
+	status = pjsip_endpt_send_request_stateless(tsx->endpt, tdata, tsx,
+						    &send_msg_callback);
+	if (status == PJ_EPENDING)
+	    status = PJ_SUCCESS;
+	if (status != PJ_SUCCESS) {
+	    pjsip_tx_data_dec_ref(tdata);
+	    tdata->mod_data[mod_tsx_layer.mod.id] = NULL;
+	    tsx->pending_tx = NULL;
+	}
+	
+	/* Check if transaction is terminated. */
+	if (status==PJ_SUCCESS && tsx->state == PJSIP_TSX_STATE_TERMINATED)
+	    status = tsx->transport_err;
+
+    } else {
+
+	tsx->transport_flag |= TSX_HAS_PENDING_TRANSPORT;
+	status = pjsip_endpt_send_response( tsx->endpt, &tsx->res_addr, 
+					    tdata, tsx, 
+					    &send_msg_callback);
+	if (status == PJ_EPENDING)
+	    status = PJ_SUCCESS;
+	if (status != PJ_SUCCESS) {
+	    pjsip_tx_data_dec_ref(tdata);
+	    tdata->mod_data[mod_tsx_layer.mod.id] = NULL;
+	    tsx->pending_tx = NULL;
+	}
+
+	/* Check if transaction is terminated. */
+	if (status==PJ_SUCCESS && tsx->state == PJSIP_TSX_STATE_TERMINATED)
+	    status = tsx->transport_err;
+
+    }
+
+
+    return status;
+}
+
+
+/*
+ * Manually retransmit the last messagewithout updating the transaction state.
+ */
+PJ_DEF(pj_status_t) pjsip_tsx_retransmit_no_state(pjsip_transaction *tsx,
+						  pjsip_tx_data *tdata)
+{
+    pj_status_t status;
+
+    pj_grp_lock_acquire(tsx->grp_lock);
+    if (tdata == NULL) {
+	tdata = tsx->last_tx;
+    }
+    status = tsx_send_msg(tsx, tdata);
+    pj_grp_lock_release(tsx->grp_lock);
+
+    /* Only decrement reference counter when it returns success.
+     * (This is the specification from the .PDF design document).
+     */
+    if (status == PJ_SUCCESS) {
+	pjsip_tx_data_dec_ref(tdata);
+    }
+
+    return status;
+}
+
+
+/*
+ * Retransmit last message sent.
+ */
+static void tsx_resched_retransmission( pjsip_transaction *tsx )
+{
+    pj_uint32_t msec_time;
+
+    pj_assert((tsx->transport_flag & TSX_HAS_PENDING_TRANSPORT) == 0);
+
+    if (tsx->role==PJSIP_ROLE_UAC && tsx->status_code >= 100)
+	msec_time = pjsip_cfg()->tsx.t2;
+    else
+	msec_time = (1 << (tsx->retransmit_count)) * pjsip_cfg()->tsx.t1;
+
+    if (tsx->role == PJSIP_ROLE_UAC) {
+	pj_assert(tsx->status_code < 200);
+	/* Retransmission for non-INVITE transaction caps-off at T2 */
+	if (msec_time > pjsip_cfg()->tsx.t2 && 
+	    tsx->method.id != PJSIP_INVITE_METHOD)
+	{
+	    msec_time = pjsip_cfg()->tsx.t2;
+	}
+    } else {
+	/* For UAS, this can be retransmission of 2xx response for INVITE
+	 * or non-100 1xx response.
+	 */
+	if (tsx->status_code < 200) {
+	    /* non-100 1xx retransmission is at 60 seconds */
+	    msec_time = PJSIP_TSX_1XX_RETRANS_DELAY * 1000;
+	} else {
+	    /* Retransmission of INVITE final response also caps-off at T2 */
+	    pj_assert(tsx->status_code >= 200);
+	    if (msec_time > pjsip_cfg()->tsx.t2)
+		msec_time = pjsip_cfg()->tsx.t2;
+	}
+    }
+
+    if (msec_time != 0) {
+	pj_time_val timeout;
+
+	timeout.sec = msec_time / 1000;
+	timeout.msec = msec_time % 1000;
+	tsx_schedule_timer( tsx, &tsx->retransmit_timer, &timeout,
+	                    RETRANSMIT_TIMER);
+    }
+}
+
+/*
+ * Retransmit last message sent.
+ */
+static pj_status_t tsx_retransmit( pjsip_transaction *tsx, int resched)
+{
+    pj_status_t status;
+
+    if (resched && pj_timer_entry_running(&tsx->retransmit_timer)) {
+	/* We've been asked to reschedule but the timer is already rerunning.
+	 * This can only happen in a race condition where, between removing
+	 * this retransmit timer from the heap and actually scheduling it,
+	 * another thread has got in and rescheduled the timer itself.  In
+	 * this scenario, the transmission has already happened and so we
+	 * should just quit out immediately, without either resending the
+	 * message or restarting the timer.
+	 */
+	return PJ_SUCCESS;
+    }
+
+    PJ_ASSERT_RETURN(tsx->last_tx!=NULL, PJ_EBUG);
+
+    PJ_LOG(5,(tsx->obj_name, "Retransmiting %s, count=%d, restart?=%d", 
+	      pjsip_tx_data_get_info(tsx->last_tx), 
+	      tsx->retransmit_count, resched));
+
+    ++tsx->retransmit_count;
+
+    /* Restart timer T1 first before sending the message to ensure that
+     * retransmission timer is not engaged when loop transport is used.
+     */
+    if (resched) {
+	pj_assert(tsx->state != PJSIP_TSX_STATE_CONFIRMED);
+	if (tsx->transport_flag & TSX_HAS_PENDING_TRANSPORT) {
+	    tsx->transport_flag |= TSX_HAS_PENDING_RESCHED;
+	} else {
+	    tsx_resched_retransmission(tsx);
+	}
+    }
+
+    status = tsx_send_msg( tsx, tsx->last_tx);
+    if (status != PJ_SUCCESS) {
+	return status;
+    }
+
+    return PJ_SUCCESS;
+}
+
+static void tsx_update_transport( pjsip_transaction *tsx, 
+				  pjsip_transport *tp)
+{
+    pj_assert(tsx);
+
+    if (tsx->transport) {
+	pjsip_transport_remove_state_listener(tsx->transport, 
+					       tsx->tp_st_key, tsx);
+	pjsip_transport_dec_ref( tsx->transport );
+	tsx->transport = NULL;
+    }
+
+    if (tp) {
+	tsx->transport = tp;
+	pjsip_transport_add_ref(tp);
+	pjsip_transport_add_state_listener(tp, &tsx_tp_state_callback, tsx,
+					    &tsx->tp_st_key);
+        if (tp->is_shutdown) {
+	    pjsip_transport_state_info info;
+
+	    pj_bzero(&info, sizeof(info));
+            info.user_data = tsx;
+            info.status = PJSIP_SC_TSX_TRANSPORT_ERROR;
+            tsx_tp_state_callback(tp, PJSIP_TP_STATE_DISCONNECTED, &info);
+        }
+    }
+}
+
+/*
+ * Handler for events in state Null.
+ */
+static pj_status_t tsx_on_state_null( pjsip_transaction *tsx, 
+                                      pjsip_event *event )
+{
+    pj_status_t status;
+
+    pj_assert(tsx->state == PJSIP_TSX_STATE_NULL);
+
+    if (tsx->role == PJSIP_ROLE_UAS) {
+
+	/* Set state to Trying. */
+	pj_assert(event->type == PJSIP_EVENT_RX_MSG &&
+		  event->body.rx_msg.rdata->msg_info.msg->type == 
+		    PJSIP_REQUEST_MSG);
+	tsx_set_state( tsx, PJSIP_TSX_STATE_TRYING, PJSIP_EVENT_RX_MSG,
+		       event->body.rx_msg.rdata);
+
+    } else {
+	pjsip_tx_data *tdata;
+
+	/* Must be transmit event. 
+	 * You may got this assertion when using loop transport with delay 
+	 * set to zero. That would cause on_rx_response() callback to be 
+	 * called before tsx_send_msg() has completed.
+	 */
+	PJ_ASSERT_RETURN(event->type == PJSIP_EVENT_TX_MSG, PJ_EBUG);
+
+	/* Get the txdata */
+	tdata = event->body.tx_msg.tdata;
+
+	/* Save the message for retransmission. */
+	if (tsx->last_tx && tsx->last_tx != tdata) {
+	    pjsip_tx_data_dec_ref(tsx->last_tx);
+	    tsx->last_tx = NULL;
+	}
+	if (tsx->last_tx != tdata) {
+	    tsx->last_tx = tdata;
+	    pjsip_tx_data_add_ref(tdata);
+	}
+
+	/* Send the message. */
+        status = tsx_send_msg( tsx, tdata);
+	if (status != PJ_SUCCESS) {
+	    return status;
+	}
+
+	/* Start Timer B (or called timer F for non-INVITE) for transaction 
+	 * timeout.
+	 */
+	lock_timer(tsx);
+	tsx_cancel_timer( tsx, &tsx->timeout_timer );
+	tsx_schedule_timer( tsx, &tsx->timeout_timer, &timeout_timer_val,
+	                    TIMEOUT_TIMER);
+	unlock_timer(tsx);
+
+	/* Start Timer A (or timer E) for retransmission only if unreliable 
+	 * transport is being used.
+	 */
+	if (!tsx->is_reliable)  {
+	    tsx->retransmit_count = 0;
+	    if (tsx->transport_flag & TSX_HAS_PENDING_TRANSPORT) {
+		tsx->transport_flag |= TSX_HAS_PENDING_RESCHED;
+	    } else {
+		tsx_schedule_timer(tsx, &tsx->retransmit_timer,
+		                   &t1_timer_val, RETRANSMIT_TIMER);
+	    }
+	}
+
+	/* Move state. */
+	tsx_set_state( tsx, PJSIP_TSX_STATE_CALLING, 
+                       PJSIP_EVENT_TX_MSG, tdata);
+    }
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * State Calling is for UAC after it sends request but before any responses
+ * is received.
+ */
+static pj_status_t tsx_on_state_calling( pjsip_transaction *tsx, 
+				         pjsip_event *event )
+{
+    pj_assert(tsx->state == PJSIP_TSX_STATE_CALLING);
+    pj_assert(tsx->role == PJSIP_ROLE_UAC);
+
+    if (event->type == PJSIP_EVENT_TIMER && 
+	event->body.timer.entry == &tsx->retransmit_timer) 
+    {
+        pj_status_t status;
+
+	/* Retransmit the request. */
+        status = tsx_retransmit( tsx, 1 );
+	if (status != PJ_SUCCESS) {
+	    return status;
+	}
+
+    } else if (event->type == PJSIP_EVENT_TIMER && 
+	       event->body.timer.entry == &tsx->timeout_timer) 
+    {
+	/* Cancel retransmission timer. */
+	tsx_cancel_timer(tsx, &tsx->retransmit_timer);
+
+	tsx->transport_flag &= ~(TSX_HAS_PENDING_RESCHED);
+
+	/* Set status code */
+	tsx_set_status_code(tsx, PJSIP_SC_TSX_TIMEOUT, NULL);
+
+	/* Inform TU. */
+	tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED,
+                       PJSIP_EVENT_TIMER, &tsx->timeout_timer);
+
+	/* Transaction is destroyed */
+	//return PJSIP_ETSXDESTROYED;
+
+    } else if (event->type == PJSIP_EVENT_RX_MSG) {
+	pjsip_msg *msg;
+	int code;
+
+	/* Get message instance */
+	msg = event->body.rx_msg.rdata->msg_info.msg;
+
+	/* Better be a response message. */
+	if (msg->type != PJSIP_RESPONSE_MSG)
+	    return PJSIP_ENOTRESPONSEMSG;
+
+	code = msg->line.status.code;
+
+	/* If the response is final, cancel both retransmission and timeout
+	 * timer.
+	 */
+	if (code >= 200) {
+	    tsx_cancel_timer(tsx, &tsx->retransmit_timer);
+
+	    if (tsx->timeout_timer.id != 0) {
+		lock_timer(tsx);
+		tsx_cancel_timer(tsx, &tsx->timeout_timer);
+		unlock_timer(tsx);
+	    }
+
+	} else {
+	    /* Cancel retransmit timer (for non-INVITE transaction, the
+	     * retransmit timer will be rescheduled at T2.
+	     */
+	    tsx_cancel_timer(tsx, &tsx->retransmit_timer);
+
+	    /* For provisional response, only cancel retransmit when this
+	     * is an INVITE transaction. For non-INVITE, section 17.1.2.1
+	     * of RFC 3261 says that:
+	     *	- retransmit timer is set to T2
+	     *	- timeout timer F is not deleted.
+	     */
+	    if (tsx->method.id == PJSIP_INVITE_METHOD) {
+
+		/* Cancel timeout timer */
+		lock_timer(tsx);
+		tsx_cancel_timer(tsx, &tsx->timeout_timer);
+		unlock_timer(tsx);
+
+	    } else {
+		if (!tsx->is_reliable) {
+		    tsx_schedule_timer(tsx, &tsx->retransmit_timer,
+		                       &t2_timer_val, RETRANSMIT_TIMER);
+		}
+	    }
+	}
+	 
+	tsx->transport_flag &= ~(TSX_HAS_PENDING_RESCHED);
+
+
+	/* Discard retransmission message if it is not INVITE.
+	 * The INVITE tdata is needed in case we have to generate ACK for
+	 * the final response.
+	 */
+	/* Keep last_tx for authorization. */
+	//blp: always keep last_tx until transaction is destroyed
+	//code = msg->line.status.code;
+	//if (tsx->method.id != PJSIP_INVITE_METHOD && code!=401 && code!=407) {
+	//    pjsip_tx_data_dec_ref(tsx->last_tx);
+	//    tsx->last_tx = NULL;
+	//}
+
+	/* Processing is similar to state Proceeding. */
+	tsx_on_state_proceeding_uac( tsx, event);
+
+    } else {
+	pj_assert(!"Unexpected event");
+        return PJ_EBUG;
+    }
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * State Trying is for UAS after it received request but before any responses
+ * is sent.
+ * Note: this is different than RFC3261, which can use Trying state for
+ *	 non-INVITE client transaction (bug in RFC?).
+ */
+static pj_status_t tsx_on_state_trying( pjsip_transaction *tsx, 
+                                        pjsip_event *event)
+{
+    pj_status_t status;
+
+    pj_assert(tsx->state == PJSIP_TSX_STATE_TRYING);
+
+    /* This state is only for UAS */
+    pj_assert(tsx->role == PJSIP_ROLE_UAS);
+
+    /* Better be transmission of response message.
+     * If we've got request retransmission, this means that the TU hasn't
+     * transmitted any responses within 500 ms, which is not allowed. If
+     * this happens, just ignore the event (we couldn't retransmit last
+     * response because we haven't sent any!).
+     */
+    if (event->type != PJSIP_EVENT_TX_MSG) {
+	return PJ_SUCCESS;
+    }
+
+    /* The rest of the processing of the event is exactly the same as in
+     * "Proceeding" state.
+     */
+    status = tsx_on_state_proceeding_uas( tsx, event);
+
+    /* Inform the TU of the state transision if state is still State_Trying */
+    if (status==PJ_SUCCESS && tsx->state == PJSIP_TSX_STATE_TRYING) {
+
+	tsx_set_state( tsx, PJSIP_TSX_STATE_PROCEEDING, 
+                       PJSIP_EVENT_TX_MSG, event->body.tx_msg.tdata);
+
+    }
+
+    return status;
+}
+
+
+/*
+ * Handler for events in Proceeding for UAS
+ * This state happens after the TU sends provisional response.
+ */
+static pj_status_t tsx_on_state_proceeding_uas( pjsip_transaction *tsx,
+                                                pjsip_event *event)
+{
+    pj_assert(tsx->state == PJSIP_TSX_STATE_PROCEEDING || 
+	      tsx->state == PJSIP_TSX_STATE_TRYING);
+
+    /* This state is only for UAS. */
+    pj_assert(tsx->role == PJSIP_ROLE_UAS);
+
+    /* Receive request retransmission. */
+    if (event->type == PJSIP_EVENT_RX_MSG) {
+
+        pj_status_t status;
+
+	/* Must have last response sent. */
+	PJ_ASSERT_RETURN(tsx->last_tx != NULL, PJ_EBUG);
+
+	/* Send last response */
+	if (tsx->transport_flag & TSX_HAS_PENDING_TRANSPORT) {
+	    tsx->transport_flag |= TSX_HAS_PENDING_SEND;
+	} else {
+	    status = tsx_send_msg(tsx, tsx->last_tx);
+	    if (status != PJ_SUCCESS)
+		return status;
+	}
+	
+    } else if (event->type == PJSIP_EVENT_TX_MSG ) {
+	pjsip_tx_data *tdata = event->body.tx_msg.tdata;
+        pj_status_t status;
+
+	/* The TU sends response message to the request. Save this message so
+	 * that we can retransmit the last response in case we receive request
+	 * retransmission.
+	 */
+	pjsip_msg *msg = tdata->msg;
+
+	/* This can only be a response message. */
+	PJ_ASSERT_RETURN(msg->type==PJSIP_RESPONSE_MSG, PJSIP_ENOTRESPONSEMSG);
+
+	/* Update last status */
+	tsx_set_status_code(tsx, msg->line.status.code, 
+			    &msg->line.status.reason);
+
+	/* Discard the saved last response (it will be updated later as
+	 * necessary).
+	 */
+	if (tsx->last_tx && tsx->last_tx != tdata) {
+	    pjsip_tx_data_dec_ref( tsx->last_tx );
+	    tsx->last_tx = NULL;
+	}
+
+	/* Send the message. */
+        status = tsx_send_msg(tsx, tdata);
+	if (status != PJ_SUCCESS) {
+	    return status;
+	}
+
+	// Update To tag header for RFC2543 transaction.
+	// TODO:
+
+	/* Update transaction state */
+	if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 100)) {
+
+	    if (tsx->last_tx != tdata) {
+		tsx->last_tx = tdata;
+		pjsip_tx_data_add_ref( tdata );
+	    }
+
+	    tsx_set_state( tsx, PJSIP_TSX_STATE_PROCEEDING, 
+                           PJSIP_EVENT_TX_MSG, tdata );
+
+	    /* Retransmit provisional response every 1 minute if this is
+	     * an INVITE provisional response greater than 100.
+	     */
+	    if (PJSIP_TSX_1XX_RETRANS_DELAY > 0 && 
+		tsx->method.id==PJSIP_INVITE_METHOD && tsx->status_code>100)
+	    {
+
+		/* Stop 1xx retransmission timer, if any */
+		tsx_cancel_timer(tsx, &tsx->retransmit_timer);
+
+		/* Schedule retransmission */
+		tsx->retransmit_count = 0;
+		if (tsx->transport_flag & TSX_HAS_PENDING_TRANSPORT) {
+		    tsx->transport_flag |= TSX_HAS_PENDING_RESCHED;
+		} else {
+		    pj_time_val delay = {PJSIP_TSX_1XX_RETRANS_DELAY, 0};
+		    tsx_schedule_timer( tsx, &tsx->retransmit_timer, &delay,
+		                        RETRANSMIT_TIMER);
+		}
+	    }
+
+	} else if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 200)) {
+
+	    /* Stop 1xx retransmission timer, if any */
+	    tsx_cancel_timer(tsx, &tsx->retransmit_timer);
+
+	    if (tsx->method.id == PJSIP_INVITE_METHOD && tsx->handle_200resp==0) {
+
+		/* 2xx class message is not saved, because retransmission 
+                 * is handled by TU.
+		 */
+		tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, 
+                               PJSIP_EVENT_TX_MSG, tdata );
+
+		/* Transaction is destroyed. */
+		//return PJSIP_ETSXDESTROYED;
+
+	    } else {
+		pj_time_val timeout;
+
+		if (tsx->method.id == PJSIP_INVITE_METHOD) {
+		    tsx->retransmit_count = 0;
+		    if (tsx->transport_flag & TSX_HAS_PENDING_TRANSPORT) {
+			tsx->transport_flag |= TSX_HAS_PENDING_RESCHED;
+		    } else {
+			tsx_schedule_timer( tsx, &tsx->retransmit_timer,
+					    &t1_timer_val, RETRANSMIT_TIMER);
+		    }
+		}
+
+		/* Save last response sent for retransmission when request 
+		 * retransmission is received.
+		 */
+		if (tsx->last_tx != tdata) {
+		    tsx->last_tx = tdata;
+		    pjsip_tx_data_add_ref(tdata);
+		}
+
+		/* Setup timeout timer: */
+		
+		if (tsx->method.id == PJSIP_INVITE_METHOD) {
+		    
+		    /* Start Timer H at 64*T1 for INVITE server transaction,
+		     * regardless of transport.
+		     */
+		    timeout = timeout_timer_val;
+		    
+		} else if (!tsx->is_reliable) {
+		    
+		    /* For non-INVITE, start timer J at 64*T1 for unreliable
+		     * transport.
+		     */
+		    timeout = timeout_timer_val;
+		    
+		} else {
+		    
+		    /* Transaction terminates immediately for non-INVITE when
+		     * reliable transport is used.
+		     */
+		    timeout.sec = timeout.msec = 0;
+		}
+
+		lock_timer(tsx);
+		tsx_cancel_timer(tsx, &tsx->timeout_timer);
+		tsx_schedule_timer( tsx, &tsx->timeout_timer,
+                                    &timeout, TIMEOUT_TIMER);
+		unlock_timer(tsx);
+
+		/* Set state to "Completed" */
+		tsx_set_state( tsx, PJSIP_TSX_STATE_COMPLETED, 
+                               PJSIP_EVENT_TX_MSG, tdata );
+	    }
+
+	} else if (tsx->status_code >= 300) {
+
+	    /* Stop 1xx retransmission timer, if any */
+	    tsx_cancel_timer(tsx, &tsx->retransmit_timer);
+
+	    /* 3xx-6xx class message causes transaction to move to 
+             * "Completed" state. 
+             */
+	    if (tsx->last_tx != tdata) {
+		tsx->last_tx = tdata;
+		pjsip_tx_data_add_ref( tdata );
+	    }
+
+	    /* For INVITE, start timer H for transaction termination 
+	     * regardless whether transport is reliable or not.
+	     * For non-INVITE, start timer J with the value of 64*T1 for
+	     * non-reliable transports, and zero for reliable transports.
+	     */
+	    lock_timer(tsx);
+	    tsx_cancel_timer(tsx, &tsx->timeout_timer);
+	    if (tsx->method.id == PJSIP_INVITE_METHOD) {
+		/* Start timer H for INVITE */
+		tsx_schedule_timer(tsx, &tsx->timeout_timer,
+				   &timeout_timer_val, TIMEOUT_TIMER);
+	    } else if (!tsx->is_reliable) {
+		/* Start timer J on 64*T1 seconds for non-INVITE */
+		tsx_schedule_timer(tsx, &tsx->timeout_timer,
+				   &timeout_timer_val, TIMEOUT_TIMER);
+	    } else {
+		/* Start timer J on zero seconds for non-INVITE */
+		pj_time_val zero_time = { 0, 0 };
+		tsx_schedule_timer(tsx, &tsx->timeout_timer,
+				   &zero_time, TIMEOUT_TIMER);
+	    }
+	    unlock_timer(tsx);
+
+	    /* For INVITE, if unreliable transport is used, retransmission 
+	     * timer G will be scheduled (retransmission).
+	     */
+	    if (!tsx->is_reliable) {
+		pjsip_cseq_hdr *cseq = (pjsip_cseq_hdr*)
+				       pjsip_msg_find_hdr( msg, PJSIP_H_CSEQ,
+                                                           NULL);
+		if (cseq->method.id == PJSIP_INVITE_METHOD) {
+		    tsx->retransmit_count = 0;
+		    if (tsx->transport_flag & TSX_HAS_PENDING_TRANSPORT) {
+			tsx->transport_flag |= TSX_HAS_PENDING_RESCHED;
+		    } else {
+			tsx_schedule_timer(tsx, &tsx->retransmit_timer,
+					   &t1_timer_val, RETRANSMIT_TIMER);
+		    }
+		}
+	    }
+
+	    /* Inform TU */
+	    tsx_set_state( tsx, PJSIP_TSX_STATE_COMPLETED, 
+                           PJSIP_EVENT_TX_MSG, tdata );
+
+	} else {
+	    pj_assert(0);
+	}
+
+
+    } else if (event->type == PJSIP_EVENT_TIMER && 
+	       event->body.timer.entry == &tsx->retransmit_timer) {
+
+	/* Retransmission timer elapsed. */
+        pj_status_t status;
+
+	/* Must not be triggered while transport is pending. */
+	pj_assert((tsx->transport_flag & TSX_HAS_PENDING_TRANSPORT) == 0);
+
+	/* Must have last response to retransmit. */
+	pj_assert(tsx->last_tx != NULL);
+
+	/* Retransmit the last response. */
+        status = tsx_retransmit( tsx, 1 );
+	if (status != PJ_SUCCESS) {
+	    return status;
+	}
+
+    } else if (event->type == PJSIP_EVENT_TIMER && 
+	       event->body.timer.entry == &tsx->timeout_timer) {
+
+	/* Timeout timer. should not happen? */
+	pj_assert(!"Should not happen(?)");
+
+	tsx_set_status_code(tsx, PJSIP_SC_TSX_TIMEOUT, NULL);
+
+	tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED,
+                       PJSIP_EVENT_TIMER, &tsx->timeout_timer);
+
+	return PJ_EBUG;
+
+    } else {
+	pj_assert(!"Unexpected event");
+        return PJ_EBUG;
+    }
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Handler for events in Proceeding for UAC
+ * This state happens after provisional response(s) has been received from
+ * UAS.
+ */
+static pj_status_t tsx_on_state_proceeding_uac(pjsip_transaction *tsx, 
+                                               pjsip_event *event)
+{
+
+    pj_assert(tsx->state == PJSIP_TSX_STATE_PROCEEDING || 
+	      tsx->state == PJSIP_TSX_STATE_CALLING);
+
+    if (event->type != PJSIP_EVENT_TIMER) {
+	pjsip_msg *msg;
+
+	/* Must be incoming response, because we should not retransmit
+	 * request once response has been received.
+	 */
+	pj_assert(event->type == PJSIP_EVENT_RX_MSG);
+	if (event->type != PJSIP_EVENT_RX_MSG) {
+	    return PJ_EINVALIDOP;
+	}
+
+	msg = event->body.rx_msg.rdata->msg_info.msg;
+
+	/* Must be a response message. */
+	if (msg->type != PJSIP_RESPONSE_MSG) {
+	    pj_assert(!"Expecting response message!");
+	    return PJSIP_ENOTRESPONSEMSG;
+	}
+
+	tsx_set_status_code(tsx, msg->line.status.code, 
+			    &msg->line.status.reason);
+
+    } else {
+	if (event->body.timer.entry == &tsx->retransmit_timer) {
+	    /* Retransmit message. */
+            pj_status_t status;
+
+            status = tsx_retransmit( tsx, 1 );
+	    
+	    return status;
+
+	} else {
+	    tsx_set_status_code(tsx, PJSIP_SC_TSX_TIMEOUT, NULL);
+	}
+    }
+
+    if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 100)) {
+
+	/* Inform the message to TU. */
+	tsx_set_state( tsx, PJSIP_TSX_STATE_PROCEEDING, 
+                       PJSIP_EVENT_RX_MSG, event->body.rx_msg.rdata );
+
+    } else if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code,200)) {
+
+	/* Stop timeout timer B/F. */
+	lock_timer(tsx);
+	tsx_cancel_timer( tsx, &tsx->timeout_timer );
+	unlock_timer(tsx);
+
+	/* For INVITE, the state moves to Terminated state (because ACK is
+	 * handled in TU). For non-INVITE, state moves to Completed.
+	 */
+	if (tsx->method.id == PJSIP_INVITE_METHOD) {
+	    tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, 
+                           PJSIP_EVENT_RX_MSG, event->body.rx_msg.rdata );
+	    //return PJSIP_ETSXDESTROYED;
+
+	} else {
+	    pj_time_val timeout;
+
+	    /* For unreliable transport, start timer D (for INVITE) or 
+	     * timer K for non-INVITE. */
+	    if (!tsx->is_reliable) {
+		if (tsx->method.id == PJSIP_INVITE_METHOD) {
+		    timeout = td_timer_val;
+		} else {
+		    timeout = t4_timer_val;
+		}
+	    } else {
+		timeout.sec = timeout.msec = 0;
+	    }
+	    lock_timer(tsx);
+	    tsx_schedule_timer( tsx, &tsx->timeout_timer,
+				&timeout, TIMEOUT_TIMER);
+	    unlock_timer(tsx);
+
+	    /* Cancel retransmission timer */
+	    tsx_cancel_timer(tsx, &tsx->retransmit_timer);
+
+	    /* Move state to Completed, inform TU. */
+	    tsx_set_state( tsx, PJSIP_TSX_STATE_COMPLETED, 
+                           PJSIP_EVENT_RX_MSG, event->body.rx_msg.rdata );
+	}
+
+    } else if (event->type == PJSIP_EVENT_TIMER &&
+	       event->body.timer.entry == &tsx->timeout_timer) {
+
+	/* Inform TU. */
+	tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED,
+		       PJSIP_EVENT_TIMER, &tsx->timeout_timer);
+
+
+    } else if (tsx->status_code >= 300 && tsx->status_code <= 699) {
+
+
+#if 0
+	/* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */
+	/*
+	 * This is the old code; it's broken for authentication.
+	 */
+	pj_time_val timeout;
+        pj_status_t status;
+
+	/* Stop timer B. */
+	tsx_cancel_timer( tsx, &tsx->timeout_timer );
+
+	/* Generate and send ACK for INVITE. */
+	if (tsx->method.id == PJSIP_INVITE_METHOD) {
+	    pjsip_tx_data *ack;
+
+	    status = pjsip_endpt_create_ack( tsx->endpt, tsx->last_tx, 
+					     event->body.rx_msg.rdata,
+					     &ack);
+	    if (status != PJ_SUCCESS)
+		return status;
+
+	    if (ack != tsx->last_tx) {
+		pjsip_tx_data_dec_ref(tsx->last_tx);
+		tsx->last_tx = ack;
+	    }
+
+            status = tsx_send_msg( tsx, tsx->last_tx);
+	    if (status != PJ_SUCCESS) {
+		return status;
+	    }
+	}
+
+	/* Start Timer D with TD/T4 timer if unreliable transport is used. */
+	if (!tsx->is_reliable) {
+	    if (tsx->method.id == PJSIP_INVITE_METHOD) {
+		timeout = td_timer_val;
+	    } else {
+		timeout = t4_timer_val;
+	    }
+	} else {
+	    timeout.sec = timeout.msec = 0;
+	}
+	tsx_schedule_timer( tsx, &tsx->timeout_timer, &timeout, TIMEOUT_TIMER);
+
+	/* Inform TU. 
+	 * blp: You might be tempted to move this notification before
+	 *      sending ACK, but I think you shouldn't. Better set-up
+	 *      everything before calling tsx_user's callback to avoid
+	 *      mess up.
+	 */
+	tsx_set_state( tsx, PJSIP_TSX_STATE_COMPLETED, 
+                       PJSIP_EVENT_RX_MSG, event->body.rx_msg.rdata );
+
+	/* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */
+#endif
+
+	/* New code, taken from 0.2.9.x branch */
+	pj_time_val timeout;
+	pjsip_tx_data *ack_tdata = NULL;
+
+	/* Cancel retransmission timer */
+	tsx_cancel_timer(tsx, &tsx->retransmit_timer);
+
+	/* Stop timer B. */
+	lock_timer(tsx);
+	tsx_cancel_timer( tsx, &tsx->timeout_timer );
+	unlock_timer(tsx);
+
+	/* Generate and send ACK (for INVITE) */
+	if (tsx->method.id == PJSIP_INVITE_METHOD) {
+	    pj_status_t status;
+
+	    status = pjsip_endpt_create_ack( tsx->endpt, tsx->last_tx, 
+					     event->body.rx_msg.rdata,
+					     &ack_tdata);
+	    if (status != PJ_SUCCESS)
+		return status;
+
+	    status = tsx_send_msg( tsx, ack_tdata);
+	    if (status != PJ_SUCCESS)
+		return status;
+	}
+
+	/* Inform TU. */
+	tsx_set_state( tsx, PJSIP_TSX_STATE_COMPLETED, 
+		       PJSIP_EVENT_RX_MSG, event->body.rx_msg.rdata);
+
+	/* Generate and send ACK for INVITE. */
+	if (tsx->method.id == PJSIP_INVITE_METHOD) {
+	    if (ack_tdata != tsx->last_tx) {
+		pjsip_tx_data_dec_ref(tsx->last_tx);
+		tsx->last_tx = ack_tdata;
+
+		/* This is a bug.
+		   tsx_send_msg() does NOT decrement tdata's reference counter,
+		   so if we add the reference counter here, tdata will have
+		   reference counter 2, causing it to leak.
+		pjsip_tx_data_add_ref(ack_tdata);
+		*/
+	    }
+	}
+
+	/* Start Timer D with TD/T4 timer if unreliable transport is used. */
+	/* Note: tsx->transport may be NULL! */
+	if (!tsx->is_reliable) {
+	    if (tsx->method.id == PJSIP_INVITE_METHOD) {
+		timeout = td_timer_val;
+	    } else {
+		timeout = t4_timer_val;
+	    }
+	} else {
+	    timeout.sec = timeout.msec = 0;
+	}
+	lock_timer(tsx);
+	/* In the short period above timer may have been inserted
+	 * by set_timeout() (by CANCEL). Cancel it if necessary. See:
+	 *  https://trac.pjsip.org/repos/ticket/1374
+	 */
+	tsx_cancel_timer( tsx, &tsx->timeout_timer );
+	tsx_schedule_timer( tsx, &tsx->timeout_timer, &timeout,
+	                    TIMEOUT_TIMER);
+	unlock_timer(tsx);
+
+    } else {
+	// Shouldn't happen because there's no timer for this state.
+	pj_assert(!"Unexpected event");
+        return PJ_EBUG;
+    }
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Handler for events in Completed state for UAS
+ */
+static pj_status_t tsx_on_state_completed_uas( pjsip_transaction *tsx, 
+                                               pjsip_event *event)
+{
+    pj_assert(tsx->state == PJSIP_TSX_STATE_COMPLETED);
+
+    if (event->type == PJSIP_EVENT_RX_MSG) {
+	pjsip_msg *msg = event->body.rx_msg.rdata->msg_info.msg;
+
+	/* This must be a request message retransmission. */
+	if (msg->type != PJSIP_REQUEST_MSG)
+	    return PJSIP_ENOTREQUESTMSG;
+
+	/* On receive request retransmission, retransmit last response. */
+	if (msg->line.req.method.id != PJSIP_ACK_METHOD) {
+            pj_status_t status;
+
+            status = tsx_retransmit( tsx, 0 );
+	    if (status != PJ_SUCCESS) {
+		return status;
+	    }
+
+	} else {
+	    pj_time_val timeout;
+
+	    /* Process incoming ACK request. */
+
+	    /* Verify that this is an INVITE transaction */
+	    if (tsx->method.id != PJSIP_INVITE_METHOD) {
+		PJ_LOG(2, (tsx->obj_name, 
+			   "Received illegal ACK for %.*s transaction",
+			   (int)tsx->method.name.slen,
+			   tsx->method.name.ptr));
+		return PJSIP_EINVALIDMETHOD;
+	    }
+
+	    /* Cease retransmission. */
+	    tsx_cancel_timer(tsx, &tsx->retransmit_timer);
+
+	    tsx->transport_flag &= ~(TSX_HAS_PENDING_RESCHED);
+
+	    /* Reschedule timeout timer. */
+	    lock_timer(tsx);
+	    tsx_cancel_timer( tsx, &tsx->timeout_timer );
+
+	    /* Timer I is T4 timer for unreliable transports, and
+	     * zero seconds for reliable transports.
+	     */
+	    if (!tsx->is_reliable) {
+		timeout.sec = 0; 
+		timeout.msec = 0;
+	    } else {
+		timeout.sec = t4_timer_val.sec;
+		timeout.msec = t4_timer_val.msec;
+	    }
+	    tsx_schedule_timer( tsx, &tsx->timeout_timer,
+				&timeout, TIMEOUT_TIMER);
+	    unlock_timer(tsx);
+
+	    /* Move state to "Confirmed" */
+	    tsx_set_state( tsx, PJSIP_TSX_STATE_CONFIRMED, 
+                           PJSIP_EVENT_RX_MSG, event->body.rx_msg.rdata );
+	}	
+
+    } else if (event->type == PJSIP_EVENT_TIMER) {
+
+	if (event->body.timer.entry == &tsx->retransmit_timer) {
+	    /* Retransmit message. */
+            pj_status_t status;
+
+            status = tsx_retransmit( tsx, 1 );
+	    if (status != PJ_SUCCESS) {
+		return status;
+	    }
+
+	} else {
+	    if (tsx->method.id == PJSIP_INVITE_METHOD) {
+
+		/* For INVITE, this means that ACK was never received.
+		 * Set state to Terminated, and inform TU.
+		 */
+
+		tsx_set_status_code(tsx, PJSIP_SC_TSX_TIMEOUT, NULL);
+
+		tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, 
+                               PJSIP_EVENT_TIMER, &tsx->timeout_timer );
+
+		//return PJSIP_ETSXDESTROYED;
+
+	    } else {
+		/* Transaction terminated, it can now be deleted. */
+		tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, 
+                               PJSIP_EVENT_TIMER, &tsx->timeout_timer );
+		//return PJSIP_ETSXDESTROYED;
+	    }
+	}
+
+    } else {
+	/* Ignore request to transmit. */
+	PJ_ASSERT_RETURN(event->type == PJSIP_EVENT_TX_MSG && 
+			 event->body.tx_msg.tdata == tsx->last_tx, 
+			 PJ_EINVALIDOP);
+    }
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Handler for events in Completed state for UAC transaction.
+ */
+static pj_status_t tsx_on_state_completed_uac( pjsip_transaction *tsx,
+                                               pjsip_event *event)
+{
+    pj_assert(tsx->state == PJSIP_TSX_STATE_COMPLETED);
+
+    if (event->type == PJSIP_EVENT_TIMER) {
+	/* Must be the timeout timer. */
+	pj_assert(event->body.timer.entry == &tsx->timeout_timer);
+
+	/* Move to Terminated state. */
+	tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED,
+                       PJSIP_EVENT_TIMER, event->body.timer.entry );
+
+	/* Transaction has been destroyed. */
+	//return PJSIP_ETSXDESTROYED;
+
+    } else if (event->type == PJSIP_EVENT_RX_MSG) {
+	if (tsx->method.id == PJSIP_INVITE_METHOD) {
+	    /* On received of final response retransmission, retransmit the ACK.
+	     * TU doesn't need to be informed.
+	     */
+	    pjsip_msg *msg = event->body.rx_msg.rdata->msg_info.msg;
+	    pj_assert(msg->type == PJSIP_RESPONSE_MSG);
+	    if (msg->type==PJSIP_RESPONSE_MSG &&
+		msg->line.status.code >= 200)
+	    {
+                pj_status_t status;
+
+                status = tsx_retransmit( tsx, 0 );
+		if (status != PJ_SUCCESS) {
+		    return status;
+		}
+	    } else {
+		/* Very late retransmission of privisional response. */
+		pj_assert( msg->type == PJSIP_RESPONSE_MSG );
+	    }
+	} else {
+	    /* Just drop the response. */
+	}
+
+    } else {
+	pj_assert(!"Unexpected event");
+        return PJ_EINVALIDOP;
+    }
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Handler for events in state Confirmed.
+ */
+static pj_status_t tsx_on_state_confirmed( pjsip_transaction *tsx,
+                                           pjsip_event *event)
+{
+    pj_assert(tsx->state == PJSIP_TSX_STATE_CONFIRMED);
+
+    /* This state is only for UAS for INVITE. */
+    pj_assert(tsx->role == PJSIP_ROLE_UAS);
+    pj_assert(tsx->method.id == PJSIP_INVITE_METHOD);
+
+    /* Absorb any ACK received. */
+    if (event->type == PJSIP_EVENT_RX_MSG) {
+
+	pjsip_msg *msg = event->body.rx_msg.rdata->msg_info.msg;
+
+	/* Only expecting request message. */
+	if (msg->type != PJSIP_REQUEST_MSG)
+	    return PJSIP_ENOTREQUESTMSG;
+
+	/* Must be an ACK request or a late INVITE retransmission. */
+	pj_assert(msg->line.req.method.id == PJSIP_ACK_METHOD || 
+		  msg->line.req.method.id == PJSIP_INVITE_METHOD);
+
+    } else if (event->type == PJSIP_EVENT_TIMER) {
+	/* Must be from timeout_timer_. */
+	pj_assert(event->body.timer.entry == &tsx->timeout_timer);
+
+	/* Move to Terminated state. */
+	tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED,
+                       PJSIP_EVENT_TIMER, &tsx->timeout_timer );
+
+	/* Transaction has been destroyed. */
+	//return PJSIP_ETSXDESTROYED;
+
+    } else {
+	pj_assert(!"Unexpected event");
+        return PJ_EBUG;
+    }
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Handler for events in state Terminated.
+ */
+static pj_status_t tsx_on_state_terminated( pjsip_transaction *tsx,
+                                            pjsip_event *event)
+{
+    pj_assert(tsx->state == PJSIP_TSX_STATE_TERMINATED);
+
+    /* Ignore events other than timer. This used to be an assertion but
+     * events may genuinely arrive at this state.
+     */
+    if (event->type != PJSIP_EVENT_TIMER) {
+	return PJ_EIGNORED;
+    }
+
+    /* Destroy this transaction */
+    tsx_set_state(tsx, PJSIP_TSX_STATE_DESTROYED, 
+                  event->type, event->body.user.user1 );
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Handler for events in state Destroyed.
+ * Shouldn't happen!
+ */
+static pj_status_t tsx_on_state_destroyed(pjsip_transaction *tsx,
+                                          pjsip_event *event)
+{
+    PJ_UNUSED_ARG(tsx);
+    PJ_UNUSED_ARG(event);
+
+    // See https://trac.pjsip.org/repos/ticket/1432
+    //pj_assert(!"Not expecting any events!!");
+
+    return PJ_EIGNORED;
+}
+
diff --git a/jni/pjproject-android/.svn/pristine/ed/edbc3e6fb9a5f4edb7d764961a147aa9e70322bb.svn-base b/jni/pjproject-android/.svn/pristine/ed/edbc3e6fb9a5f4edb7d764961a147aa9e70322bb.svn-base
new file mode 100644
index 0000000..daa4c6c
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/ed/edbc3e6fb9a5f4edb7d764961a147aa9e70322bb.svn-base
@@ -0,0 +1,22 @@
+# $Id$
+#
+from inc_cfg import *
+
+# Note:
+#	- need --dis-codec to make INVITE packet less than typical MTU
+uas_args = "--null-audio --id=\"<sip:test1@pjsip.org>\" --registrar=sip:sip.pjsip.org --username=test1 --password=test1  --realm=pjsip.org  --proxy=\"sip:sip.pjsip.org;lr\"  --rtp-port 0 --stun-srv stun.pjsip.org  --use-ice --use-compact-form --max-calls 1 --dis-codec=i --dis-codec=s --dis-codec=g"
+
+uac_args = "--null-audio --id=\"<sip:test2@pjsip.org>\" --registrar=sip:sip.pjsip.org --username=test2 --password=test2 --realm=pjsip.org --proxy=\"sip:sip.pjsip.org;lr\" --rtp-port 0 --stun-srv stun.pjsip.org --use-ice --use-compact-form --max-calls 1 --dis-codec=i --dis-codec=s --dis-codec=g"
+
+test_param = TestParam(
+		"ICE via public internet",
+		[
+			InstanceParam(	"callee", uas_args, 
+					uri="<sip:test1@pjsip.org>",
+					have_reg=True, have_publish=False),
+			InstanceParam(	"caller", uac_args,
+					uri="<sip:test2@pjsip.org>",
+					have_reg=True, have_publish=False),
+		]
+		)
+
diff --git a/jni/pjproject-android/.svn/pristine/ed/ede3c4ddb5074771aa97a31978cae7eed6f077e3.svn-base b/jni/pjproject-android/.svn/pristine/ed/ede3c4ddb5074771aa97a31978cae7eed6f077e3.svn-base
new file mode 100644
index 0000000..30f422e
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/ed/ede3c4ddb5074771aa97a31978cae7eed6f077e3.svn-base
@@ -0,0 +1,2 @@
+
+export LDFLAGS += -lwinmm