Index: pjproject/trunk/pjsip/include/pjsip-ua/sip_inv.h
===================================================================
--- pjproject/trunk/pjsip/include/pjsip-ua/sip_inv.h	(revision 1597)
+++ pjproject/trunk/pjsip/include/pjsip-ua/sip_inv.h	(revision 1598)
@@ -191,4 +191,28 @@
     void (*on_media_update)(pjsip_inv_session *inv_ses, 
 			    pj_status_t status);
+
+    /**
+     * This callback is called when the framework needs to send
+     * ACK request after it receives incoming  2xx response for 
+     * INVITE. It allows application to manually handle the 
+     * transmission of ACK request, which is required by some 3PCC
+     * scenarios. If this callback is not implemented, the framework
+     * will handle the ACK transmission automatically.
+     *
+     * When this callback is overridden, application may delay the
+     * sending of the ACK request (for example, when it needs to 
+     * wait for answer from the other call leg, in 3PCC scenarios). 
+     *
+     * Application creates the ACK request
+     *
+     * Once it has sent the ACK request, the framework will keep 
+     * this ACK request in the cache. Subsequent receipt of 2xx response
+     * will not cause this callback to be called, and instead automatic
+     * retransmission of this ACK request from the cache will be done
+     * by the framework.
+     *
+     * This callback is optional.
+     */
+    void (*on_send_ack)(pjsip_inv_session *inv, pjsip_rx_data *rdata);
 
 } pjsip_inv_callback;
@@ -588,4 +612,30 @@
 
 /**
+ * Create an ACK request. Normally ACK request transmission is handled
+ * by the framework. Application only needs to use this function if it
+ * handles the ACK transmission manually, by overriding \a on_send_ack()
+ * callback in #pjsip_inv_callback.
+ *
+ * Note that if the invite session has a pending offer to be answered 
+ * (for example when the last 2xx response to INVITE contains an offer), 
+ * application MUST have set the SDP answer with #pjsip_create_sdp_body()
+ * prior to creating the ACK request. In this case, the ACK request
+ * will be added with SDP message body.
+ *
+ * @param inv		The invite session.
+ * @param cseq		Mandatory argument to specify the CSeq of the
+ *			ACK request. This value MUST match the value
+ *			of the INVITE transaction to be acknowledged.
+ * @param p_tdata	Pointer to receive the ACK request message to
+ *			be created.
+ *
+ * @return		PJ_SUCCESS if ACK request has been created.
+ */
+PJ_DECL(pj_status_t) pjsip_inv_create_ack(pjsip_inv_session *inv,
+					  int cseq,
+					  pjsip_tx_data **p_tdata);
+
+
+/**
  * Send request or response message in tdata. 
  *
@@ -603,5 +653,4 @@
 
 
-
 /**
  * Get the invite session for the dialog, if any.
Index: pjproject/trunk/pjsip/src/pjsip-ua/sip_inv.c
===================================================================
--- pjproject/trunk/pjsip/src/pjsip-ua/sip_inv.c	(revision 1597)
+++ pjproject/trunk/pjsip/src/pjsip-ua/sip_inv.c	(revision 1598)
@@ -273,7 +273,18 @@
     {
 	struct tsx_inv_data *tsx_inv_data;
-
-	/* Get invite session's transaction data */
-	tsx_inv_data = (struct tsx_inv_data*) tsx->mod_data[mod_inv.mod.id];
+	struct tsx_inv_data dummy;
+
+	/* Get invite session's transaction data.
+	 * Note that tsx may be NULL, for example when application sends
+	 * delayed ACK request (at this time, the original INVITE 
+	 * transaction may have been destroyed.
+	 */
+	if (tsx) {
+	    tsx_inv_data = (struct tsx_inv_data*)tsx->mod_data[mod_inv.mod.id];
+	} else {
+	    tsx_inv_data = &dummy;
+	    pj_bzero(&dummy, sizeof(dummy));
+	    dummy.inv = inv;
+	}
 
 	status = inv_negotiate_sdp(inv);
@@ -302,7 +313,17 @@
  * Send ACK for 2xx response.
  */
-static pj_status_t inv_send_ack(pjsip_inv_session *inv, pjsip_rx_data *rdata)
-{
+static pj_status_t inv_send_ack(pjsip_inv_session *inv, pjsip_event *e)
+{
+    pjsip_rx_data *rdata;
     pj_status_t status;
+
+    if (e->type == PJSIP_EVENT_TSX_STATE)
+	rdata = e->body.tsx_state.src.rdata;
+    else if (e->type == PJSIP_EVENT_RX_MSG)
+	rdata = e->body.rx_msg.rdata;
+    else {
+	pj_assert(!"Unsupported event type");
+	return PJ_EBUG;
+    }
 
     PJ_LOG(5,(inv->obj_name, "Received %s, sending ACK",
@@ -311,34 +332,20 @@
     /* Check if we have cached ACK request */
     if (inv->last_ack && rdata->msg_info.cseq->cseq == inv->last_ack_cseq) {
+
 	pjsip_tx_data_add_ref(inv->last_ack);
+
+    } else if (mod_inv.cb.on_send_ack) {
+	/* If application handles ACK transmission manually, just notify the
+	 * callback
+	 */
+	PJ_LOG(5,(inv->obj_name, "Received %s, notifying application callback",
+		  pjsip_rx_data_get_info(rdata)));
+
+	(*mod_inv.cb.on_send_ack)(inv, rdata);
+	return PJ_SUCCESS;
+
     } else {
-	const pjmedia_sdp_session *sdp = NULL;
-
-	/* Destroy last_ack */
-	if (inv->last_ack) {
-	    pjsip_tx_data_dec_ref(inv->last_ack);
-	    inv->last_ack = NULL;
-	}
-
-	/* Create new ACK request */
-	status = pjsip_dlg_create_request(inv->dlg, pjsip_get_ack_method(), 
-					  rdata->msg_info.cseq->cseq, 
-					  &inv->last_ack);
-	if (status != PJ_SUCCESS) {
-	    /* Better luck next time */
-	    pj_assert(!"Unable to create ACK!");
-	    return status;
-	}
-
-	/* See if we have pending SDP answer to send */
-	sdp = inv_has_pending_answer(inv, inv->invite_tsx);
-	if (sdp) {
-	    inv->last_ack->msg->body=create_sdp_body(inv->last_ack->pool, sdp);
-	}
-
-
-	/* Keep this for subsequent response retransmission */
-	inv->last_ack_cseq = rdata->msg_info.cseq->cseq;
-	pjsip_tx_data_add_ref(inv->last_ack);
+	status = pjsip_inv_create_ack(inv, rdata->msg_info.cseq->cseq,
+				      &inv->last_ack);
     }
 
@@ -349,4 +356,10 @@
 	pj_assert(!"Unable to send ACK!");
 	return status;
+    }
+
+
+    /* Set state to CONFIRMED (if we're not in CONFIRMED yet) */
+    if (inv->state != PJSIP_INV_STATE_CONFIRMED) {
+	inv_set_state(inv, PJSIP_INV_STATE_CONFIRMED, e);
     }
 
@@ -474,6 +487,8 @@
 	inv->invite_tsx == NULL) 
     {
-
-	inv_send_ack(inv, rdata);
+	pjsip_event e;
+
+	PJSIP_EVENT_INIT_RX_MSG(e, rdata);
+	inv_send_ack(inv, &e);
 	return PJ_TRUE;
 
@@ -1967,4 +1982,52 @@
 
     return status;
+}
+
+/*
+ * Create an ACK request.
+ */
+PJ_DEF(pj_status_t) pjsip_inv_create_ack(pjsip_inv_session *inv,
+					 int cseq,
+					 pjsip_tx_data **p_tdata)
+{
+    const pjmedia_sdp_session *sdp = NULL;
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(inv && p_tdata, PJ_EINVAL);
+
+    /* Lock dialog. */
+    pjsip_dlg_inc_lock(inv->dlg);
+
+    /* Destroy last_ack */
+    if (inv->last_ack) {
+	pjsip_tx_data_dec_ref(inv->last_ack);
+	inv->last_ack = NULL;
+    }
+
+    /* Create new ACK request */
+    status = pjsip_dlg_create_request(inv->dlg, pjsip_get_ack_method(), 
+				      cseq, &inv->last_ack);
+    if (status != PJ_SUCCESS) {
+	pjsip_dlg_dec_lock(inv->dlg);
+	return status;
+    }
+
+    /* See if we have pending SDP answer to send */
+    sdp = inv_has_pending_answer(inv, inv->invite_tsx);
+    if (sdp) {
+	inv->last_ack->msg->body = create_sdp_body(inv->last_ack->pool, sdp);
+    }
+
+    /* Keep this for subsequent response retransmission */
+    inv->last_ack_cseq = cseq;
+    pjsip_tx_data_add_ref(inv->last_ack);
+
+    /* Done */
+    *p_tdata = inv->last_ack;
+
+    /* Unlock dialog. */
+    pjsip_dlg_dec_lock(inv->dlg);
+
+    return PJ_SUCCESS;
 }
 
@@ -2548,7 +2611,5 @@
 		pj_assert(e->body.tsx_state.type == PJSIP_EVENT_RX_MSG);
 
-		inv_send_ack(inv, e->body.tsx_state.src.rdata);
-		inv_set_state(inv, PJSIP_INV_STATE_CONFIRMED, e);
-
+		inv_send_ack(inv, e);
 
 	    } else  {
@@ -2727,6 +2788,5 @@
 		    pj_assert(e->body.tsx_state.type == PJSIP_EVENT_RX_MSG);
 
-		    inv_send_ack(inv, e->body.tsx_state.src.rdata);
-		    inv_set_state(inv, PJSIP_INV_STATE_CONFIRMED, e);
+		    inv_send_ack(inv, e);
 		}
 
@@ -3168,5 +3228,5 @@
 
 	    /* Send ACK */
-	    inv_send_ack(inv, e->body.tsx_state.src.rdata);
+	    inv_send_ack(inv, e);
 
 	} else if (tsx->state == PJSIP_TSX_STATE_COMPLETED &&
