Index: pjproject/trunk/pjnath/include/pjnath/ice_strans.h
===================================================================
--- pjproject/trunk/pjnath/include/pjnath/ice_strans.h	(revision 3211)
+++ pjproject/trunk/pjnath/include/pjnath/ice_strans.h	(revision 3212)
@@ -124,5 +124,10 @@
 
     /** Negotiation */
-    PJ_ICE_STRANS_OP_NEGOTIATION
+    PJ_ICE_STRANS_OP_NEGOTIATION,
+
+    /** This operatino is used to report failure in keep-alive operation.
+     *  Currently it is only used to report TURN Refresh failure.
+     */
+    PJ_ICE_STRANS_OP_KEEP_ALIVE
 
 } pj_ice_strans_op;
@@ -153,8 +158,8 @@
 
     /**
-     * Callback to report status.
+     * Callback to report status of various ICE operations.
      * 
      * @param ice_st	    The ICE stream transport.
-     * @param op	    The operation
+     * @param op	    The operation which status is being reported.
      * @param status	    Operation status.
      */
Index: pjproject/trunk/pjnath/src/pjnath/ice_strans.c
===================================================================
--- pjproject/trunk/pjnath/src/pjnath/ice_strans.c	(revision 3211)
+++ pjproject/trunk/pjnath/src/pjnath/ice_strans.c	(revision 3212)
@@ -153,4 +153,5 @@
     pj_turn_sock	*turn_sock;	/**< TURN relay transport.	*/
     pj_bool_t		 turn_log_off;	/**< TURN loggin off?		*/
+    unsigned		 turn_err_cnt;	/**< TURN disconnected count.	*/
 
     unsigned		 cand_cnt;	/**< # of candidates/aliaes.	*/
@@ -240,4 +241,102 @@
 			  &src->turn.auth_cred);
 }
+
+
+/*
+ * Add or update TURN candidate.
+ */
+static pj_status_t add_update_turn(pj_ice_strans *ice_st,
+				   pj_ice_strans_comp *comp)
+{
+    pj_turn_sock_cb turn_sock_cb;
+    pj_ice_sess_cand *cand = NULL;
+    unsigned i;
+    pj_status_t status;
+
+    /* Find relayed candidate in the component */
+    for (i=0; i<comp->cand_cnt; ++i) {
+	if (comp->cand_list[i].type == PJ_ICE_CAND_TYPE_RELAYED) {
+	    cand = &comp->cand_list[i];
+	    break;
+	}
+    }
+
+    /* If candidate is found, invalidate it first */
+    if (cand) {
+	cand->status = PJ_EPENDING;
+
+	/* Also if this component's default candidate is set to relay,
+	 * move it temporarily to something else.
+	 */
+	if (comp->default_cand == cand - comp->cand_list) {
+	    /* Init to something */
+	    comp->default_cand = 0;
+	    /* Use srflx candidate as the default, if any */
+	    for (i=0; i<comp->cand_cnt; ++i) {
+		if (comp->cand_list[i].type == PJ_ICE_CAND_TYPE_SRFLX) {
+		    comp->default_cand = i;
+		    break;
+		}
+	    }
+	}
+    }
+
+    /* Init TURN socket */
+    pj_bzero(&turn_sock_cb, sizeof(turn_sock_cb));
+    turn_sock_cb.on_rx_data = &turn_on_rx_data;
+    turn_sock_cb.on_state = &turn_on_state;
+
+    /* Override with component specific QoS settings, if any */
+    if (ice_st->cfg.comp[comp->comp_id-1].qos_type) {
+	ice_st->cfg.turn.cfg.qos_type =
+	    ice_st->cfg.comp[comp->comp_id-1].qos_type;
+    }
+    if (ice_st->cfg.comp[comp->comp_id-1].qos_params.flags) {
+	pj_memcpy(&ice_st->cfg.turn.cfg.qos_params,
+		  &ice_st->cfg.comp[comp->comp_id-1].qos_params,
+		  sizeof(ice_st->cfg.turn.cfg.qos_params));
+    }
+
+    /* Create the TURN transport */
+    status = pj_turn_sock_create(&ice_st->cfg.stun_cfg, ice_st->cfg.af,
+				 ice_st->cfg.turn.conn_type,
+				 &turn_sock_cb, &ice_st->cfg.turn.cfg,
+				 comp, &comp->turn_sock);
+    if (status != PJ_SUCCESS) {
+	return status;
+    }
+
+    /* Add pending job */
+    ///sess_add_ref(ice_st);
+
+    /* Start allocation */
+    status=pj_turn_sock_alloc(comp->turn_sock,
+			      &ice_st->cfg.turn.server,
+			      ice_st->cfg.turn.port,
+			      ice_st->cfg.resolver,
+			      &ice_st->cfg.turn.auth_cred,
+			      &ice_st->cfg.turn.alloc_param);
+    if (status != PJ_SUCCESS) {
+	///sess_dec_ref(ice_st);
+	return status;
+    }
+
+    /* Add relayed candidate with pending status if there's no existing one */
+    if (cand == NULL) {
+	cand = &comp->cand_list[comp->cand_cnt++];
+	cand->type = PJ_ICE_CAND_TYPE_RELAYED;
+	cand->status = PJ_EPENDING;
+	cand->local_pref = RELAY_PREF;
+	cand->transport_id = TP_TURN;
+	cand->comp_id = (pj_uint8_t) comp->comp_id;
+    }
+
+    PJ_LOG(4,(ice_st->obj_name,
+		  "Comp %d: TURN relay candidate waiting for allocation",
+		  comp->comp_id));
+
+    return PJ_SUCCESS;
+}
+
 
 /*
@@ -396,61 +495,5 @@
     /* Create TURN relay if configured. */
     if (ice_st->cfg.turn.server.slen) {
-	pj_turn_sock_cb turn_sock_cb;
-	pj_ice_sess_cand *cand;
-
-	/* Init TURN socket */
-	pj_bzero(&turn_sock_cb, sizeof(turn_sock_cb));
-	turn_sock_cb.on_rx_data = &turn_on_rx_data;
-	turn_sock_cb.on_state = &turn_on_state;
-
-	/* Override with component specific QoS settings, if any */
-	if (ice_st->cfg.comp[comp_id-1].qos_type) {
-	    ice_st->cfg.turn.cfg.qos_type = 
-		ice_st->cfg.comp[comp_id-1].qos_type;
-	}
-	if (ice_st->cfg.comp[comp_id-1].qos_params.flags) {
-	    pj_memcpy(&ice_st->cfg.turn.cfg.qos_params,
-		      &ice_st->cfg.comp[comp_id-1].qos_params,
-		      sizeof(ice_st->cfg.turn.cfg.qos_params));
-	}
-
-	/* Create the TURN transport */
-	status = pj_turn_sock_create(&ice_st->cfg.stun_cfg, ice_st->cfg.af,
-				     ice_st->cfg.turn.conn_type,
-				     &turn_sock_cb, &ice_st->cfg.turn.cfg, 
-				     comp, &comp->turn_sock);
-	if (status != PJ_SUCCESS) {
-	    return status;
-	}
-
-	/* Add pending job */
-	///sess_add_ref(ice_st);
-
-	/* Start allocation */
-	status=pj_turn_sock_alloc(comp->turn_sock,  
-				  &ice_st->cfg.turn.server,
-				  ice_st->cfg.turn.port,
-				  ice_st->cfg.resolver, 
-				  &ice_st->cfg.turn.auth_cred, 
-				  &ice_st->cfg.turn.alloc_param);
-	if (status != PJ_SUCCESS) {
-	    ///sess_dec_ref(ice_st);
-	    return status;
-	}
-
-	/* Add relayed candidate with pending status */
-	cand = &comp->cand_list[comp->cand_cnt++];
-	cand->type = PJ_ICE_CAND_TYPE_RELAYED;
-	cand->status = PJ_EPENDING;
-	cand->local_pref = RELAY_PREF;
-	cand->transport_id = TP_TURN;
-	cand->comp_id = (pj_uint8_t) comp_id;
-
-	PJ_LOG(4,(ice_st->obj_name, 
-		      "Comp %d: TURN relay candidate waiting for allocation",
-		      comp_id));
-
-	/* Set default candidate to relay */
-	comp->default_cand = cand - comp->cand_list;
+	add_update_turn(ice_st, comp);
     }
 
@@ -1582,4 +1625,6 @@
 	unsigned i;
 
+	comp->turn_err_cnt = 0;
+
 	/* Get allocation info */
 	pj_turn_sock_get_info(turn_sock, &rel_info);
@@ -1608,4 +1653,7 @@
 	cand->status = PJ_SUCCESS;
 
+	/* Set default candidate to relay */
+	comp->default_cand = cand - comp->cand_list;
+
 	PJ_LOG(4,(comp->ice_st->obj_name, 
 		  "Comp %d: TURN allocation complete, relay address is %s",
@@ -1619,4 +1667,6 @@
 	pj_turn_session_info info;
 
+	++comp->turn_err_cnt;
+
 	pj_turn_sock_get_info(turn_sock, &info);
 
@@ -1626,7 +1676,15 @@
 
 	/* Set session to fail if we're still initializing */
-	if (old_state < PJ_TURN_STATE_READY) {
+	if (comp->ice_st->state < PJ_ICE_STRANS_STATE_READY) {
 	    sess_fail(comp->ice_st, PJ_ICE_STRANS_OP_INIT,
-		      "TURN relay failed", info.last_status);
+		      "TURN allocation failed", info.last_status);
+	} else if (comp->turn_err_cnt > 1) {
+	    sess_fail(comp->ice_st, PJ_ICE_STRANS_OP_KEEP_ALIVE,
+		      "TURN refresh failed", info.last_status);
+	} else {
+	    PJ_PERROR(4,(comp->ice_st->obj_name, info.last_status,
+		      "Comp %d: TURN allocation failed, retrying",
+		      comp->comp_id));
+	    add_update_turn(comp->ice_st, comp);
 	}
     }
Index: pjproject/trunk/pjsip-apps/src/pjsua/pjsua_app.c
===================================================================
--- pjproject/trunk/pjsip-apps/src/pjsua/pjsua_app.c	(revision 3211)
+++ pjproject/trunk/pjsip-apps/src/pjsua/pjsua_app.c	(revision 3212)
@@ -2910,4 +2910,16 @@
 
 /*
+ * Notification on ICE error.
+ */
+static void on_ice_transport_error(int index, pj_ice_strans_op op,
+				   pj_status_t status, void *param)
+{
+    PJ_UNUSED_ARG(op);
+    PJ_UNUSED_ARG(param);
+    PJ_PERROR(1,(THIS_FILE, status,
+	         "ICE keep alive failure for transport %d", index));
+}
+
+/*
  * Print buddy list.
  */
@@ -4506,4 +4518,5 @@
     app_config.cfg.cb.on_mwi_info = &on_mwi_info;
     app_config.cfg.cb.on_transport_state = &on_transport_state;
+    app_config.cfg.cb.on_ice_transport_error = &on_ice_transport_error;
     app_config.log_cfg.cb = log_cb;
 
Index: pjproject/trunk/pjsip/include/pjsua-lib/pjsua.h
===================================================================
--- pjproject/trunk/pjsip/include/pjsua-lib/pjsua.h	(revision 3211)
+++ pjproject/trunk/pjsip/include/pjsua-lib/pjsua.h	(revision 3212)
@@ -850,4 +850,17 @@
     pjsip_tp_state_callback on_transport_state;
 
+    /**
+     * This callback is called to report error in ICE media transport.
+     * Currently it is used to report TURN Refresh error.
+     *
+     * @param index	Transport index.
+     * @param op	Operation which trigger the failure.
+     * @param status	Error status.
+     * @param param	Additional info about the event. Currently this will
+     * 			always be set to NULL.
+     */
+    void (*on_ice_transport_error)(int index, pj_ice_strans_op op,
+				   pj_status_t status, void *param);
+
 } pjsua_callback;
 
Index: pjproject/trunk/pjsip/src/pjsua-lib/pjsua_media.c
===================================================================
--- pjproject/trunk/pjsip/src/pjsua-lib/pjsua_media.c	(revision 3211)
+++ pjproject/trunk/pjsip/src/pjsua-lib/pjsua_media.c	(revision 3212)
@@ -861,4 +861,14 @@
 		pjsua_call_update(id, 0, NULL);
 	    }
+	}
+	break;
+    case PJ_ICE_STRANS_OP_KEEP_ALIVE:
+	if (result != PJ_SUCCESS) {
+	    PJ_PERROR(4,(THIS_FILE, result,
+		         "ICE keep alive failure for transport %d", id));
+	}
+	if (pjsua_var.ua_cfg.cb.on_ice_transport_error) {
+	    (*pjsua_var.ua_cfg.cb.on_ice_transport_error)(id, op, result,
+							  NULL);
 	}
 	break;
