Index: pjproject/trunk/pjsip-apps/src/pjsua/pjsua_app.c
===================================================================
--- pjproject/trunk/pjsip-apps/src/pjsua/pjsua_app.c	(revision 6136)
+++ pjproject/trunk/pjsip-apps/src/pjsua/pjsua_app.c	(revision 6137)
@@ -1024,4 +1024,67 @@
 			       &app_config.auto_hangup_timer, 
 			       &delay);
+}
+
+/* IP change progress callback. */
+void on_ip_change_progress(pjsua_ip_change_op op,
+			   pj_status_t status,
+			   const pjsua_ip_change_op_info *info)
+{
+    char info_str[128];
+    pjsua_acc_info acc_info;
+    pjsua_transport_info tp_info;
+
+    if (status == PJ_SUCCESS) {
+	switch (op) {
+	case PJSUA_IP_CHANGE_OP_RESTART_LIS:
+	    pjsua_transport_get_info(info->lis_restart.transport_id, &tp_info);
+	    pj_ansi_snprintf(info_str, sizeof(info_str),
+			     "restart transport %.*s",
+			     tp_info.info.slen, tp_info.info.ptr);
+	    break;
+	case PJSUA_IP_CHANGE_OP_ACC_SHUTDOWN_TP:
+	    pjsua_acc_get_info(info->acc_shutdown_tp.acc_id, &acc_info);
+
+	    pj_ansi_snprintf(info_str, sizeof(info_str),
+			     "transport shutdown for account %.*s",
+			     acc_info.acc_uri.slen, acc_info.acc_uri.ptr);
+	    break;
+	case PJSUA_IP_CHANGE_OP_ACC_UPDATE_CONTACT:
+	    pjsua_acc_get_info(info->acc_shutdown_tp.acc_id, &acc_info);
+	    if (info->acc_update_contact.code) {
+		pj_ansi_snprintf(info_str, sizeof(info_str),
+				 "update contact for account %.*s, code[%d]",
+				 acc_info.acc_uri.slen, acc_info.acc_uri.ptr,
+				 info->acc_update_contact.code);
+	    } else {
+		pj_ansi_snprintf(info_str, sizeof(info_str),
+				 "update contact for account %.*s",
+				 acc_info.acc_uri.slen, acc_info.acc_uri.ptr);
+	    }
+	    break;
+	case PJSUA_IP_CHANGE_OP_ACC_HANGUP_CALLS:
+	    pjsua_acc_get_info(info->acc_shutdown_tp.acc_id, &acc_info);
+	    pj_ansi_snprintf(info_str, sizeof(info_str),
+			     "hangup call for account %.*s, call_id[%d]",
+			     acc_info.acc_uri.slen, acc_info.acc_uri.ptr,
+			     info->acc_hangup_calls.call_id);
+	    break;
+	case PJSUA_IP_CHANGE_OP_ACC_REINVITE_CALLS:
+	    pjsua_acc_get_info(info->acc_shutdown_tp.acc_id, &acc_info);
+	    pj_ansi_snprintf(info_str, sizeof(info_str),
+			     "reinvite call for account %.*s, call_id[%d]",
+			     acc_info.acc_uri.slen, acc_info.acc_uri.ptr,
+			     info->acc_reinvite_calls.call_id);
+	    break;
+	case PJSUA_IP_CHANGE_OP_COMPLETED:
+	    pj_ansi_snprintf(info_str, sizeof(info_str),
+			     "done");
+	    break;
+	}
+	PJ_LOG(3,(THIS_FILE, "IP change progress report : %s", info_str));
+
+    } else {
+	PJ_PERROR(3,(THIS_FILE, status, "IP change progress fail"));
+    }
 }
 
@@ -1301,4 +1364,5 @@
     app_config.cfg.cb.on_snd_dev_operation = &on_snd_dev_operation;
     app_config.cfg.cb.on_call_media_event = &on_call_media_event;
+    app_config.cfg.cb.on_ip_change_progress = &on_ip_change_progress;
 #ifdef TRANSPORT_ADAPTER_SAMPLE
     app_config.cfg.cb.on_create_media_transport = &on_create_media_transport;
Index: pjproject/trunk/pjsip-apps/src/pjsua/pjsua_app_legacy.c
===================================================================
--- pjproject/trunk/pjsip-apps/src/pjsua/pjsua_app_legacy.c	(revision 6136)
+++ pjproject/trunk/pjsip-apps/src/pjsua/pjsua_app_legacy.c	(revision 6137)
@@ -1756,4 +1756,10 @@
 }
 
+static void ui_handle_ip_change()
+{
+    pjsua_ip_change_param param;
+    pjsua_ip_change_param_default(&param);
+    pjsua_handle_ip_change(&param);
+}
 
 /*
@@ -2007,4 +2013,8 @@
 	    break;
 
+	case 'I': /* Handle IP change. */
+	    ui_handle_ip_change();
+	    break;
+
 	default:
 	    if (menuin[0] != '\n' && menuin[0] != '\r') {
Index: pjproject/trunk/pjsip-apps/src/swig/symbols.i
===================================================================
--- pjproject/trunk/pjsip-apps/src/swig/symbols.i	(revision 6136)
+++ pjproject/trunk/pjsip-apps/src/swig/symbols.i	(revision 6137)
@@ -848,5 +848,6 @@
   PJSUA_IP_CHANGE_OP_ACC_UPDATE_CONTACT,
   PJSUA_IP_CHANGE_OP_ACC_HANGUP_CALLS,
-  PJSUA_IP_CHANGE_OP_ACC_REINVITE_CALLS
+  PJSUA_IP_CHANGE_OP_ACC_REINVITE_CALLS,
+  PJSUA_IP_CHANGE_OP_COMPLETED
 } pjsua_ip_change_op;
 
Index: pjproject/trunk/pjsip/include/pjsua-lib/pjsua.h
===================================================================
--- pjproject/trunk/pjsip/include/pjsua-lib/pjsua.h	(revision 6136)
+++ pjproject/trunk/pjsip/include/pjsua-lib/pjsua.h	(revision 6137)
@@ -749,5 +749,10 @@
      * The re-INVITE call process.
      */
-    PJSUA_IP_CHANGE_OP_ACC_REINVITE_CALLS
+    PJSUA_IP_CHANGE_OP_ACC_REINVITE_CALLS,
+
+    /**
+     * The ip change process has completed.
+     */
+    PJSUA_IP_CHANGE_OP_COMPLETED
 
 } pjsua_ip_change_op;
Index: pjproject/trunk/pjsip/include/pjsua-lib/pjsua_internal.h
===================================================================
--- pjproject/trunk/pjsip/include/pjsua-lib/pjsua_internal.h	(revision 6136)
+++ pjproject/trunk/pjsip/include/pjsua-lib/pjsua_internal.h	(revision 6137)
@@ -898,4 +898,9 @@
 pj_status_t pjsua_acc_handle_call_on_ip_change(pjsua_acc *acc);
 
+/*
+ * End IP change process per account.
+ */
+void pjsua_acc_end_ip_change(pjsua_acc *acc);
+
 PJ_END_DECL
 
Index: pjproject/trunk/pjsip/src/pjsua-lib/pjsua_acc.c
===================================================================
--- pjproject/trunk/pjsip/src/pjsua-lib/pjsua_acc.c	(revision 6136)
+++ pjproject/trunk/pjsip/src/pjsua-lib/pjsua_acc.c	(revision 6137)
@@ -693,5 +693,5 @@
     acc->via_tp = NULL;
     acc->next_rtp_port = 0;
-    acc->ip_change_op = PJSUA_IP_CHANGE_OP_NULL;    
+    acc->ip_change_op = PJSUA_IP_CHANGE_OP_NULL;
 
     /* Remove from array */
@@ -2423,10 +2423,10 @@
 
 	    pj_bzero(&ip_chg_info, sizeof(ip_chg_info));
-	    pjsip_regc_get_info(param->regc, &rinfo);	    
+	    pjsip_regc_get_info(param->regc, &rinfo);
 	    ip_chg_info.acc_update_contact.acc_id = acc->index;
 	    ip_chg_info.acc_update_contact.code = param->code;
 	    ip_chg_info.acc_update_contact.is_register = !param->is_unreg;
-	    (*pjsua_var.ua_cfg.cb.on_ip_change_progress)(acc->ip_change_op, 
-							 param->status, 
+	    (*pjsua_var.ua_cfg.cb.on_ip_change_progress)(acc->ip_change_op,
+							 param->status,
 							 &ip_chg_info);
 	}
@@ -2441,6 +2441,6 @@
 
 		status = pjsua_acc_set_registration(acc->index, PJ_TRUE);
-		if ((status != PJ_SUCCESS) && 
-		    pjsua_var.ua_cfg.cb.on_ip_change_progress) 
+		if ((status != PJ_SUCCESS) &&
+		    pjsua_var.ua_cfg.cb.on_ip_change_progress)
 		{
 		    pjsua_ip_change_op_info ip_chg_info;
@@ -2450,16 +2450,20 @@
 		    ip_chg_info.acc_update_contact.is_register = PJ_TRUE;
 		    (*pjsua_var.ua_cfg.cb.on_ip_change_progress)(
-							    acc->ip_change_op, 
-							    status, 
+							    acc->ip_change_op,
+							    status,
 							    &ip_chg_info);
+
+		    pjsua_acc_end_ip_change(acc);
 		}
 	    } else {
                 /* Avoid deadlock issue when sending BYE or Re-INVITE.  */
-		pjsua_schedule_timer2(&handle_call_on_ip_change_cb, 
+		pjsua_schedule_timer2(&handle_call_on_ip_change_cb,
 				      (void*)acc, 0);
 	    }
-	} 
-    }
-    
+	} else {
+	    pjsua_acc_end_ip_change(acc);
+	}
+    }
+
     PJSUA_UNLOCK();
     pj_log_pop_indent();
@@ -3903,7 +3907,20 @@
 	    pjsip_regc_release_transport(pjsua_var.acc[i].regc);
 
-	    if (pjsua_var.acc[i].ip_change_op == 
+	    if (pjsua_var.acc[i].ip_change_op ==
 					    PJSUA_IP_CHANGE_OP_ACC_SHUTDOWN_TP)
 	    {
+		/* Before progressing to next step, report here. */
+		if (pjsua_var.ua_cfg.cb.on_ip_change_progress) {
+		    pjsua_ip_change_op_info ch_info;
+
+		    pj_bzero(&ch_info, sizeof(ch_info));
+		    ch_info.acc_shutdown_tp.acc_id = acc->index;
+
+		    pjsua_var.ua_cfg.cb.on_ip_change_progress(
+							 acc->ip_change_op,
+							 PJ_SUCCESS,
+							 &ch_info);
+		}
+
 		if (acc->cfg.allow_contact_rewrite) {
 		    pjsua_acc_update_contact_on_ip_change(acc);
@@ -3940,15 +3957,21 @@
     status = pjsua_acc_set_registration(acc->index, !need_unreg);
 
-    if ((status != PJ_SUCCESS) && (pjsua_var.ua_cfg.cb.on_ip_change_progress)) 
+    if ((status != PJ_SUCCESS) && (pjsua_var.ua_cfg.cb.on_ip_change_progress)
+	&& (acc->ip_change_op == PJSUA_IP_CHANGE_OP_ACC_UPDATE_CONTACT))
     {
+	/* If update contact fails, notification might already been triggered
+	 * from registration callback.
+	 */
 	pjsua_ip_change_op_info info;
-	
+
 	pj_bzero(&info, sizeof(info));
 	info.acc_update_contact.acc_id = acc->index;
-	info.acc_update_contact.is_register = !need_unreg;	
+	info.acc_update_contact.is_register = !need_unreg;
 
 	pjsua_var.ua_cfg.cb.on_ip_change_progress(acc->ip_change_op,
 						  status,
 						  &info);
+
+	pjsua_acc_end_ip_change(acc);
     }
     return status;
@@ -3965,5 +3988,5 @@
 
     PJSUA_LOCK();
-    if (acc->cfg.ip_change_cfg.hangup_calls || 
+    if (acc->cfg.ip_change_cfg.hangup_calls ||
 	acc->cfg.ip_change_cfg.reinvite_flags)
     {
@@ -3977,5 +4000,7 @@
 	    }
 
-	    if (acc->cfg.ip_change_cfg.hangup_calls) {
+	    if ((acc->cfg.ip_change_cfg.hangup_calls) &&
+		(call_info.state >= PJSIP_INV_STATE_EARLY))
+	    {
 		acc->ip_change_op = PJSUA_IP_CHANGE_OP_ACC_HANGUP_CALLS;
 		PJ_LOG(3, (THIS_FILE, "call to %.*s: hangup "
@@ -4032,7 +4057,39 @@
 	    }
 	}
-    }    
-    acc->ip_change_op = PJSUA_IP_CHANGE_OP_NULL;
+    }
+    pjsua_acc_end_ip_change(acc);
     PJSUA_UNLOCK();
     return status;
 }
+
+void pjsua_acc_end_ip_change(pjsua_acc *acc)
+{
+    int i = 0;
+    pj_bool_t all_done = PJ_TRUE;
+
+    PJSUA_LOCK();
+    if (acc && acc->ip_change_op < PJSUA_IP_CHANGE_OP_COMPLETED) {
+	PJ_LOG(3, (THIS_FILE, "IP address change handling for acc %d "
+		   "completed", acc->index));
+	acc->ip_change_op = PJSUA_IP_CHANGE_OP_COMPLETED;
+	if (pjsua_var.acc_cnt) {
+	    for (; i < (int)PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
+		if (pjsua_var.acc[i].valid &&
+		    pjsua_var.acc[i].ip_change_op !=
+						  PJSUA_IP_CHANGE_OP_COMPLETED)
+		{
+		    all_done = PJ_FALSE;
+		    break;
+		}
+	    }
+	}
+    }
+    if (all_done && pjsua_var.ua_cfg.cb.on_ip_change_progress) {
+	PJ_LOG(3, (THIS_FILE, "IP address change handling completed"));
+	pjsua_var.ua_cfg.cb.on_ip_change_progress(
+					    PJSUA_IP_CHANGE_OP_COMPLETED,
+					    PJ_SUCCESS,
+					    NULL);
+    }
+    PJSUA_UNLOCK();
+}
Index: pjproject/trunk/pjsip/src/pjsua-lib/pjsua_core.c
===================================================================
--- pjproject/trunk/pjsip/src/pjsua-lib/pjsua_core.c	(revision 6136)
+++ pjproject/trunk/pjsip/src/pjsua-lib/pjsua_core.c	(revision 6137)
@@ -3572,10 +3572,19 @@
 
     PJSUA_LOCK();
+
+    if (pjsua_var.acc_cnt == 0) {
+	PJ_LOG(3, (THIS_FILE,
+		   "No account is set, IP change handling will stop"));
+	pjsua_acc_end_ip_change(NULL);
+	PJSUA_UNLOCK();
+	return status;
+    }
+
     /* Reset ip_change_active flag. */
     for (; i < (int)PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
 	pjsua_var.acc[i].ip_change_op = PJSUA_IP_CHANGE_OP_NULL;
 	acc_done[i] = PJ_FALSE;
-    }    
-    
+    }
+
     for (i = 0; i < (int)PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
 	pj_bool_t shutdown_transport = PJ_FALSE;
@@ -3595,8 +3604,8 @@
 
 	    pjsip_regc_get_info(acc->regc, &regc_info);
-	    
+
 	    /* Check if transport restart listener succeed. */
 	    for (; j < PJ_ARRAY_SIZE(pjsua_var.tpdata); ++j) {
-		if (pjsua_var.tpdata[j].data.ptr != NULL && 
+		if (pjsua_var.tpdata[j].data.ptr != NULL &&
 		  pjsua_var.tpdata[j].restart_status != PJ_SUCCESS &&
 		  pjsua_var.tpdata[j].type == regc_info.transport->key.type)
@@ -3605,5 +3614,5 @@
 					   == regc_info.transport->factory) ||
 			(pjsua_var.tpdata[j].data.tp
-					       == regc_info.transport)) 
+					       == regc_info.transport))
 		    {
 			found_restart_tp_fail = PJ_TRUE;
@@ -3623,5 +3632,5 @@
 		    }
 		}
-		    
+		pjsua_acc_end_ip_change(acc);
 		continue;
 	    }
@@ -3633,7 +3642,7 @@
 		shutdown_transport = acc->cfg.ip_change_cfg.shutdown_tp;
 		shut_acc_ids[shut_acc_cnt++] = acc->index;
-	    }	
+	    }
 	} else if (acc->cfg.reg_uri.slen &&
-		   acc->reg_last_code != PJSIP_SC_BAD_GATEWAY &&
+		   acc->reg_last_code != PJSIP_SC_OK &&
 		   acc->reg_last_code != PJSIP_SC_REQUEST_TIMEOUT &&
 		   acc->reg_last_code != PJSIP_SC_INTERNAL_SERVER_ERROR &&
@@ -3641,9 +3650,13 @@
 		   acc->reg_last_code != PJSIP_SC_SERVICE_UNAVAILABLE &&
 		   acc->reg_last_code != PJSIP_SC_SERVER_TIMEOUT &&
-		   acc->reg_last_code != PJSIP_SC_TEMPORARILY_UNAVAILABLE) 
+		   acc->reg_last_code != PJSIP_SC_TEMPORARILY_UNAVAILABLE)
 	{
+	    PJ_LOG(3, (THIS_FILE, "Permanent registration failure, "
+		       "IP change handling will stop for acc %d", acc->index));
+
+	    pjsua_acc_end_ip_change(acc);
 	    continue;
-	} 
-	pj_ansi_snprintf(acc_id, sizeof(acc_id), "#%d", i);	
+	}
+	pj_ansi_snprintf(acc_id, sizeof(acc_id), "#%d", i);
 
 	if (transport) {
@@ -3656,5 +3669,5 @@
 
 		if (!next_acc->valid || !next_acc->regc ||
-		    (next_acc->ip_change_op > PJSUA_IP_CHANGE_OP_NULL)) 
+		    (next_acc->ip_change_op > PJSUA_IP_CHANGE_OP_NULL))
 		{
 		    continue;
@@ -3683,5 +3696,5 @@
 	if (shutdown_transport) {
 	    unsigned j;
-	    /* Shutdown the transport. */	    
+	    /* Shutdown the transport. */
 	    PJ_LOG(3, (THIS_FILE, "Shutdown transport %s used by account %s "
 		       "triggered by IP change", transport->obj_name, acc_id));
@@ -3694,22 +3707,4 @@
 
 	    status = pjsip_transport_shutdown2(transport, PJ_TRUE);
-
-	    /* Report progress to each acc which uses the same transport. */
-	    for (j = 0; j < shut_acc_cnt; ++j) {
-		pjsua_acc *tmp_acc = &pjsua_var.acc[shut_acc_ids[j]];
-
-		if (pjsua_var.ua_cfg.cb.on_ip_change_progress) {
-		    pjsua_ip_change_op_info info;
-
-		    pj_bzero(&info, sizeof(info));
-		    info.acc_shutdown_tp.acc_id = tmp_acc->index;
-
-		    pjsua_var.ua_cfg.cb.on_ip_change_progress(
-							 tmp_acc->ip_change_op,
-							 status,
-							 &info);
-		}
-
-	    }
 	} else {
 	    acc_done[i] = PJ_TRUE;
@@ -3726,17 +3721,17 @@
 
 
-static pj_status_t restart_listener(pjsua_transport_id id, 
+static pj_status_t restart_listener(pjsua_transport_id id,
 				    unsigned restart_lis_delay)
 {
     pj_sockaddr bind_addr;
     pjsua_transport_info tp_info;
-    pj_status_t status;    
-
-    pjsua_transport_get_info(id, &tp_info);        
+    pj_status_t status;
+
+    pjsua_transport_get_info(id, &tp_info);
     pj_sockaddr_init(pjsip_transport_type_get_af(tp_info.type),
 		     &bind_addr,
 		     NULL,
 		     pj_sockaddr_get_port(&tp_info.local_addr));
-    
+
     switch (tp_info.type) {
     case PJSIP_TRANSPORT_UDP:
@@ -3771,16 +3766,15 @@
     }
 
-    PJ_PERROR(3,(THIS_FILE, status, "Listener %s restart",
-		 pjsip_transport_get_type_name(tp_info.type)));
+    PJ_PERROR(3,(THIS_FILE, status, "Listener %.*s restart",
+		 tp_info.info.slen, tp_info.info.ptr));
 
     if (status != PJ_SUCCESS && (restart_lis_delay > 0)) {
 	/* Try restarting again, with delay. */
-	pjsua_schedule_timer2(&restart_listener_cb, 
-			      (void*)(pj_size_t)id, 
+	pjsua_schedule_timer2(&restart_listener_cb,
+			      (void*)(pj_size_t)id,
 			      restart_lis_delay);
 
-	PJ_LOG(3,(THIS_FILE, "Retry listener %s restart in %d ms",
-		     pjsip_transport_get_type_name(tp_info.type),
-		     restart_lis_delay));
+	PJ_LOG(3,(THIS_FILE, "Retry listener %.*s restart in %d ms",
+		  tp_info.info.slen, tp_info.info.ptr, restart_lis_delay));
 
 	status = PJ_SUCCESS;
@@ -3797,6 +3791,6 @@
 	    info.lis_restart.transport_id = id;
 	    pjsua_var.ua_cfg.cb.on_ip_change_progress(
-						PJSUA_IP_CHANGE_OP_RESTART_LIS, 
-						status, 
+						PJSUA_IP_CHANGE_OP_RESTART_LIS,
+						status,
 						&info);
 	}
@@ -3804,6 +3798,6 @@
 	/* Move forward if all listener has been restarted. */
 	for (; i < PJ_ARRAY_SIZE(pjsua_var.tpdata); ++i) {
-	    if (pjsua_var.tpdata[i].data.ptr != NULL && 
-		pjsua_var.tpdata[i].is_restarting) 
+	    if (pjsua_var.tpdata[i].data.ptr != NULL &&
+		pjsua_var.tpdata[i].is_restarting)
 	    {
 		all_done = PJ_FALSE;
@@ -3832,6 +3826,15 @@
     PJ_ASSERT_RETURN(param, PJ_EINVAL);
 
+    for (; i < (int)PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
+	if (pjsua_var.acc[i].valid &&
+	    pjsua_var.acc[i].ip_change_op != PJSUA_IP_CHANGE_OP_NULL &&
+	    pjsua_var.acc[i].ip_change_op != PJSUA_IP_CHANGE_OP_COMPLETED)
+	{
+	    PJ_LOG(2, (THIS_FILE,
+		     "Previous IP address change handling still in progress"));
+	}
+    }
+
     PJ_LOG(3, (THIS_FILE, "Start handling IP address change"));
-    
     if (param->restart_listener) {
 	PJSUA_LOCK();
@@ -3846,5 +3849,5 @@
 	}
 	for (i = 0; i < PJ_ARRAY_SIZE(pjsua_var.tpdata); ++i) {
-	    if (pjsua_var.tpdata[i].data.ptr != NULL) {		
+	    if (pjsua_var.tpdata[i].data.ptr != NULL) {
 		status = restart_listener(i, param->restart_lis_delay);
 	    }
