What is HangUp Cause of 32?

Hi,

I’m using AsterNET.ARI to interact with Asterisk v.16 and trying to call a real number. So far so good and I now need to explore the Hangup Cause when the destination declines the call so I’ve got this result as below:
{"Cause":32,"Soft":true,"Channel":{"Id":"1550726476.44","Name":"SIP/symbio-00000018","State":"Up","Caller":{"Name":"","Number":""},"Connected":{"Name":"","Number":""},"Accountcode":"","Dialplan":{"Context":"csntelephony","Exten":"","Priority":1},"Creationtime":"2019-02-21T16:21:16.737+11:00","Language":"en"},"Application":"app","Timestamp":"2019-02-21T16:21:54.455+11:00","Type":"ChannelHangupRequest"}
I don’t know what the Cause of 32 is according to this link https://wiki.asterisk.org/wiki/display/AST/Hangup+Cause+Mappings. Can anyone have a look and let me know if I’m missing anything. Thanks

That’s a “soft” hangup cause code, which differs from the cause codes on the wiki.

If I am reading the code correctly then 32 represents the initiation of an explicit soft hangup from a non-associated party. Usually this means it was “told” to specifically hang up the call. For instance, someone, or some application is hanging up the call via the CLI, AGI, ARI, the soft hangup application, certain transfer scenarios, etc…

1 Like

So does that mean I can use this option to determine the far end hanged up the call?

No. As I understood the reply, it is saying that this is only used when a third party, not the the caller or callee, hangs up the call. That will not happen through SIP.

Hmm, so is there any way to know when the far end hangs up the call? Been searching for days but could not find the answer.

Use the g option on Dial. If the far end hangs up, the dialplan, will continue. If the near end hangs up, it will go to the h exension.

Thanks @david551, however our dial plan does not have that logic as we’re using Stasis, see below:
[ari-test]

exten => _[+0-9a-zA-Z]X.,1(start), Verbose(1, Inbound call: From ${CALLERID(num)} to ${EXTEN}|${SIP_HEADER(To)})
same => n,Wait(1)
same => n,Set(CALLERID(name)=xyz)
same => n,Stasis(app,${CALLERID(num)}, ${EXTEN},${SIP_HEADER(To)})
same => n,Hangup()

How do I use those options with the dial plan as above? Are those options accessible from ARI? Would be great if you can provide some code?

I have never used ARI.

As a general principle I do not provide code. Getting good code, that someone else can use without having to come back for further support takes a lot longer than the amount of voluntary time one can reasonably expect on a peer support forum.

I will generally explain what is wrong on hint at solutions to investigate. If I provide code is generally an example of a very specific point and not a complete solution.

Incidentally, I suspect a lot of people use ARI and, before that, AGI, when the requirement only requires normal dialplan. However, if you do use scripting, you need to provide the script, not just the dailplan that invokes it.

@david551, I just double checked the Dial command here https://www.voip-info.org/asterisk-cmd-dial/#Options, it appears that to hang up the call, either caller or callee has to dial * which basically not the one I’m after. Also according to this https://support.digium.com/s/article/ISDN-Disconnect-cause-codes there is no code 32 still even from non-associated party. Am I missing anything?

With Dial, hanging up a call is normally done by the normal mechanism for the channel technology (e.g. sending BYE for SIP). “*” is an additional option.

Voip.info is not effectively maintained and some of the information is very out of date.

Non-assocaited parties cannot use ISDN to signal anything. They have to use other mechanisms, like AMI. As such ISDN does not need a code for that purpose. I assume the developers of Asterisk have internally used an unallocated code for that purpose, and the document probably needs updating to indicate that they have done this.

ARI doesn’t hang up a channel unless it is told to, or the remote side does. If you haven’t told it… then the remote side has seemingly hung up.

For any further information you’d need to provide more information about your scenario, what you’re doing, and what events are occurring.

Hi @jcolp,
Here is what I’m trying to do:
I’m creating a telephony app which is using AsterNET.ARI to interact with Asterisk v.16. Basically my app will receive incoming calls, pass it to Asterisk dial plan which I described above (Stasis), then our ARI app will dial out multiple numbers depends on our logic (one to many). Here is the step:

  1. Accept incoming channels
  2. Create new bridge then add the incoming channel to the bridge
  3. Create outgoing channels from the ARI app
  4. Add outgoing channels to the bridge then dial out those channels at the same time.

If one of those outgoing channels answers the call, I will receive the DIALSTATUS=ANSWER in the OnDialEvent and I use this value to hangup the other channels. This scenario is working perfectly. Now the problem is if one of those outgoing channels declines the call, others will be hangup as well because the DIALSTATUS I receive in the OnDialEvent is always ANSWER. Here is the JSON I got when one of the outgoing channels declined the call:

{"Caller":null,"Peer":{"Id":"1550921467.57","Name":"SIP/symbio-00000021","State":"Up","Caller":{"Name":"","Number":""},"Connected":{"Name":"carsales","Number":"61434466477"},"Accountcode":"","Dialplan":{"Context":"csntelephony","Exten":"","Priority":1},"Creationtime":"2019-02-23T22:31:07.237+11:00","Language":"en"},"Forward":"","Forwarded":null,"Dialstring":"","Dialstatus":"ANSWER","Application":"app","Timestamp":"2019-02-23T22:31:11.551+11:00","Type":"Dial"}

I’ve also checked the OnChannelHangupEvent but could not find anything that could help determine how the far end rejects the call. See below JSONs:

{"Cause":0,"Soft":false,"Channel":{"Id":"1550921458.56","Name":"SIP/125.213.160.7-00000020","State":"Up","Caller":{"Name":"carsales","Number":"61434466477"},"Connected":{"Name":"","Number":""},"Accountcode":"","Dialplan":{"Context":"ari-test","Exten":"61370182002","Priority":4},"Creationtime":"2019-02-23T22:30:58.111+11:00","Language":"en"},"Application":"app","Timestamp":"2019-02-23T22:31:37.754+11:00","Type":"ChannelHangupRequest"}
{"Cause":16,"Soft":true,"Channel":{"Id":"1550921458.56","Name":"SIP/125.213.160.7-00000020","State":"Up","Caller":{"Name":"carsales","Number":"61434466477"},"Connected":{"Name":"","Number":""},"Accountcode":"","Dialplan":{"Context":"ari-test","Exten":"61370182002","Priority":4},"Creationtime":"2019-02-23T22:30:58.111+11:00","Language":"en"},"Application":"app","Timestamp":"2019-02-23T22:31:37.755+11:00","Type":"ChannelHangupRequest"}
{"Cause":32,"Soft":true,"Channel":{"Id":"1550921467.57","Name":"SIP/symbio-00000021","State":"Up","Caller":{"Name":"","Number":""},"Connected":{"Name":"","Number":""},"Accountcode":"","Dialplan":{"Context":"csntelephony","Exten":"","Priority":1},"Creationtime":"2019-02-23T22:31:07.237+11:00","Language":"en"},"Application":"app","Timestamp":"2019-02-23T22:31:37.906+11:00","Type":"ChannelHangupRequest"}

ChannelId 1550921458.56 is incoming channel, and channelId 1550921467.57 is outgoing channel that declined the call.

Here is from the Asterisk CLI console:


*** System restart required ***
Last login: Fri Feb 22 10:34:49 2019 from 10.1.56.185
ubuntu@ip-10-224-73-184:~$ sudo asterisk -rvvvvvvv
Asterisk 16.1.1, Copyright (C) 1999 - 2018, Digium, Inc. and others.
Created by Mark Spencer <markster@digium.com>
Asterisk comes with ABSOLUTELY NO WARRANTY; type 'core show warranty' for detail                                                                                                                                                             s.
This is free software, with components licensed under the GNU General Public
License version 2 and other licenses; you are welcome to redistribute it under
certain conditions. Type 'core show license' for details.
=========================================================================
Connected to Asterisk 16.1.1 currently running on ip-10-224-73-184 (pid = 15603)
[Feb 23 23:00:13] ERROR[30958]: res_http_websocket.c:525 ws_safe_read: Error rea                                                                                                                                                             ding from web socket: Connection reset by peer
[Feb 23 23:00:13] WARNING[30958]: ari/ari_websockets.c:126 ast_ari_websocket_ses                                                                                                                                                             sion_read: WebSocket read error: Connection reset by peer
 Deactivating Stasis app 'app'
  == WebSocket connection from '10.3.100.1:65501' forcefully closed due to fatal                                                                                                                                                              write error
 Activating Stasis app 'app'
  == WebSocket connection from '10.3.100.1:49856' for protocol '' accepted using                                                                                                                                                              version '13'
  == Using SIP RTP CoS mark 5
       > 0x7f6a4000eda0 -- Strict RTP learning after remote address set to: 125.                                                                                                                                                             213.160.11:47332
    -- Executing [61370182002@csntelephony:1] Set("SIP/125.213.160.7-00000022",                                                                                                                                                              "agipath=VerifyCallerAndConnectAgi") in new stack
    -- Executing [61370182002@csntelephony:2] Set("SIP/125.213.160.7-00000022",                                                                                                                                                              "CHANNEL(userfield)=61370182002_61434466477_1550923240.60") in new stack
    -- Executing [61370182002@csntelephony:3] GotoIf("SIP/125.213.160.7-00000022                                                                                                                                                             ", "0?cctester-legb,s,1") in new stack
    -- Executing [61370182002@csntelephony:4] GotoIf("SIP/125.213.160.7-00000022                                                                                                                                                             ", "0?cctester-legb,s,1") in new stack
    -- Executing [61370182002@csntelephony:5] GotoIf("SIP/125.213.160.7-00000022                                                                                                                                                             ", "0?cctester-legb,s,1") in new stack
    -- Executing [61370182002@csntelephony:6] Goto("SIP/125.213.160.7-00000022",                                                                                                                                                              "ari-test,61370182002,start") in new stack
    -- Goto (ari-test,61370182002,1)
    -- Executing [61370182002@ari-test:1] Verbose("SIP/125.213.160.7-00000022",                                                                                                                                                              "1, Inbound call: From 61434466477 to 61370182002|<sip:61370182002@13.55.19.101;                                                                                                                                                             user=phone>") in new stack
  Inbound call: From 61434466477 to 61370182002|<sip:61370182002@13.55.19.101;us                                                                                                                                                             er=phone>
    -- Executing [61370182002@ari-test:2] Wait("SIP/125.213.160.7-00000022", "1"                                                                                                                                                             ) in new stack
    -- Executing [61370182002@ari-test:3] Set("SIP/125.213.160.7-00000022", "CAL                                                                                                                                                             LERID(name)=carsales") in new stack
    -- Executing [61370182002@ari-test:4] Stasis("SIP/125.213.160.7-00000022", "                                                                                                                                                             app,61434466477,61370182002,<sip:61370182002@13.55.19.101;user=phone>") in new s                                                                                                                                                             tack
    -- Channel SIP/125.213.160.7-00000022 joined 'simple_bridge' stasis-bridge <                                                                                                                                                             b45ea910-7628-438b-9124-00162a90ba34>
       > 0x7f6a4000eda0 -- Strict RTP switching to RTP target address 125.213.16                                                                                                                                                             0.11:47332 as source
    -- <SIP/125.213.160.7-00000022> Playing 'agent-pass.gsm' (language 'en')
       > 0x7f6a4000eda0 -- Strict RTP learning complete - Locking on source addr                                                                                                                                                             ess 125.213.160.11:47332
[Feb 23 23:00:46] WARNING[4940][C-00000014]: app.c:938 dtmf_stream: Illegal DTMF                                                                                                                                                              character 't' in string. (0-9*#aAbBcCdD allowed)
[Feb 23 23:00:46] WARNING[4940][C-00000014]: app.c:938 dtmf_stream: Illegal DTMF                                                                                                                                                              character 'm' in string. (0-9*#aAbBcCdD allowed)
    -- <SIP/125.213.160.7-00000022> Playing 'priv-introsaved.gsm' (language 'en'                                                                                                                                                             )
  == Using SIP RTP CoS mark 5
    -- Channel SIP/symbio-00000023 joined 'simple_bridge' stasis-bridge <b45ea91                                                                                                                                                             0-7628-438b-9124-00162a90ba34>
       > 0x7f6a540058c0 -- Strict RTP learning after remote address set to: 125.                                                                                                                                                             213.162.242:13814
       > 0x7f6a540058c0 -- Strict RTP switching to RTP target address 125.213.16                                                                                                                                                             2.242:13814 as source
       > 0x7f6a540058c0 -- Strict RTP learning complete - Locking on source address 125.213.162.242:13814
    -- Channel SIP/symbio-00000023 left 'simple_bridge' stasis-bridge <b45ea910-7628-438b-9124-00162a90ba34>
    -- Channel SIP/symbio-00000023 joined 'simple_bridge' stasis-bridge <b45ea910-7628-438b-9124-00162a90ba34>
    -- Channel Recorder/ARI-0000000d;2 joined 'simple_bridge' stasis-bridge <b45ea910-7628-438b-9124-00162a90ba34>
       > Bridge b45ea910-7628-438b-9124-00162a90ba34: switching from simple_bridge technology to softmix
    -- x=0, open writing:  /var/spool/asterisk/recording/7bd3b80d-dc25-412e-92a2-497e7e281c8c format: WAV, 0x7f6a58012ed0
    -- Channel SIP/125.213.160.7-00000022 left 'softmix' stasis-bridge <b45ea910-7628-438b-9124-00162a90ba34>
       > Bridge b45ea910-7628-438b-9124-00162a90ba34: switching from softmix technology to simple_bridge
    -- Message ended by control
    -- Channel Recorder/ARI-0000000d;2 left 'simple_bridge' stasis-bridge <b45ea910-7628-438b-9124-00162a90ba34>
    -- Channel SIP/symbio-00000023 left 'simple_bridge' stasis-bridge <b45ea910-7628-438b-9124-00162a90ba34>
ip-10-224-73-184*CLI>

Again my question is how do I know get correct DIALSTATUS when the far end declines the call. Below is my code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Timers;
using AsterNET.ARI;
using AsterNET.ARI.Models;
using Newtonsoft.Json;
using SimpleTestApplication.Models;
using SimpleTestApplication.Services;

namespace SimpleTestApplication
{
    public class AriCallManager
    {
        private readonly AriClient _ariClient;
        private readonly ICallContextService _callContextService;
        private readonly IAsteriskSettings _asteriskSettings;
        private readonly List<CallContext> _contextCalls = new List<CallContext>();
        
        public AriCallManager(AriClient ariClient, ICallContextService callContextService, IAsteriskSettings asteriskSettings)
        {
            _ariClient = ariClient;
            _callContextService = callContextService;
            _asteriskSettings = asteriskSettings;
        }

        public void Start()
        {
            _ariClient.OnStasisStartEvent += OnStasisStartEvent;
            _ariClient.OnDialEvent += OnDialEvent;
            _ariClient.OnChannelStateChangeEvent += OnChannelStateChangeEvent;
            _ariClient.OnChannelHangupRequestEvent += OnChannelHangupRequestEvent;
            _ariClient.OnChannelDtmfReceivedEvent += OnChannelDtmfReceivedEvent;
            _ariClient.Connect();
        }

        
        private void OnChannelDtmfReceivedEvent(IAriClient sender, ChannelDtmfReceivedEvent e)
        {
            _callContextService.HandleDtmf(sender, e);
        }

        private void OnChannelHangupRequestEvent(IAriClient sender, ChannelHangupRequestEvent e)
        {
            Console.WriteLine($"OnChannelHangupRequestEvent: {JsonConvert.SerializeObject(e)}");

            _callContextService.Hangup(sender,e);
        }

        private void OnDialEvent(IAriClient sender, DialEvent e)
        {
            Console.WriteLine($"OnDialEvent: {JsonConvert.SerializeObject(e)}");
            _callContextService.HandleMultipleDialing(sender, e);
        }

        private void OnStasisStartEvent(IAriClient sender, StasisStartEvent e)
        {
            _callContextService.AcceptIncomingCall(sender, e);
        }
    }
}

using System;
using System.Collections.Generic;
using System.Linq;
using AsterNET.ARI;
using AsterNET.ARI.Models;
using Newtonsoft.Json;
using SimpleTestApplication.Ioc;
using SimpleTestApplication.Models;

namespace SimpleTestApplication.Services
{
    class CallContextService : ICallContextService
    {
        private readonly IAsteriskSettings _asteriskSettings;
        private readonly IDialService _dialService;
        private readonly INumberRouteService _numberRouteService;
        private readonly IHangupService _hangupService;
        private readonly IAriCallFactory _ariCallFactory;
        private readonly IRecordingService _recordingService;
        private readonly IDtmfService _dtmfService;


        public CallContextService(IAsteriskSettings asteriskSettings, 
            IDialService dialService, INumberRouteService numberRouteService, IHangupService hangupService, IAriCallFactory ariCallFactory,
            IRecordingService recordingService, IDtmfService dtmfService)
        {
            _asteriskSettings = asteriskSettings;
            _dialService = dialService;
            _numberRouteService = numberRouteService;
            _hangupService = hangupService;
            _ariCallFactory = ariCallFactory;
            _recordingService = recordingService;
            _dtmfService = dtmfService;
        }

        public void AcceptIncomingCall(IAriClient ariClient, StasisStartEvent e)
        {
            var ariCallManager = _ariCallFactory.Instance(ariClient);

            if (e.Channel.State == "Ring")
            {
                if (ariCallManager.GetContextCallByChannelId(e.Channel.Id)  == null)
                {
                    var bridgeId = Guid.NewGuid().ToString();

                    var contextChannel = new ChannelContext()
                    {
                        Id = e.Channel.Id,
                        CallerId = e.Channel.Caller.Number,
                        CallerName = e.Channel.Caller.Name,
                        Role = "Caller",
                        Variables = new Dictionary<string, string>()
                        {
                            {"BridgeId", bridgeId},
                            {"Role", "Caller"},
                            {"CallerId", e.Channel.Caller.Number},
                            {"CallerName", e.Channel.Caller.Name}
                        },
                        VirtualNumber = e.Channel.Dialplan.Exten,
                        State = e.Channel.State
                    };

                    var contextCall = new CallContext()
                    {
                        Bridge = new BridgeContext()
                        {
                            Id = bridgeId,
                            Channels = new List<ChannelContext>()
                            {
                                contextChannel
                            }
                        }
                    };

                    ariCallManager.AddContextCall(contextCall);

                    ariClient.Channels.Answer(e.Channel.Id);

                    contextChannel.State = e.Channel.State;

                    contextChannel.StartCallDuration();

                    ariClient.Bridges.Create("mixing, dtmf_events, proxy_media", bridgeId, _asteriskSettings.StasisAppName());
                    ariClient.Bridges.AddChannel(bridgeId,e.Channel.Id);
                    ariClient.Channels.Play(e.Channel.Id, "sound:agent-pass");

                    ariClient.Channels.SendDTMF(e.Channel.Id, "dtmf");

                    Console.WriteLine($"ChannelContext: {JsonConvert.SerializeObject(contextChannel)}");
                }
            }
        }

        public void Hangup(IAriClient ariClient, ChannelHangupRequestEvent e)
        {
            _hangupService.Hangup(ariClient, e.Channel.Id);
        }

        public void HandleMultipleDialing(IAriClient ariClient, DialEvent e)
        {
            if (e.Dialstatus == "ANSWER")
            {
                var role = ariClient.Channels.GetChannelVar(e.Peer.Id, "Role");
                var bridge = ariClient.Channels.GetChannelVar(e.Peer.Id, "BridgeId");
                
                if (role.Value == "Callee")
                {
                    ariClient.Channels.Answer(e.Peer.Id);
                    ariClient.Bridges.AddChannel(bridge.Value, e.Peer.Id);
                }

                _dialService.HandleMultipleDialing(ariClient, e);
                _recordingService.StartRecording(ariClient, e);
            }
        }

        public void HandleDtmf(IAriClient ariClient, ChannelDtmfReceivedEvent e)
        {
            var ariCallManager = _ariCallFactory.Instance(ariClient);

            var dtmfResult = _dtmfService.HandleDtmf(ariClient, e.Channel.Id, e.Digit);

            if (dtmfResult == DtmfEnum.Invalid)
            {
                ariClient.Channels.Play(e.Channel.Id, "sound:auth-incorrect");
            }
            else if (dtmfResult == DtmfEnum.Valid)
            {
                ariClient.Channels.Play(e.Channel.Id, "sound:priv-introsaved");
                var contextCall = ariCallManager.GetContextCallByChannelId(e.Channel.Id);
                _dialService.Dial(ariClient, _numberRouteService.GetDestinationNumbers(e.Channel.Dialplan.Exten), contextCall);
            }
        }
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using AsterNET.ARI;
using AsterNET.ARI.Models;
using Newtonsoft.Json;
using SimpleTestApplication.Ioc;
using SimpleTestApplication.Models;

namespace SimpleTestApplication.Services
{
    class DialService : IDialService
    {
        private readonly IAsteriskSettings _asteriskSettings;
        private readonly IHangupService _hangupService;
        private readonly IAriCallFactory _ariCallFactory;

        public DialService(IAsteriskSettings asteriskSettings, IHangupService hangupService, IAriCallFactory ariCallFactory)
        {
            _asteriskSettings = asteriskSettings;
            _hangupService = hangupService;
            _ariCallFactory = ariCallFactory;
        }

        public void Dial(IAriClient ariClient, List<string> numbers, CallContext callContext)
        {
            var callerChan = callContext.Bridge.Channels.FirstOrDefault(x => x.Role == "Caller");
            if (callerChan == null)
            {
                throw new ArgumentException("Invalid context call: caller/originator channel not found.");
            }

            foreach (var number in numbers)
            {
                if (!string.IsNullOrEmpty(number))
                {
                    var chan = ariClient.Channels.Create($"SIP/symbio/{number}", _asteriskSettings.StasisAppName(), null, null, null, callerChan.Id);

                    ariClient.Channels.SetChannelVar(chan.Id, "BridgeId", callContext.Bridge.Id);
                    ariClient.Channels.SetChannelVar(chan.Id, "Role", "Callee");
                    ariClient.Channels.SetChannelVar(chan.Id, "CallerId", callerChan.CallerId);
                    ariClient.Channels.SetChannelVar(chan.Id, "CallerName", callerChan.CallerName);
                    ariClient.Channels.SetChannelVar(chan.Id, "CalleeId", number);

                    ariClient.Bridges.AddChannel(callContext.Bridge.Id, chan.Id);

                    ariClient.Channels.Dial(chan.Id, callerChan.CallerName, 30);

                    callContext.Bridge.Channels.Add(new ChannelContext()
                    {
                        Id = chan.Id,
                        Role = "Callee",
                        Variables = new Dictionary<string, string>()
                        {
                            {"BridgeId", callContext.Bridge.Id},
                            {"Role", "Callee"},
                            {"CalleeId", number},
                            {"CallerId", callerChan.CallerId},
                            {"CallerName", callerChan.CallerName}
                        },
                        State = chan.State
                    });
                }
            }
        }

        public void HandleMultipleDialing(IAriClient ariClient, DialEvent e)
        {
            var ariCallManager = _ariCallFactory.Instance(ariClient);

            var ctx = ariCallManager.GetContextCallByChannelId(e.Peer.Id);
            if (ctx != null)
            {
                Console.WriteLine($"HandleMultipleDialing: {JsonConvert.SerializeObject(e)}");
                var calleeChan = ctx.Bridge.Channels.FirstOrDefault(x => x.Id == e.Peer.Id);
                if (calleeChan != null)
                {
                    calleeChan.State = e.Peer.State;
                }
                Console.WriteLine($"Callee Chan: {JsonConvert.SerializeObject(calleeChan)}");

                var hangUpChannels = new List<string>();
                foreach (var chan in ctx.Bridge.Channels)
                {
                    if (chan.Role == "Callee" && chan.Id != e.Peer.Id)
                    {
                        hangUpChannels.Add(chan.Id);
                    }
                }
                foreach (var hangUpChannel in hangUpChannels)
                {
                    _hangupService.Hangup(ariClient, hangUpChannel);
                }
            }
        }
    }
}