Termination Attempt

Introduction

A SIP call-control Lua script using LhoSipIncallLuaService or LhoSipOutcallLuaAgent may request that an attempt be made to terminate to a B-Leg using an outbound SIP INVITE Request.

If any internal Media Server or external SRF is currently connected to the A-Leg as a result of previous interactions, it will be disconnected prior to the B-Leg termination attempt.

If the B-Leg is successfully established, the SIP framework will connect it to the A-Leg using either SIP INVITE Final Response 200 OK or SIP re-INVITE Request.

There are three distinct modes for a Termination:

In the simplest case (no monitoring, no charging) the termination is attempted with four possible outcomes.

  1. The A-Leg abandons the call. The Lua script resumes control but can no longer interact with the call.
  2. The B-Leg is answered. The Lua script resumes control but can no longer interact with the call.
  3. The B-Leg rejects the call or does not answer. The Lua script resumes control of the call.
  4. A SIP processing error occurs. The Lua script resumes control but can no longer interact with the call.

The second outcome is different if Monitoring is requested:

  1. The B-Leg is answered. The Lua script resumes control and must read monitor reports for the call.

The second outcome is also different if Charging is requested:

  1. The B-Leg is answered. The Lua script resumes control and must read charge reports for the call and grant extensions.

If a Monitored or Charged call is answered by the B-Leg, the Lua script will be able to resume control of the call if the B-Leg subsequently hangs up but the A-Leg remains. Otherwise if the A-Leg hangs up, the B-Leg will be disconnected, and the Lua script will resume control but can no longer interact with the call.

If a call that is not Monitored or Charged is answered by the B-Leg, the Lua script will resume control but can no longer interact with the call.

The LhoSipLuaService Termination API

.termination_attempt [Asynchronous]

This method provides full access to the Termination mechanism and is provided for use by custom service developers. In the majority of scenarios, one of the subsequently-described convenience methods should be sufficient and preferable. However, this full-featured alternative is provided for use by complex or non-standard services.

NOTE: If this method returns result.answered == true and result.monitored == truethen your service logic must invokesip_incall_api.monitor_checkuntil the call is no longer monitored or the logic decides to end the call (using the.hangup` method or ending the script).

NOTE: If this method returns result.answered == true and result.charged == true then your service logic must invoke sip_incall_api.charge_check and sip_incall_api.charge_extend alternately until the call is no longer charged or the logic decides to end the call (using the .hangup method or ending the script).

This method takes a single details parameter which is a Lua table with the following structure:

Field Type Description
details Table [Required] The detailed SIP parameters for the message.
.address_digits (+)Hex String [Required] The userinfo part of the INVITE Request URI address.
The protocol and domain parts of the address will be determined automatically.
The applicable configured called_party denormalisation on the LhoSipApp will be applied.
.calling_party (+)Hex String The userinfo part of the INVITE Request From header address.
The protocol and domain parts of the address will be determined automatically.
The applicable configured calling_party denormalisation on the LhoSipApp will be applied.
Set to UNDEF to suppress the network-supplied presented calling party (if any).
This parameter will have no effect if presentation of the calling party is restricted.
(Default = for an A-Leg initiated by an outbound INVITE Request, the A-Leg presented called party; for an A-Leg initiated by an inbound INVITE Request, the A-Leg presented calling party).
.is_calling_restricted Boolean Specify whether presentation of the calling party should be restricted.
This parameter will have no effect if:
  • The A-Leg was initiated by an inbound INVITE Request that contained a Privacy header with value none.
  • The A-Leg was initiated by an outbound INVITE Request with a Response that contained a Privacy header with value none.
Otherwise, when restricted, the B-Leg INVITE Request From and Contact headers will be anonymous, and a Privacy header containing the id flag will be included. Other flags from any Privacy headers in the A-Leg INVITE Request or Response will be included in the B-Leg INVITE Request Privacy header.
When not restricted, the B-Leg INVITE Request From and Contact headers will not be anonymous, and a Privacy header will not be included.
(Default = for an A-Leg initiated by an inbound INVITE Request, the restriction requested by the calling party; for an A-Leg initiated by an outbound INVITE Request, the restriction requested by the called party).
.original_called_party (+)Hex String The userinfo part of the INVITE Request To header address.
The protocol and domain parts of the address will be determined automatically.
The applicable configured called_party denormalisation on the LhoSipApp will be applied.
(Default = use the .address_digits parameter).
.no_answer_timeout Positive Integer Specifies the desired time limit for INVITE Request processing in seconds.
The LhoSipApp will enforce a maximum bound for this value.
A CANCEL Request will be sent if a Final Response is not received within this time.
(Default = use the default_no_answer_secs configured on the LhoSipOutcallLuaAgent or LhoSipIncallLuaService).
.extra_headers Table Additional user headers to include in the SIP INVITE Request.
The table keys are header names.
The table values are tables of header values.
(Default = do not add extra user headers).
.max_call_secs Positive Integer Specifies the desired maximum permitted call duration in seconds.
The LhoSipApp will enforce minimum and maximum bounds for this value.
(Default = use the max_call_secs configured on the LhoSipApp).
.monitored Boolean Set this to true to indicate that this is a "Monitored" call attempt.
Only one of .monitored or .charged may be set to true.
If this call attempt is answered, the service logic must use the .monitor_check method to monitor the call until it completes.
SIP re-INVITE Requests will be sent to both the A-Leg and B-Leg at intervals specified by .monitor_interval_secs to check that the call is still active. Corresponding monitor reports will be sent to the service logic.
(Default = false, the call is not monitored).
.monitor_interval_secs Positive Integer Specifies the desired interval in seconds between the SIP re-INVITE Requests / monitor reports.
The LhoSipApp will enforce minimum and maximum bounds for this value.
Valid only when .monitored is true.
(Default = use the activity_interval_secs configured on the LhoSipApp).
.charged Boolean Set this value to indicate that this is a "Charged" call attempt.
Only one of .monitored or .charged may be set to true.
If this call attempt is answered, the service logic must use the .charge_check and .charge_extend methods to authorise extensions on the charged call until it completes.
SIP re-INVITE Requests will be sent to both the A-Leg and B-Leg at the end of grant periods to check that the call is still active. Corresponding charge reports will be sent to the service logic.
(Default = false, the call is not charged).
.grant_secs Positive Integer [Required if .charged is true] Specifies the desired period in seconds from when the call attempt is answered until the initial SIP re-INVITE Requests / charge report.
The LhoSipApp will enforce minimum and maximum bounds for this value.
Valid only when .charged is true.

The termination_attempt method returns a Lua table with the following attributes.

Attribute Type Description
.controlled Boolean [Required] Is this call still controlled right now?
This value being true indicates that (at least some) telephony API actions can be performed by service logic.
Note that in some cases the available control actions may be limited.
.answered Boolean [Required] Was this call answered?
This value being true indicates that the A-Leg to B-Leg setup was successfully completed.
.monitored Boolean [Required] Is this call monitored?
This value will be true when "Monitored" termination was requested, and .answered is true.
Only one (or neither) of .monitored or .charged will be true.
.charged Boolean [Required] Is this call charged?
This value will be true when "Charged" termination was requested, and .answered is true.
Only one (or neither) of .monitored or .charged will be true.
.proceed_ok Boolean [Required] Can the service logic still use the .proceeding method?
This value will be true when .controlled is true, we have not yet sent a Final Response, and an SDP Offer would not be required to proceed.
.decline_ok Boolean [Required] Can the service logic still use the .decline method?
This value will be true when .controlled is true and we have not yet sent a Final Response.
.reason No Route / Declined / No Answer / Answered / Abandoned [Required] The reason why the termination attempt ended.
.code Integer A SIP Final Response code (in the range 200-699) associated with the B-Leg termination attempt.
This value is present only when the B-Leg termination attempt received a SIP INVITE Final Response.
(Default = not present, a Final Response was not received).
.ring_dsm Integer The ring-time duration in deci-seconds, as measured by the LhoSipApp.
This value is present only when .answered is true.
(Default = not present, .answered is false).
.max_call_secs Positive Integer The actual maximum permitted call duration in seconds.
This may differ from the requested value because the LhoSipApp applies a default, and minimum and maximum bounds.
This value is present only when .answered is true.
(Default = not present, .answered is false).
.monitor_interval_secs Positive Integer The actual monitor interval in seconds.
This may differ from the requested value because the LhoSipApp applies a default, and minimum and maximum bounds.
This value is present only when .monitored and .answered are true.
(Default = not present, .monitored or .answered are false).
.grant_secs Positive Integer The actual first grant duration in seconds.
This may differ from the requested value because the LhoSipApp applies minimum and maximum bounds.
This value is present only when .charged and .answered are true.
(Default = not present, .charged or .answered are false).

If the A-Leg abandons before the B-Leg answers, the result will be:

Otherwise if the B-Leg is not answered, the result will always include:

The result may include code = <300-699> if a Final Response was received from the B-Leg.

If the B-Leg is answered, the result will always include:

If the B-Leg is answered for a call attempt that is not monitored or charged, the result will also include:

If the B-Leg is answered for a monitored call attempt, the result will also include:

If the B-Leg is answered for a charged call attempt, the result will also include:

Example (connection attempt to called party with a prefix, 60 second no-answer timeout, custom header):

local n2svcd = require "n2.n2svcd"
local incall_api = require "n2.n2svcd.sip_incall_service"

local sip_incall = ...

local result = incall_api.termination_attempt ({
    address_digits = '1703' .. sip_incall.normalised_called_party,
    no_answer_timeout = 60,
    extra_headers = {
      'ChargeRef' = { 'XX-001234' }   -- Header values must be contained in a list.
    }
})

if (result.answered) then
    n2svcd.notice ("CALL ANSWERED = YES")

else
    if (result.controlled) then
        n2svcd.notice ('Controlled after Reason = %s', result.reason)
        if (result.decline_ok) then
            incall_api.decline (633)

        else
            incall_api.hangup ()
        end

    else
        n2svcd.notice ('Control Lost after Reason = %s', result.reason)
    end
end

return

Example (monitored connection attempt to called party with a prefix, 10 minute maximum talk-time, 60 second monitor interval):

local n2svcd = require "n2.n2svcd"
local incall_api = require "n2.n2svcd.sip_incall_service"

local sip_incall = ...

local result = incall_api.termination_attempt ({
    monitored = true,
    address_digits = '1703' .. sip_incall.normalised_called_party,
    max_call_secs = 600,
    monitor_interval_secs = 60
})

-- When a monitored call is answered, we must sweep for monitor events.
if (result.answered) then
    repeat
        result = sip_incall_api.monitor_check ()
    until (not result.monitored)
end

return

Example (charged connection attempt to called party with a prefix, 10 minute maximum talk-time, 60 + 30 + 30 + end):

local n2svcd = require "n2.n2svcd"
local incall_api = require "n2.n2svcd.sip_incall_service"

local sip_incall = ...

local result = incall_api.termination_attempt ({
    charged = true,
    address_digits = '1703' .. sip_incall.normalised_called_party,
    max_call_secs = 600,
    grant_secs = 60
})

-- When a charged call is answered, we must sweep for charge events and extend.
-- We grant 30 + 30 then no more
--
if (result.answered) then

    local ngrants = 0
    while (true) do
        result = sip_incall_api.charge_check ()
        if (not result.charged) then break end

        ngrants = ngrants + 1
        if (ngrants <= 2) then
            sip_incall.charge_extend (30)

        else
            sip_incall.hangup ()
            break
        end
    end
end

return

.connect_attempt [Asynchronous]

This helper method provides a flattened-argument version of the termination_attempt method.

The connect_attempt method takes the following arguments:

Field Type Description
called_party (+)Hex String [Required] The userinfo part of the INVITE Request To header address.
The protocol and domain parts of the address will be determined automatically.
The applicable configured called_party denormalisation on the LhoSipApp will be applied.
no_answer_timeout Positive Integer Specifies the desired time limit for INVITE Request processing in seconds.
The LhoSipApp will enforce a maximum bound for this value.
A CANCEL Request will be sent if a Final Response is not received within this time.
(Default = use the default_no_answer_secs configured on the LhoSipOutcallLuaAgent or LhoSipIncallLuaService).
max_call_secs Positive Integer Specifies the desired maximum permitted call duration in seconds.
The LhoSipApp will enforce minimum and maximum bounds for this value.
(Default = use the max_call_secs configured on the LhoSipApp).

The connect_attempt method returns the same table structure as termination_attempt.

Example (connect to called party with a prefix, 60 second no-answer timeout):

local n2svcd = require "n2.n2svcd"
local incall_api = require "n2.n2svcd.sip_incall_service"

local sip_incall = ...

local result = incall_api.connect_attempt ('1703' .. sip_incall.normalised_called_party, 60)

if (result.answered) then
    n2svcd.notice ("CALL ANSWERED = YES")
end

return

.connect_monitored [Asynchronous]

This helper method provides a flattened-argument version of the termination_attempt method for the “Monitored” call mode.

NOTE: If this method returns result.answered == true then your service logic must invoke sip_incall_api.monitor_check until the call is no longer monitored or the logic decides to end the call (using the .hangup method or ending the script).

The connect_monitored method takes the following arguments:

Field Type Description
called_party (+)Hex String [Required] The userinfo part of the INVITE Request To header address.
The protocol and domain parts of the address will be determined automatically.
The applicable configured called_party denormalisation on the LhoSipApp will be applied.
no_answer_timeout Positive Integer Specifies the desired time limit for INVITE Request processing in seconds.
The LhoSipApp will enforce a maximum bound for this value.
A CANCEL Request will be sent if a Final Response is not received within this time.
(Default = use the default_no_answer_secs configured on the LhoSipOutcallLuaAgent or LhoSipIncallLuaService).
monitor_interval_secs Positive Integer Specifies the desired interval in seconds between the SIP re-INVITE Requests / monitor reports.
The LhoSipApp will enforce minimum and maximum bounds for this value.
(Default = use the activity_interval_secs configured on the LhoSipApp).
max_call_secs Positive Integer Specifies the desired maximum permitted call duration in seconds.
The LhoSipApp will enforce minimum and maximum bounds for this value.
(Default = use the max_call_secs configured on the LhoSipApp).

The connect_monitored method returns the same table structure as termination_attempt.

Example (connect to specific destination, 10 second no-answer timeout, 3 second monitor interval, 20 second max call duration):

local n2svcd = require "n2.n2svcd"
local incall_api = require "n2.n2svcd.sip_incall_service"

local sip_incall = ...

local result = sip_incall_api.connect_monitored ("902112233", 10, 3, 20)

if (result.answered) then
    n2svcd.notice ("CALL ANSWERED = YES")

    print ("Actual monitor_interval_secs = " .. tostring (result.monitor_interval_secs))
    print ("Actual max_call_secs = " .. tostring (result.max_call_secs))

    -- When a monitored call is answered, we must sweep for monitor events.
    repeat
        result = sip_incall_api.monitor_check ()
    until (not result.monitored)

elseif (result.decline_ok) then
    sip_incall_api.decline (400)
end

return

.connect_charged [Asynchronous]

This helper method provides a flattened-argument version of the termination_attempt method for the “Charged” call mode.

NOTE: If this method returns result.answered == true then your service logic must invoke sip_incall_api.charge_check and sip_incall.charge_extend alternately until the call is no longer charged or the logic decides to end the call (using the .hangup method or ending the script).

The connect_charged method takes the following arguments:

Field Type Description
called_party (+)Hex String [Required] The userinfo part of the INVITE Request To header address.
The protocol and domain parts of the address will be determined automatically.
The applicable configured called_party denormalisation on the LhoSipApp will be applied.
no_answer_timeout Positive Integer Specifies the desired time limit for INVITE Request processing in seconds.
The LhoSipApp will enforce a maximum bound for this value.
A CANCEL Request will be sent if a Final Response is not received within this time.
(Default = use the default_no_answer_secs configured on the LhoSipOutcallLuaAgent or LhoSipIncallLuaService).
grant_secs Positive Integer [Required] Specifies the desired period in seconds from when the call attempt is answered until the initial SIP re-INVITE Requests / charge report.
The LhoSipApp will enforce minimum and maximum bounds for this value.
max_call_secs Positive Integer Specifies the desired maximum permitted call duration in seconds.
The LhoSipApp will enforce minimum and maximum bounds for this value.
(Default = use the max_call_secs configured on the LhoSipApp).

The connect_charged method returns the same table structure as termination_attempt.

Example (connect to specific destination, grant 30 seconds indefinitely):

local n2svcd = require "n2.n2svcd"
local incall_api = require "n2.n2svcd.sip_incall_service"

local sip_incall = ...

local result = sip_incall_api.connect_charged ("902112233", nil, 30)
if (result.answered) then
    while (true) do
        result = sip_incall_api.charge_check ()
        if (not result.charged) then break end
        sip_incall.charge_extend (30)
    end
end

return

.monitor_check [Asynchronous]

This monitor check method must be invoked by any script using monitored calls, after a monitored call attempt has been answered.

The logic must repeat calls to result = sip_incall_api.monitor_check until result.monitored is false.

The script may perform other protocol actions prior to each invocation of monitor_check. These may include asynchronous actions, e.g. Diameter client or REST client actions via the corresponding agent.

See also the subsequent sections related to follow-on telephony actions, and to service-initiated hangup during monitored calls for further discussion of how the monitor_check method interacts with other telephony methods.

The monitor_check method takes no arguments.

The monitor_check method returns a Lua table with the following attributes.

Attribute Type Description
.controlled Boolean [Required] Is this call still controlled right now?
This value being true indicates that (at least some) telephony API actions can be performed by service logic.
Note that in some cases the available control actions may be limited.
.monitored Boolean [Required] Is this call still monitored.
This value being true indicates that the A-Leg to B-Leg talk-time is still in progress, and the total talk-time has reached the sum of all monitoring report intervals to-date.
.monitored_secs Integer The accumulated talk-time in seconds confirmed by this and all previous reports, as measured by the LhoSipApp.
This value is present only when .monitored is true.
(Default = not present, .monitored is false).
.talk_dsm Integer The talk-time duration in deci-seconds, as measured by the LhoSipApp.
This value is present only when .monitored is false.
(Default = not present, .monitored is true).

If the call successfully completes the entire monitoring interval with both legs still connected, the result will be:

If the B-Leg is terminated but the A-Leg is still available, follow-on telephony is possible, and the result will be:

If the A-Leg is terminated then the B-Leg will also be terminated and the result will be:

.charge_check [Asynchronous]

This charge check method must be invoked by any script using charged calls, after the charged call attempt has initially been answered, and then subsequently after each call to charge_extend.

The script may perform other protocol actions prior to each invocation of charge_check. These may include asynchronous actions, e.g. Diameter client or REST client actions via the corresponding agent.

See also the subsequent sections related to follow-on telephony actions, and to service-initiated hangup during charged calls for further discussion of how the charge_check method interacts with other telephony methods.

The charge_check method takes no arguments.

The charge_check method returns a Lua table with the following attributes.

Attribute Type Description
.controlled Boolean [Required] Is this call still controlled right now?
This value being true indicates that (at least some) telephony API actions can be performed by service logic.
Note that in some cases the available control actions may be limited.
.charged Boolean [Required] Is this call still charged.
This value being true indicates that the A-Leg to B-Leg talk-time is still in progress, and the total talk-time has reached the sum of all granted intervals to-date.
.charged_secs Integer The accumulated talk-time in seconds confirmed by this and all previous reports, as measured by the LhoSipApp.
This value is present only when .charged is true.
(Default = not present, .charged is false).
.talk_dsm Integer The talk-time duration in deci-seconds, as measured by the LhoSipApp.
This value is present only when .charged is false.
(Default = not present, .charged is true).

If the call successfully completes the entire granted time with both legs still connected, the result will be:

If the B-Leg is terminated but the A-Leg is still available, follow-on telephony is possible, and the result will be:

If the A-Leg is terminated then the B-Leg will also be terminated and the result will be:

.charge_extend [Synchronous]

This charge extend method must be invoked by any script using charged calls, whenever the charge_check method returns result.charged = true and the service decides to grant a subsequent talk-time extension.

If the service does not wish to grant a talk-time extension, then it should invoke the hangup method or end the script. This must be done before the service logic timer expires.

The script may perform other protocol actions prior to each invocation of charge_extend. These may include asynchronous actions, e.g. Diameter client or REST client actions via the corresponding agent.

See also the subsequent sections related to follow-on telephony actions, and to service-initiated hangup during charged calls for further discussion of how the charge_extend method interacts with other telephony methods.

The charge_extend method takes the following arguments:

[Required] Specifies the desired period in seconds until the next SIP re-INVITE Requests / charge report.
The LhoSipApp will enforce minimum and maximum bounds for this value.
Field Type Description

The charge_extend method returns true.

Limited Telephony During Monitoring/Charging

The termination_attempt, monitor_check, and charge_check methods can return monitored = true or charged = true when controlled = true.

This indicates that a monitored or charged call is in a progress, and a limited telephony control is available. Specifically, the following methods are the only methods that should be invoked:

After using the hangup method the call is over and there is no additional information which can be provided. No methods should be invoked, including those listed above.

Follow-On Telephony After Monitoring/Charging

The monitor_check and charge_check methods can return monitored = false and charged = false when controlled = true.

This occurs when the B-Leg of the monitored/charged call ends, but the A-Leg is still available.

At this time, almost all of the standard telephony methods are available, including those associated with interaction and B-Leg termination.

Exceptions are:

Slow Service Logic

All service logic must respond promptly, within the service_logic_ms timer (default = 2500 milliseconds) configured for the LhoSipApp.

If the service logic response is not received within this time whenever it is expected, the LhoSipApp will assume that call control has been abandoned, and will terminate the in-progress call with the loss of all legs.

Constants

The following relevant constants are defined on the sip_incall_service and sip_outcall_agent objects:

sip_incall_service.REASON_NO_ROUTE = "No Route"        -- B-Party RSF (we have no route)
sip_incall_service.REASON_DECLINED = "Declined"        -- B-Party Busy (and other explicit decline)
sip_incall_service.REASON_NO_ANSWER = "No Answer"      -- B-Party Time-Out
sip_incall_service.REASON_ANSWERED = "Answered"        -- B-Party Answer
sip_incall_service.REASON_ABANDONED = "Abandoned"      -- A-Party Hangup

sip_outcall_agent.REASON_NO_ROUTE = "No Route"        -- A/B-Party RSF (we have no route)
sip_outcall_agent.REASON_DECLINED = "Declined"        -- A/B-Party Busy (and other explicit decline)
sip_outcall_agent.REASON_NO_ANSWER = "No Answer"      -- A/B-Party Time-Out
sip_outcall_agent.REASON_ANSWERED = "Answered"        -- A/B-Party Answer
sip_outcall_agent.REASON_ABANDONED = "Abandoned"      -- A-Party Hangup before Answer