asterisk: test Precondition extension in IMS MT call

Related: SYS#6969
Change-Id: Ic96a6f99edbcf299fde36a2146a3ce252e09536a
diff --git a/asterisk/Asterisk_Tests.ttcn b/asterisk/Asterisk_Tests.ttcn
index 981d5f1..596848b 100644
--- a/asterisk/Asterisk_Tests.ttcn
+++ b/asterisk/Asterisk_Tests.ttcn
@@ -49,7 +49,7 @@
 	charstring mp_ami_user := "test_user";
 	charstring mp_ami_secret := "1234";
 	charstring mp_volte_ims_outbound_registration := "volte_ims";
-	/* Current default by pjproject (timeout_timer_val) is set to 32s, and not changed by Asterisk */
+	/* Current default by pjproject (timeout_timer_val) is 	set to 32s, and not changed by Asterisk */
 	integer mp_volte_ims_outbound_register_timeout := 32;
 }
 
@@ -633,7 +633,7 @@
 }
 
 /* Test SIP registration of local clients */
-private function f_TC_ims_call_mt(charstring id) runs on IMS_ConnHdlr {
+private function f_TC_ims_call_mt_IMS_ConnHdlr(charstring id) runs on IMS_ConnHdlr {
 	f_create_sip_expect(valueof(ts_SipUrl_from_Addr_Union(g_pars.subscr.registrar_sip_record.addr)));
 	as_IMS_register();
 	COORD.send(IMS_COORD_CMD_REGISTERED);
@@ -650,7 +650,7 @@
 
 	as_IMS_unregister();
 }
-testcase TC_ims_call_mt() runs on test_CT {
+private function f_TC_ims_call_mt(boolean use_precondition_ext) runs on test_CT {
 	var SIPConnHdlrPars sip_pars;
 	var IMS_ConnHdlrPars ims_pars;
 	var SIPConnHdlr vc_conn_sip;
@@ -670,8 +670,10 @@
 							ts_UserInfo(c_ext_msisdn)));
 	ims_pars.subscr.cp.called := valueof(ts_SipAddr(ts_HostPort(ims_pars.realm),
 							 ts_UserInfo(ims_pars.subscr.msisdn)));
+	ims_pars.subscr.cp.support_precondition_ext := use_precondition_ext;
+	ims_pars.subscr.cp.require_precondition_ext := use_precondition_ext;
 
-	vc_conn_ims := f_start_handler_IMS(refers(f_TC_ims_call_mt), ims_pars);
+	vc_conn_ims := f_start_handler_IMS(refers(f_TC_ims_call_mt_IMS_ConnHdlr), ims_pars);
 	vc_conn_sip := f_start_handler(refers(f_TC_internal_call_mt), sip_pars);
 
 	COORD.receive(COORD_CMD_REGISTERED) from vc_conn_sip;
@@ -699,6 +701,12 @@
 	vc_conn_ims.done;
 	f_shutdown();
 }
+testcase TC_ims_call_mt() runs on test_CT {
+	f_TC_ims_call_mt(true);
+}
+testcase TC_ims_call_mt_noprecondition() runs on test_CT {
+	f_TC_ims_call_mt(false);
+}
 
 control {
 	execute( TC_internal_registration() );
@@ -716,6 +724,7 @@
 	execute( TC_ims_call_mo() );
 	execute( TC_ims_call_mo_noprecondition() );
 	execute( TC_ims_call_mt() );
+	execute( TC_ims_call_mt_noprecondition() );
 }
 
 }
diff --git a/asterisk/IMS_ConnectionHandler.ttcn b/asterisk/IMS_ConnectionHandler.ttcn
index 860b799..dd3223c 100644
--- a/asterisk/IMS_ConnectionHandler.ttcn
+++ b/asterisk/IMS_ConnectionHandler.ttcn
@@ -954,6 +954,7 @@
 	[fail_others] as_SIP_fail_req(sip_expect_str);
 }
 
+/* IMS Core starts a call towards the peer: 3GPP TS 24.229 5.1.3.1 */
 function f_IMS_mt_call_setup() runs on IMS_ConnHdlr
 {
 	var template (value) PDU_SIP_Request req;
@@ -961,6 +962,8 @@
 	var template (present) From from_addr_exp;
 	var template (present) To to_addr_exp;
 	var Via via;
+	var template (omit) Require require := omit;
+	var template (omit) Supported supported := omit;
 	var charstring tx_sdp := f_gen_sdp();
 	var default d_trying, d_ringing;
 	var charstring branch_value;
@@ -983,12 +986,19 @@
 					ts_ContactAddress(g_pars.subscr.cp.calling.addr, omit)
 				}));
 
+	if (g_pars.subscr.cp.support_precondition_ext) {
+		/* indicate the support for "reliable provisional response"
+		 * and "preconditions mechanism" */
+		supported := ts_Supported({ "100rel", "precondition" });
+	}
+
 	req := ts_SIP_INVITE(g_pars.subscr.cp.sip_call_id,
 			     g_pars.subscr.cp.from_addr,
 			     g_pars.subscr.cp.to_addr,
 			     via,
 			     calling_contact,
 			     g_pars.subscr.cp.sip_seq_nr,
+			     supported := supported,
 			     body := tx_sdp);
 
 	SIP.send(req);
@@ -1001,6 +1011,75 @@
 			g_pars.subscr.cp.sip_seq_nr, "INVITE");
 	d_trying := activate(as_SIP_ignore_resp(exp));
 
+	if (g_pars.subscr.cp.require_precondition_ext) {
+		/* Rx 183 Session Progress */
+		exp := tr_SIP_Response_SessionProgress(
+				g_pars.subscr.cp.sip_call_id,
+				from_addr_exp,
+				to_addr_exp,
+				f_tr_Via_response(via),
+				g_pars.subscr.cp.sip_seq_nr, "INVITE",
+				require := tr_Require(superset("100rel", "precondition")),
+				rseq := tr_RSeq(?));
+		alt {
+		[] SIP.receive(exp) -> value g_rx_sip_resp;
+		[] SIP.receive(tr_SIP_Response_Ringing(?, ?, ?)) -> value g_rx_sip_resp {
+			Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
+						log2str(g_name & ": Expected 183 Session Progress instead of 180 Ringing: ",
+							g_rx_sip_resp));
+			}
+		}
+
+		/* Tx PRACK */
+		req := ts_SIP_PRACK(g_pars.subscr.cp.to_addr.addressField.nameAddr.addrSpec,
+				    g_pars.subscr.cp.sip_call_id,
+				    g_pars.subscr.cp.from_addr,
+				    g_pars.subscr.cp.to_addr,
+				    via,
+				    g_rx_sip_resp.msgHeader.cSeq.seqNumber + 1,
+				    rack := ts_RAck(g_rx_sip_resp.msgHeader.rseq.response_num,
+						    g_rx_sip_resp.msgHeader.cSeq.seqNumber,
+						    "INVITE"),
+				    body := omit);
+		SIP.send(req);
+
+		/* Rx 200 OK (PRACK) */
+		exp := tr_SIP_Response(g_pars.subscr.cp.sip_call_id,
+				       from_addr_exp,
+				       to_addr_exp,
+				       f_tr_Via_response(via),
+				       omit,
+				       "PRACK", 200,
+				       g_rx_sip_resp.msgHeader.cSeq.seqNumber + 1, "OK",
+				       body := omit);
+		as_SIP_expect_resp(exp, fail_others := false);
+
+		/* Tx UPDATE */
+		req := ts_SIP_UPDATE(g_pars.subscr.cp.to_addr.addressField.nameAddr.addrSpec,
+				     g_pars.subscr.cp.sip_call_id,
+				     g_pars.subscr.cp.from_addr,
+				     g_pars.subscr.cp.to_addr,
+				     via,
+				     g_rx_sip_resp.msgHeader.cSeq.seqNumber + 1,
+				     contact := calling_contact,
+				     require := ts_Require({"sec-agree", "precondition"}),
+				     body := tx_sdp);
+		SIP.send(req);
+
+		/* Rx 200 OK (UPDATE) */
+		exp := tr_SIP_Response(g_pars.subscr.cp.sip_call_id,
+				       from_addr_exp,
+				       to_addr_exp,
+				       f_tr_Via_response(via),
+				       omit,
+				       "UPDATE", 200,
+				       g_rx_sip_resp.msgHeader.cSeq.seqNumber + 1, "OK",
+				       body := ?);
+		as_SIP_expect_resp(exp, fail_others := false);
+
+		g_pars.subscr.cp.sip_seq_nr := g_rx_sip_resp.msgHeader.cSeq.seqNumber + 1;
+	}
+
 	/* Conditionally match and accept 180 Ringing */
 	exp := tr_SIP_Response_Ringing(g_pars.subscr.cp.sip_call_id,
 			from_addr_exp,
diff --git a/asterisk/expected-results.xml b/asterisk/expected-results.xml
index e9d2b82..31a03e5 100644
--- a/asterisk/expected-results.xml
+++ b/asterisk/expected-results.xml
@@ -15,4 +15,5 @@
   <testcase classname='Asterisk_Tests' name='TC_ims_call_mo' time='MASKED'/>
   <testcase classname='Asterisk_Tests' name='TC_ims_call_mo_noprecondition' time='MASKED'/>
   <testcase classname='Asterisk_Tests' name='TC_ims_call_mt' time='MASKED'/>
+  <testcase classname='Asterisk_Tests' name='TC_ims_call_mt_noprecondition' time='MASKED'/>
 </testsuite>
diff --git a/library/SIP_Templates.ttcn b/library/SIP_Templates.ttcn
index cfe21eb..d0210d1 100644
--- a/library/SIP_Templates.ttcn
+++ b/library/SIP_Templates.ttcn
@@ -736,6 +736,7 @@
 		template P_Associated_Uri p_associated_uri := *,
 		template RAck rack := *,
 		template Require require := *,
+		template RSeq rseq := *,
 		template Security_client security_client := *,
 		template Security_server security_server := *,
 		template Server server := *,
@@ -762,6 +763,7 @@
 	p_associated_uri := p_associated_uri,
 	rack := rack,
 	require := require,
+	rseq := rseq,
 	security_client := security_client,
 	security_server := security_server,
 	server := server,
@@ -833,13 +835,15 @@
 	      template (value) Via via,
 	      template (value) Contact contact,
 	      integer seq_nr,
+	      template (omit) Supported supported := omit,
 	      template (omit) charstring body := omit) := {
 	requestLine := ts_SIP_ReqLine(INVITE_E, to_addr.addressField.nameAddr.addrSpec),
 	msgHeader := ts_SIP_msgh_std(call_id, from_addr, to_addr, contact,
 				     "INVITE", seq_nr,
 				     via,
 				     content_length := f_ContentLength(body),
-				     content_type := f_ContentTypeOrOmit(ts_CT_SDP, body)),
+				     content_type := f_ContentTypeOrOmit(ts_CT_SDP, body),
+				     supported := supported),
 	messageBody := body,
 	payload := omit
 }
@@ -939,6 +943,25 @@
 	payload := omit
 }
 
+template (value) PDU_SIP_Request
+ts_SIP_PRACK(template (value) SipUrl uri,
+	     template (value) CallidString call_id,
+	     template (value) From from_addr,
+	     template (value) To to_addr,
+	     template (value) Via via,
+	     integer seq_nr,
+	     template (value) RAck rack,
+	     template (omit) charstring body := omit) := {
+	requestLine := ts_SIP_ReqLine(PRACK_E, uri),
+	msgHeader := ts_SIP_msgh_std(call_id, from_addr, to_addr, omit,
+				     "PRACK", seq_nr,
+				     via,
+				     content_length := f_ContentLength(body),
+				     content_type := f_ContentTypeOrOmit(ts_CT_SDP, body),
+				     rack := rack),
+	messageBody := body,
+	payload := omit
+}
 template (present) PDU_SIP_Request
 tr_SIP_PRACK(template (present) SipUrl uri,
 	     template CallidString call_id,
@@ -957,6 +980,24 @@
 	payload := omit
 }
 
+template (value) PDU_SIP_Request
+ts_SIP_UPDATE(template (value) SipUrl uri,
+	      template (value) CallidString call_id,
+	      template (value) From from_addr,
+	      template (value) To to_addr,
+	      template (value) Via via,
+	      integer seq_nr,
+	      template (omit) Contact contact,
+	      template (value) Require require := ts_Require({"sec-agree", "precondition"}),
+	      template (value) charstring body) := {
+	requestLine := ts_SIP_ReqLine(UPDATE_E, uri),
+	msgHeader := ts_SIP_msgh_std(call_id, from_addr, to_addr, contact,
+				     "UPDATE", seq_nr,
+				     via,
+				     require := require),
+	messageBody := body,
+	payload := omit
+}
 template (present) PDU_SIP_Request
 tr_SIP_UPDATE(template (present) SipUrl uri,
 	      template CallidString call_id,
@@ -1187,6 +1228,28 @@
 	payload := omit
 }
 
+/* 183 Session Progress */
+template (present) PDU_SIP_Response
+tr_SIP_Response_SessionProgress(
+	template (present) CallidString call_id,
+	template (present) From from_addr,
+	template (present) To to_addr,
+	template (present) Via via,
+	template (present) integer seq_nr := ?,
+	template (present) charstring method := "INVITE",
+	template Require require := *,
+	template RSeq rseq := *,
+	template (omit) charstring body := omit) := {
+	statusLine := tr_SIP_StatusLine(183, "Session Progress"),
+	msgHeader := tr_SIP_msgh_std(call_id, from_addr, to_addr, *,
+				     via,
+				     method, seq_nr,
+				     require := require,
+				     rseq := rseq),
+	messageBody := body,
+	payload := omit
+}
+
 /****************
  * FUNCTIONS:
  ****************/
diff --git a/sip/SIP_Tests.ttcn b/sip/SIP_Tests.ttcn
index 9c376f3..10f8b4d 100644
--- a/sip/SIP_Tests.ttcn
+++ b/sip/SIP_Tests.ttcn
@@ -273,7 +273,8 @@
 	SIP.send(ts_SIP_INVITE(cp.comp.sip_call_id, from_addr, to_addr,
 			       via,
 			       ts_Contact_SipAddr(cp.comp.sip_url_ext),
-			       cp.comp.sip_seq_nr, cp.comp.sip_body));
+			       cp.comp.sip_seq_nr,
+			       body := cp.comp.sip_body));
 	if (cp.mncc_with_sdp) {
 		/* We just sent SDP via SIP, now expect the same SDP in MNCC to the MSC */
 		expect_sdp_to_msc := cp.comp.sip_body;