Index: pjproject/trunk/pjsip-apps/src/pjsua/pjsua_app.c
===================================================================
--- pjproject/trunk/pjsip-apps/src/pjsua/pjsua_app.c	(revision 2190)
+++ pjproject/trunk/pjsip-apps/src/pjsua/pjsua_app.c	(revision 2191)
@@ -2211,5 +2211,10 @@
     ring_stop(call_id);
 
-    if (call_info.media_status == PJSUA_CALL_MEDIA_ACTIVE) {
+    /* Connect ports appropriately when media status is ACTIVE or REMOTE HOLD,
+     * otherwise we should NOT connect the ports.
+     */
+    if (call_info.media_status == PJSUA_CALL_MEDIA_ACTIVE ||
+	call_info.media_status == PJSUA_CALL_MEDIA_REMOTE_HOLD)
+    {
 	pj_bool_t connect_sound = PJ_TRUE;
 
@@ -2279,26 +2284,41 @@
 	    }
 	}
-
+    }
+
+    /* Handle media status */
+    switch (call_info.media_status) {
+    case PJSUA_CALL_MEDIA_ACTIVE:
 	PJ_LOG(3,(THIS_FILE, "Media for call %d is active", call_id));
-
-    } else if (call_info.media_status == PJSUA_CALL_MEDIA_LOCAL_HOLD) {
+	break;
+
+    case PJSUA_CALL_MEDIA_LOCAL_HOLD:
 	PJ_LOG(3,(THIS_FILE, "Media for call %d is suspended (hold) by local",
 		  call_id));
-    } else if (call_info.media_status == PJSUA_CALL_MEDIA_REMOTE_HOLD) {
+	break;
+
+    case PJSUA_CALL_MEDIA_REMOTE_HOLD:
 	PJ_LOG(3,(THIS_FILE, 
 		  "Media for call %d is suspended (hold) by remote",
 		  call_id));
-    } else if (call_info.media_status == PJSUA_CALL_MEDIA_ERROR) {
-	pj_str_t reason = pj_str("ICE negotiation failed");
-
-	PJ_LOG(1,(THIS_FILE,
+	break;
+
+    case PJSUA_CALL_MEDIA_ERROR:
+	PJ_LOG(3,(THIS_FILE,
 		  "Media has reported error, disconnecting call"));
-
-	pjsua_call_hangup(call_id, 500, &reason, NULL);
-
-    } else {
+	{
+	    pj_str_t reason = pj_str("ICE negotiation failed");
+	    pjsua_call_hangup(call_id, 500, &reason, NULL);
+	}
+	break;
+
+    case PJSUA_CALL_MEDIA_NONE:
 	PJ_LOG(3,(THIS_FILE, 
 		  "Media for call %d is inactive",
 		  call_id));
+	break;
+
+    default:
+	pj_assert(!"Unhandled media status");
+	break;
     }
 }
Index: pjproject/trunk/pjsip/include/pjsua-lib/pjsua_internal.h
===================================================================
--- pjproject/trunk/pjsip/include/pjsua-lib/pjsua_internal.h	(revision 2190)
+++ pjproject/trunk/pjsip/include/pjsua-lib/pjsua_internal.h	(revision 2191)
@@ -61,4 +61,5 @@
     pjsua_acc_id	 acc_id;    /**< Account index being used.	    */
     int			 secure_level;/**< Signaling security level.	    */
+    pj_bool_t		 local_hold;/**< Flag for call-hold by local.	    */
     pjsua_call_media_status media_st;/**< Media state.			    */
     pjmedia_dir		 media_dir; /**< Media direction.		    */
Index: pjproject/trunk/pjsip/src/pjsua-lib/pjsua_call.c
===================================================================
--- pjproject/trunk/pjsip/src/pjsua-lib/pjsua_call.c	(revision 2190)
+++ pjproject/trunk/pjsip/src/pjsua-lib/pjsua_call.c	(revision 2191)
@@ -67,8 +67,7 @@
 
 
-
-/* Create inactive SDP for call hold. */
-static pj_status_t create_inactive_sdp(pjsua_call *call,
-				       pjmedia_sdp_session **p_answer);
+/* Create SDP for call hold. */
+static pj_status_t create_sdp_of_call_hold(pjsua_call *call,
+					   pjmedia_sdp_session **p_answer);
 
 /* Update SDP version in the offer */
@@ -118,4 +117,5 @@
     call->rem_nat_type = PJ_STUN_NAT_TYPE_UNKNOWN;
     call->rem_srtp_use = PJMEDIA_SRTP_DISABLED;
+    call->local_hold = PJ_FALSE;
 }
 
@@ -1427,5 +1427,5 @@
     }
 
-    status = create_inactive_sdp(call, &sdp);
+    status = create_sdp_of_call_hold(call, &sdp);
     if (status != PJ_SUCCESS) {
 	pjsip_dlg_dec_lock(dlg);
@@ -1453,4 +1453,7 @@
 	return status;
     }
+
+    /* Set flag that local put the call on hold */
+    call->local_hold = PJ_TRUE;
 
     pjsip_dlg_dec_lock(dlg);
@@ -1488,8 +1491,11 @@
 
     /* Create SDP */
-    PJ_UNUSED_ARG(unhold);
-    PJ_TODO(create_active_inactive_sdp_based_on_unhold_arg);
-    status = pjsua_media_channel_create_sdp(call->index, call->inv->pool, 
-					    NULL, &sdp, NULL);
+    if (call->local_hold && !unhold) {
+	status = create_sdp_of_call_hold(call, &sdp);
+    } else {
+	status = pjsua_media_channel_create_sdp(call->index, call->inv->pool, 
+						NULL, &sdp, NULL);
+	call->local_hold = PJ_FALSE;
+    }
     if (status != PJ_SUCCESS) {
 	pjsua_perror(THIS_FILE, "Unable to get SDP from media endpoint", 
@@ -1574,8 +1580,10 @@
     status = pjsip_inv_send_msg( call->inv, tdata);
     if (status != PJ_SUCCESS) {
-	pjsua_perror(THIS_FILE, "Unable to send UPDAT Erequest", status);
+	pjsua_perror(THIS_FILE, "Unable to send UPDATE request", status);
 	pjsip_dlg_dec_lock(dlg);
 	return status;
     }
+
+    call->local_hold = PJ_FALSE;
 
     pjsip_dlg_dec_lock(dlg);
@@ -3013,15 +3021,10 @@
 
 
-/*
- * Create inactive SDP for call hold.
- */
-static pj_status_t create_inactive_sdp(pjsua_call *call,
-				       pjmedia_sdp_session **p_answer)
+/* Create SDP for call hold. */
+static pj_status_t create_sdp_of_call_hold(pjsua_call *call,
+					   pjmedia_sdp_session **p_answer)
 {
     pj_status_t status;
     pj_pool_t *pool;
-    pjmedia_sdp_conn *conn;
-    pjmedia_sdp_attr *attr;
-    pjmedia_transport_info tp_info;
     pjmedia_sdp_session *sdp;
 
@@ -3029,11 +3032,7 @@
     pool = call->inv->pool;
 
-    /* Get media socket info */
-    pjmedia_transport_info_init(&tp_info);
-    pjmedia_transport_get_info(call->med_tp, &tp_info);
-
     /* Create new offer */
-    status = pjmedia_endpt_create_sdp(pjsua_var.med_endpt, pool, 1,
-				      &tp_info.sock_info, &sdp);
+    status = pjsua_media_channel_create_sdp(call->index, pool, NULL, &sdp, 
+					    NULL);
     if (status != PJ_SUCCESS) {
 	pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
@@ -3041,21 +3040,28 @@
     }
 
-    /* Get SDP media connection line */
-    conn = sdp->media[0]->conn;
-    if (!conn)
-	conn = sdp->conn;
-
-    /* Modify address */
-    conn->addr = pj_str("0.0.0.0");
-
-    /* Remove existing directions attributes */
-    pjmedia_sdp_media_remove_all_attr(sdp->media[0], "sendrecv");
-    pjmedia_sdp_media_remove_all_attr(sdp->media[0], "sendonly");
-    pjmedia_sdp_media_remove_all_attr(sdp->media[0], "recvonly");
-    pjmedia_sdp_media_remove_all_attr(sdp->media[0], "inactive");
-
-    /* Add inactive attribute */
-    attr = pjmedia_sdp_attr_create(pool, "inactive", NULL);
-    pjmedia_sdp_media_add_attr(sdp->media[0], attr);
+    /* Call-hold is done by set the media direction to 'sendonly' 
+     * (PJMEDIA_DIR_ENCODING), except when current media direction is 
+     * 'inactive' (PJMEDIA_DIR_NONE).
+     * (See RFC 3264 Section 8.4 and RFC 4317 Section 3.1)
+     */
+    if (call->media_dir != PJMEDIA_DIR_ENCODING) {
+	pjmedia_sdp_attr *attr;
+
+	/* Remove existing directions attributes */
+	pjmedia_sdp_media_remove_all_attr(sdp->media[0], "sendrecv");
+	pjmedia_sdp_media_remove_all_attr(sdp->media[0], "sendonly");
+	pjmedia_sdp_media_remove_all_attr(sdp->media[0], "recvonly");
+	pjmedia_sdp_media_remove_all_attr(sdp->media[0], "inactive");
+
+	if (call->media_dir == PJMEDIA_DIR_ENCODING_DECODING) {
+	    /* Add sendonly attribute */
+	    attr = pjmedia_sdp_attr_create(pool, "sendonly", NULL);
+	    pjmedia_sdp_media_add_attr(sdp->media[0], attr);
+	} else {
+	    /* Add inactive attribute */
+	    attr = pjmedia_sdp_attr_create(pool, "inactive", NULL);
+	    pjmedia_sdp_media_add_attr(sdp->media[0], attr);
+	}
+    }
 
     *p_answer = sdp;
@@ -3063,5 +3069,4 @@
     return status;
 }
-
 
 /*
@@ -3071,9 +3076,7 @@
 				   const pjmedia_sdp_session *offer)
 {
-    const char *remote_state;
     pjsua_call *call;
     pjmedia_sdp_conn *conn;
     pjmedia_sdp_session *answer;
-    pj_bool_t is_remote_active;
     pj_status_t status;
 
@@ -3081,9 +3084,4 @@
 
     call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
-
-    /*
-     * See if remote is offering active media (i.e. not on-hold)
-     */
-    is_remote_active = PJ_TRUE;
 
     conn = offer->media[0]->conn;
@@ -3091,36 +3089,39 @@
 	conn = offer->conn;
 
+    /* Supply candidate answer */
+    PJ_LOG(4,(THIS_FILE, "Call %d: received updated media offer",
+	      call->index));
+
+    status = pjsua_media_channel_create_sdp(call->index, call->inv->pool, 
+					    offer, &answer, NULL);
+    if (status != PJ_SUCCESS) {
+	pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
+	PJSUA_UNLOCK();
+	return;
+    }
+
+    /* Check if offer's conn address is zero */
     if (pj_strcmp2(&conn->addr, "0.0.0.0")==0 ||
 	pj_strcmp2(&conn->addr, "0")==0)
     {
-	is_remote_active = PJ_FALSE;
-
-    } 
-    else if (pjmedia_sdp_media_find_attr2(offer->media[0], "inactive", NULL) ||
-	     pjmedia_sdp_media_find_attr2(offer->media[0], "sendonly", NULL))
-    {
-	is_remote_active = PJ_FALSE;
-    }
-
-    remote_state = (is_remote_active ? "active" : "inactive");
-
-    /* Supply candidate answer */
-    if (call->media_st == PJSUA_CALL_MEDIA_LOCAL_HOLD || !is_remote_active) {
-	PJ_LOG(4,(THIS_FILE, 
-		  "Call %d: RX new media offer, creating inactive SDP "
-		  "(media in offer is %s)", call->index, remote_state));
-	status = create_inactive_sdp( call, &answer );
-    } else {
-	PJ_LOG(4,(THIS_FILE, "Call %d: received updated media offer",
-		  call->index));
-
-	status = pjsua_media_channel_create_sdp(call->index, call->inv->pool, 
-					        offer, &answer, NULL);
-    }
-
-    if (status != PJ_SUCCESS) {
-	pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
-	PJSUA_UNLOCK();
-	return;
+	/* Modify address */
+	answer->conn->addr = pj_str("0.0.0.0");
+    }
+
+    /* Check if call is on-hold */
+    if (call->local_hold) {
+        pjmedia_sdp_attr *attr;
+
+	/* Remove existing directions attributes */
+	pjmedia_sdp_media_remove_all_attr(answer->media[0], "sendrecv");
+	pjmedia_sdp_media_remove_all_attr(answer->media[0], "sendonly");
+	pjmedia_sdp_media_remove_all_attr(answer->media[0], "recvonly");
+	pjmedia_sdp_media_remove_all_attr(answer->media[0], "inactive");
+
+	/* Keep call on-hold by setting 'sendonly' attribute.
+	 * (See RFC 3264 Section 8.4 and RFC 4317 Section 3.1)
+	 */
+	attr = pjmedia_sdp_attr_create(call->inv->pool, "sendonly", NULL);
+	pjmedia_sdp_media_add_attr(answer->media[0], attr);
     }
 
@@ -3150,9 +3151,9 @@
 
     /* See if we've put call on hold. */
-    if (call->media_st == PJSUA_CALL_MEDIA_LOCAL_HOLD) {
+    if (call->local_hold) {
 	PJ_LOG(4,(THIS_FILE, 
-		  "Call %d: call is on-hold locally, creating inactive SDP ",
+		  "Call %d: call is on-hold locally, creating call-hold SDP ",
 		  call->index));
-	status = create_inactive_sdp( call, offer );
+	status = create_sdp_of_call_hold( call, offer );
     } else {
 	PJ_LOG(4,(THIS_FILE, "Call %d: asked to send a new offer",
Index: pjproject/trunk/pjsip/src/pjsua-lib/pjsua_media.c
===================================================================
--- pjproject/trunk/pjsip/src/pjsua-lib/pjsua_media.c	(revision 2190)
+++ pjproject/trunk/pjsip/src/pjsua-lib/pjsua_media.c	(revision 2191)
@@ -1232,18 +1232,11 @@
 	pj_memcpy(&sess_info.stream_info[0], si, sizeof(pjmedia_stream_info));
 
-    /* Check if media is put on-hold */
+    /* Check if no media is active */
     if (sess_info.stream_cnt == 0 || si->dir == PJMEDIA_DIR_NONE)
     {
-
-	/* Determine who puts the call on-hold */
-	if (prev_media_st == PJSUA_CALL_MEDIA_ACTIVE) {
-	    if (pjmedia_sdp_neg_was_answer_remote(call->inv->neg)) {
-		/* It was local who offer hold */
-		call->media_st = PJSUA_CALL_MEDIA_LOCAL_HOLD;
-	    } else {
-		call->media_st = PJSUA_CALL_MEDIA_REMOTE_HOLD;
-	    }
-	}
-
+	/* Call media state */
+	call->media_st = PJSUA_CALL_MEDIA_NONE;
+
+	/* Call media direction */
 	call->media_dir = PJMEDIA_DIR_NONE;
 
@@ -1368,7 +1361,14 @@
 	}
 
-	/* Call's media state is active */
-	call->media_st = PJSUA_CALL_MEDIA_ACTIVE;
+	/* Call media direction */
 	call->media_dir = si->dir;
+
+	/* Call media state */
+	if (call->local_hold)
+	    call->media_st = PJSUA_CALL_MEDIA_LOCAL_HOLD;
+	else if (call->media_dir == PJMEDIA_DIR_DECODING)
+	    call->media_st = PJSUA_CALL_MEDIA_REMOTE_HOLD;
+	else
+	    call->media_st = PJSUA_CALL_MEDIA_ACTIVE;
     }
 
