Index: /pjproject/trunk/pjsip/src/pjsip-ua/sip_inv.c
===================================================================
--- /pjproject/trunk/pjsip/src/pjsip-ua/sip_inv.c	(revision 3214)
+++ /pjproject/trunk/pjsip/src/pjsip-ua/sip_inv.c	(revision 3215)
@@ -2394,5 +2394,5 @@
 
     /* Verify arguments. */
-    PJ_ASSERT_RETURN(inv && p_tdata && offer, PJ_EINVAL);
+    PJ_ASSERT_RETURN(inv && p_tdata, PJ_EINVAL);
 
     /* Dialog must have been established */
@@ -2407,22 +2407,24 @@
     pjsip_dlg_inc_lock(inv->dlg);
 
-    /* Process offer */
-    if (pjmedia_sdp_neg_get_state(inv->neg)!=PJMEDIA_SDP_NEG_STATE_DONE) {
-	PJ_LOG(4,(inv->dlg->obj_name, 
-		  "Invalid SDP offer/answer state for UPDATE"));
-	status = PJ_EINVALIDOP;
-	goto on_error;
-    }
-
-    /* Notify negotiator about the new offer. This will fix the offer
-     * with correct SDP origin.
-     */
-    status = pjmedia_sdp_neg_modify_local_offer(inv->pool_prov, inv->neg,
-						offer);
-    if (status != PJ_SUCCESS)
-	goto on_error;
-
-    /* Retrieve the "fixed" offer from negotiator */
-    pjmedia_sdp_neg_get_neg_local(inv->neg, &offer);
+    /* Process offer, if any */
+    if (offer) {
+	if (pjmedia_sdp_neg_get_state(inv->neg)!=PJMEDIA_SDP_NEG_STATE_DONE) {
+	    PJ_LOG(4,(inv->dlg->obj_name,
+		      "Invalid SDP offer/answer state for UPDATE"));
+	    status = PJ_EINVALIDOP;
+	    goto on_error;
+	}
+
+	/* Notify negotiator about the new offer. This will fix the offer
+	 * with correct SDP origin.
+	 */
+	status = pjmedia_sdp_neg_modify_local_offer(inv->pool_prov, inv->neg,
+						    offer);
+	if (status != PJ_SUCCESS)
+	    goto on_error;
+
+	/* Retrieve the "fixed" offer from negotiator */
+	pjmedia_sdp_neg_get_neg_local(inv->neg, &offer);
+    }
 
     /* Update Contact if required */
@@ -2450,6 +2452,8 @@
 
     /* Attach SDP body */
-    sdp_copy = pjmedia_sdp_session_clone(tdata->pool, offer);
-    pjsip_create_sdp_body(tdata->pool, sdp_copy, &tdata->msg->body);
+    if (offer) {
+	sdp_copy = pjmedia_sdp_session_clone(tdata->pool, offer);
+	pjsip_create_sdp_body(tdata->pool, sdp_copy, &tdata->msg->body);
+    }
 
     /* Unlock dialog. */
@@ -2880,4 +2884,14 @@
     else 
     {
+	/* Session-Timer needs to see any error responses, to determine
+	 * whether peer supports UPDATE with empty body.
+	 */
+	if (tsx->state == PJSIP_TSX_STATE_COMPLETED &&
+	    tsx->role == PJSIP_ROLE_UAC)
+	{
+	    status = handle_timer_response(inv, e->body.tsx_state.src.rdata,
+					   PJ_FALSE);
+	}
+
 	tsx_inv_data = (struct tsx_inv_data*)tsx->mod_data[mod_inv.mod.id];
 	if (tsx_inv_data == NULL) {
Index: /pjproject/trunk/pjsip/src/pjsip-ua/sip_timer.c
===================================================================
--- /pjproject/trunk/pjsip/src/pjsip-ua/sip_timer.c	(revision 3214)
+++ /pjproject/trunk/pjsip/src/pjsip-ua/sip_timer.c	(revision 3215)
@@ -60,4 +60,5 @@
     pj_bool_t			 use_update;	/**< Use UPDATE method to
 						     refresh the session    */
+    pj_bool_t		  	 with_sdp;	/**< SDP in UPDATE?	    */
     pjsip_role_e		 role;		/**< Role in last INVITE/
 						     UPDATE transaction.    */
@@ -322,5 +323,5 @@
  * the session.
  */
-void timer_cb(pj_timer_heap_t *timer_heap, struct pj_timer_entry *entry)
+static void timer_cb(pj_timer_heap_t *timer_heap, struct pj_timer_entry *entry)
 {
     pjsip_inv_session *inv = (pjsip_inv_session*) entry->user_data;
@@ -331,18 +332,7 @@
     pj_assert(inv);
 
+    inv->timer->timer.id = 0;
+
     PJ_UNUSED_ARG(timer_heap);
-
-    /* When there is a pending INVITE transaction, delay/reschedule this timer
-     * for five seconds to cover the case that pending INVITE fails and the
-     * previous session is still active. If the pending INVITE is successful, 
-     * timer state will be updated, i.e: restarted or stopped.
-     */
-    if (inv->invite_tsx != NULL) {
-	pj_time_val delay = {5};
-
-	inv->timer->timer.id = 1;
-	pjsip_endpt_schedule_timer(inv->dlg->endpt, &inv->timer->timer, &delay);
-	return;
-    }
 
     /* Lock dialog. */
@@ -350,5 +340,5 @@
 
     /* Check our role */
-    as_refresher = 
+    as_refresher =
 	(inv->timer->refresher == TR_UAC && inv->timer->role == PJSIP_ROLE_UAC) ||
 	(inv->timer->refresher == TR_UAS && inv->timer->role == PJSIP_ROLE_UAS);
@@ -356,11 +346,39 @@
     /* Do action based on role, refresher or refreshee */
     if (as_refresher) {
-
 	pj_time_val now;
+
+	/* As refresher, reshedule the refresh request on the following:
+	 *  - msut not send re-INVITE if another INVITE or SDP negotiation
+	 *    is in progress.
+	 *  - must not send UPDATE with SDP if SDP negotiation is in progress
+	 */
+	pjmedia_sdp_neg_state neg_state = pjmedia_sdp_neg_get_state(inv->neg);
+	if ( (!inv->timer->use_update && (
+			inv->invite_tsx != NULL ||
+			neg_state != PJMEDIA_SDP_NEG_STATE_DONE)
+             )
+	     ||
+	     (inv->timer->use_update && inv->timer->with_sdp &&
+		     neg_state != PJMEDIA_SDP_NEG_STATE_DONE
+	     )
+	   )
+	{
+	    pj_time_val delay = {1, 0};
+
+	    inv->timer->timer.id = 1;
+	    pjsip_endpt_schedule_timer(inv->dlg->endpt, &inv->timer->timer,
+				       &delay);
+	    pjsip_dlg_dec_lock(inv->dlg);
+	    return;
+	}
 
 	/* Refresher, refresh the session */
 	if (inv->timer->use_update) {
-	    /* Create UPDATE request without offer */
-	    status = pjsip_inv_update(inv, NULL, NULL, &tdata);
+	    const pjmedia_sdp_session *offer = NULL;
+
+	    if (inv->timer->with_sdp) {
+		pjmedia_sdp_neg_get_active_local(inv->neg, &offer);
+	    }
+	    status = pjsip_inv_update(inv, NULL, offer, &tdata);
 	} else {
 	    /* Create re-INVITE without modifying session */
@@ -385,6 +403,6 @@
 
 	pj_gettimeofday(&now);
-	PJ_LOG(4, (inv->pool->obj_name, 
-		   "Refresh session after %ds (expiration period=%ds)",
+	PJ_LOG(4, (inv->pool->obj_name,
+		   "Refreshing session after %ds (expiration period=%ds)",
 		   (now.sec-inv->timer->last_refresh.sec),
 		   inv->timer->setting.sess_expires));
@@ -415,15 +433,10 @@
     /* Print error message, if any */
     if (status != PJ_SUCCESS) {
-	char errmsg[PJ_ERR_MSG_SIZE];
-
 	if (tdata)
 	    pjsip_tx_data_dec_ref(tdata);
 
-	pj_strerror(status, errmsg, sizeof(errmsg));
-	PJ_LOG(2, (inv->pool->obj_name, "Session timer fails in %s session, "
-					"err code=%d (%s)",
-					(as_refresher? "refreshing" : 
-						       "terminating"),
-					status, errmsg));					
+	PJ_PERROR(2, (inv->pool->obj_name, status,
+		      "Error in %s session timer",
+		      (as_refresher? "refreshing" : "terminating")));
     }
 }
@@ -432,4 +445,5 @@
 static void start_timer(pjsip_inv_session *inv)
 {
+    const pj_str_t UPDATE = { "UPDATE", 6 };
     pjsip_timer *timer = inv->timer;
     pj_time_val delay = {0};
@@ -438,4 +452,12 @@
 
     stop_timer(inv);
+
+    inv->timer->use_update =
+	    (pjsip_dlg_remote_has_cap(inv->dlg, PJSIP_H_ALLOW, NULL,
+				      &UPDATE) == PJSIP_DIALOG_CAP_SUPPORTED);
+    if (!inv->timer->use_update) {
+	/* INVITE always needs SDP */
+	inv->timer->with_sdp = PJ_TRUE;
+    }
 
     pj_timer_entry_init(&timer->timer,
@@ -838,6 +860,4 @@
 	    inv->timer->refresher = TR_UAC;
 
-	PJ_TODO(CHECK_IF_REMOTE_SUPPORT_UPDATE);
-
 	/* Remember our role in this transaction */
 	inv->timer->role = PJSIP_ROLE_UAC;
@@ -846,4 +866,22 @@
 	inv->timer->active = PJ_TRUE;
 	start_timer(inv);
+
+    } else if (pjsip_method_cmp(&rdata->msg_info.cseq->method,
+				&pjsip_update_method) == 0 &&
+	       msg->line.status.code >= 400 && msg->line.status.code < 600)
+    {
+	/* This is to handle error response to previous UPDATE that was
+	 * sent without SDP. In this case, retry sending UPDATE but
+	 * with SDP this time.
+	 * Note: the additional expressions are to check that the
+	 *       UPDATE was really the one sent by us, not by other
+	 *       call components (e.g. to change codec)
+	 */
+	if (inv->timer->timer.id == 0 && inv->timer->use_update &&
+	    inv->timer->with_sdp == PJ_FALSE)
+	{
+	    inv->timer->with_sdp = PJ_TRUE;
+	    timer_cb(NULL, &inv->timer->timer);
+	}
     }
 
Index: /pjproject/trunk/pjsip/src/pjsua-lib/pjsua_media.c
===================================================================
--- /pjproject/trunk/pjsip/src/pjsua-lib/pjsua_media.c	(revision 3214)
+++ /pjproject/trunk/pjsip/src/pjsua-lib/pjsua_media.c	(revision 3215)
@@ -856,8 +856,23 @@
 				&pjsua_var.calls[id].med_rtp_addr))
 	    {
+		pj_bool_t use_update;
+		const pj_str_t STR_UPDATE = { "UPDATE", 6 };
+		pjsip_dialog_cap_status support_update;
+		pjsip_dialog *dlg;
+
+		dlg = pjsua_var.calls[id].inv->dlg;
+		support_update = pjsip_dlg_remote_has_cap(dlg, PJSIP_H_ALLOW,
+							  NULL, &STR_UPDATE);
+		use_update = (support_update == PJSIP_DIALOG_CAP_SUPPORTED);
+
 		PJ_LOG(4,(THIS_FILE, 
 		          "ICE default transport address has changed for "
-			  "call %d, sending UPDATE", id));
-		pjsua_call_update(id, 0, NULL);
+			  "call %d, sending %s", id,
+			  (use_update ? "UPDATE" : "re-INVITE")));
+
+		if (use_update)
+		    pjsua_call_update(id, 0, NULL);
+		else
+		    pjsua_call_reinvite(id, 0, NULL);
 	    }
 	}
Index: /pjproject/trunk/tests/pjsua/scripts-sipp/uas-timer-reinvite.xml
===================================================================
--- /pjproject/trunk/tests/pjsua/scripts-sipp/uas-timer-reinvite.xml	(revision 3215)
+++ /pjproject/trunk/tests/pjsua/scripts-sipp/uas-timer-reinvite.xml	(revision 3215)
@@ -0,0 +1,108 @@
+<?xml version="1.0" encoding="ISO-8859-1" ?>
+<!DOCTYPE scenario SYSTEM "sipp.dtd">
+
+<!-- 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             -->
+
+
+<!--                                                                    -->
+<!--   Session timer where UAS doesn't indicate support for UPDATE.     -->
+<!--   In this case, UAC MUST use re-INVITE with SDP.                   -->
+
+<scenario name="Basic UAS responder">
+  <recv request="INVITE" crlf="true">
+  </recv>
+
+  <send retrans="500">
+    <![CDATA[
+
+      SIP/2.0 200 OK
+      [last_Via:]
+      [last_From:]
+      [last_To:];tag=[call_number]
+      [last_Call-ID:]
+      [last_CSeq:]
+      Contact: <sip:[local_ip]:[local_port];transport=[transport]> 
+      Require: timer
+      Session-Expires: 90;refresher=uac
+      Content-Type: application/sdp
+      Content-Length: [len]
+ 
+      v=0
+      o=Some-UserAgent 68 210 IN IP4 [local_ip]
+      s=SIP Call
+      c=IN IP4 [local_ip]
+      t=0 0
+      m=audio 17294 RTP/AVP 0 101
+      c=IN IP4 [local_ip]
+      a=rtpmap:101 telephone-event/8000
+      a=fmtp:101 0-16
+    ]]>
+  </send>
+
+  <recv request="ACK"
+        optional="true"
+        rtd="true"
+        crlf="true"> 
+  </recv> 
+ 
+  <recv request="INVITE" crlf="true">
+  </recv>
+
+  <send retrans="500">
+    <![CDATA[
+
+      SIP/2.0 200 OK
+      [last_Via:]
+      [last_From:]
+      [last_To:];tag=[call_number]
+      [last_Call-ID:]
+      [last_CSeq:]
+      Contact: <sip:[local_ip]:[local_port];transport=[transport]> 
+      Require: timer
+      Session-Expires: 90;refresher=uac
+      Content-Type: application/sdp
+      Content-Length: [len]
+ 
+      v=0
+      o=Some-UserAgent 68 210 IN IP4 [local_ip]
+      s=SIP Call
+      c=IN IP4 [local_ip]
+      t=0 0
+      m=audio 17294 RTP/AVP 0 101
+      c=IN IP4 [local_ip]
+      a=rtpmap:101 telephone-event/8000
+      a=fmtp:101 0-16
+    ]]>
+  </send>
+
+  <recv request="ACK"
+        rtd="true"
+        crlf="true"> 
+  </recv> 
+
+
+  <!-- Keep the call open for a while in case the 200 is lost to be     -->
+  <!-- able to retransmit it if we receive the BYE again.               -->
+  <pause milliseconds="4000"/>
+
+  <!-- definition of the response time repartition table (unit is ms)   -->
+  <ResponseTimeRepartition value="10, 20, 30, 40, 50, 100, 150, 200"/>
+
+  <!-- definition of the call length repartition table (unit is ms)     -->
+  <CallLengthRepartition value="10, 50, 100, 500, 1000, 5000, 10000"/>
+
+</scenario>
+
Index: /pjproject/trunk/tests/pjsua/scripts-sipp/uas-timer-update.xml
===================================================================
--- /pjproject/trunk/tests/pjsua/scripts-sipp/uas-timer-update.xml	(revision 3215)
+++ /pjproject/trunk/tests/pjsua/scripts-sipp/uas-timer-update.xml	(revision 3215)
@@ -0,0 +1,126 @@
+<?xml version="1.0" encoding="ISO-8859-1" ?>
+<!DOCTYPE scenario SYSTEM "sipp.dtd">
+
+<!-- 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             -->
+
+
+<!--                                                                    -->
+<!--   Session timer where UAS incidates support for UPDATE.            -->
+<!--   In this case, UAC will first use empty UPDATE, which we          -->
+<!--   will reply with 400. UAC MUST retry sending UPDATE with SDP.     -->
+
+<scenario name="Basic UAS responder">
+  <recv request="INVITE" crlf="true">
+  </recv>
+
+  <send retrans="500">
+    <![CDATA[
+
+      SIP/2.0 200 OK
+      [last_Via:]
+      [last_From:]
+      [last_To:];tag=[call_number]
+      [last_Call-ID:]
+      [last_CSeq:]
+      Contact: <sip:[local_ip]:[local_port];transport=[transport]> 
+      Allow: UPDATE, INVITE
+      Require: timer
+      Session-Expires: 90;refresher=uac
+      Content-Type: application/sdp
+      Content-Length: [len]
+ 
+      v=0
+      o=Some-UserAgent 68 210 IN IP4 [local_ip]
+      s=SIP Call
+      c=IN IP4 [local_ip]
+      t=0 0
+      m=audio 17294 RTP/AVP 0 101
+      c=IN IP4 [local_ip]
+      a=rtpmap:101 telephone-event/8000
+      a=fmtp:101 0-16
+    ]]>
+  </send>
+
+  <recv request="ACK"
+        optional="true"
+        rtd="true"
+        crlf="true"> 
+  </recv> 
+ 
+  <recv request="UPDATE" crlf="true">
+  </recv>
+
+  <send>
+    <![CDATA[
+
+      SIP/2.0 400 Want SDP Body
+      [last_Via:]
+      [last_From:]
+      [last_To:];tag=[call_number]
+      [last_Call-ID:]
+      [last_CSeq:]
+      Contact: <sip:[local_ip]:[local_port];transport=[transport]> 
+      Allow: INVITE
+      Require: timer
+      Session-Expires: 90;refresher=uac
+      Content-Length: 0
+    ]]>
+  </send>
+
+  <recv request="UPDATE" crlf="true">
+  </recv>
+
+  <send>
+    <![CDATA[
+
+      SIP/2.0 200 OK
+      [last_Via:]
+      [last_From:]
+      [last_To:];tag=[call_number]
+      [last_Call-ID:]
+      [last_CSeq:]
+      Contact: <sip:[local_ip]:[local_port];transport=[transport]> 
+      Allow: INVITE
+      Require: timer
+      Session-Expires: 90;refresher=uac
+      Content-Type: application/sdp
+      Content-Length: [len]
+ 
+      v=0
+      o=Some-UserAgent 68 210 IN IP4 [local_ip]
+      s=SIP Call
+      c=IN IP4 [local_ip]
+      t=0 0
+      m=audio 17294 RTP/AVP 0 101
+      c=IN IP4 [local_ip]
+      a=rtpmap:101 telephone-event/8000
+      a=fmtp:101 0-16
+    ]]>
+  </send>
+
+
+  <!-- Keep the call open for a while in case the 200 is lost to be     -->
+  <!-- able to retransmit it if we receive the BYE again.               -->
+  <pause milliseconds="4000"/>
+
+  <!-- definition of the response time repartition table (unit is ms)   -->
+  <ResponseTimeRepartition value="10, 20, 30, 40, 50, 100, 150, 200"/>
+
+  <!-- definition of the call length repartition table (unit is ms)     -->
+  <CallLengthRepartition value="10, 50, 100, 500, 1000, 5000, 10000"/>
+
+</scenario>
+
