Call Pickup ignores dial options

I’ve dabbled with the Pickup() application a little bit in the past, but I’m taking a closer look at it now to implement Call Pickup features.

It seems that Pickup() directly bridges two channels together without regard to the nature of the Dial() that was being executed. For instance, any time a phone rings, I have the G option set in dial to split the call upon answer. With call pickup, this doesn’t happen, which breaks the dialplan flow of my system so two channels are not supposed to be directly connected like that. The channels themselves get destroyed too as the hangup handlers execute as soon as Pickup() is called.
My end goal would be to have everything be exactly the same as if the intended station answered, except a different station answered instead, so at that point it’s like that was the channel dialed instead, but the G option, for instance, would still be obeyed: the caller going to the caller priority and the station using Pickup() going to the caller + 1 priority as usual.

I’m wondering if this is by design and, assuming that there isn’t a way to do this already, if it would be practical to modify the application to do this? This is for a regular undirected pickup, but the same would apply for a directed pickup.

Or would Pickup() be too unsuitable for achieving this, and I’d be better off writing my own Call Pickup code in dialplan, perhaps using ChannelRedirect(), etc.? Just want to make sure I didn’t overlook something basic here.

[WARNING: Contains false assumptions about which side is manipulated by Pickup().]

That’s because the dialplan thread that owns the channel has become the thread that is executing Pickup, not the one executing Dial. Only one thread can own a channel at any one time and the only way a thread can be told that it has a lost a channel is by hanging up the channel data structure, after transferring its key contents to a newly created channel data structure (a masquerade operation).

In the pickup case, the original B side is obviously dead, but the information about the source of the original A side should have been copied into a new B side channel,before the empty shell of the original channel data structure is hung up.

Everything to do with the G option is stored in the local data of the redundant PBX thread, not in either channel.

Thanks, David,

I am not even referring to the information in the channel specifically, though I suppose logically that would need to be there as well. In the dialplan, all that is executed after Pickup() is the h extension for the channel that was picked up. It seems like Pickup() just directly answers whatever was being dialed and ignores anything else. I don’t see any other dialplan execution in the console at all that would allow me to redirect each end to the proper place or anything of that sort. So how I would make sure the G option is obeyed, if that is at all possible?

I don’t believe Pickup cares what, if any, application is being run before the pickup, so I don’t think it makes sense to say what has been dialled. In the context of Dial, it will pickup the dialling channel, not the dialled one.

You might be able to achieve what you want by making the target of the Dial with G to be a local channel, and then picking up the ;2 side of the local channel, as it does a Dial to the final destination. It may be necessary to disable optimisation of the local channel, and there may be other subtle effects of not directly connecting the initial incoming and final outgoing sides, so you will have to experiment.

As a technical detail, which I don’t think has a material effect in this case, where the original code did a masquerade, the current code requests a stasis call pickup operation. I haven’t followed that through, but it is possible that it doesn’t need to create a new channel.

It might be worth pointing out that the original A channel has been answered, before the stasis request, so the pickup would never be reached if one strictly split control on answer.

Hmm… that might work if I knew I was doing a call pickup, but if the actual device endpoints are in there, it seems like that would cause a problem there.

Usually I do disable local channel optimization. The docs say that could lead to weird behavior, but usually it leads to weird behavior if you don’t disable that. CDR gets messed up, hangup handlers execute, variables disappear, etc.

Right now, it’s not clear to me which leg it’s picking up because the G dialplan fork never happens. But if it is picking up the caller leg and not the callee leg, that would also be problematic for me.

For a regular incoming call, I’m calling the device endpoint. For a call waiting, I’m dialing a dummy channel that does some out of band monitoring and polls for either call waiting cancelled, call waiting answered, or something else. At that point, it hangs up the local channel with a cause code to signal what action should be taken; if answered, splitting the same way Dial with G would. I anticipated with pickup, answering this would split the same way.

By “original code”, do you mean from an earlier version of Asterisk? I think a masquerade is maybe what I was looking for. Maybe I can try forward-porting that and see if the older Pickup does what I need to. Otherwise, it sounds like I might have better luck with ChannelRedirect than Pickup(), since then I can dictate the dialplan flow.

Yes. I meant older versions of the code.

Although a lot of the code uses/used masquerades, the internal API for them is not exposed, as anything but C code. I haven’t checked if masquerade still exists as an explicit operation in the source code. There was a big rewrite to try and replace masquerades by less drastic operations. Masquerades might be easy at a surface level, but details are difficult to follow.

What I think you want is the processing that you would get for a SIP 302 redirect on the B side. I don’t know how that is now handled.

I have no idea how SIP redirects work. The operation should be channel-type agnostic though, i.e. compatible with SIP, DAHDI, etc. I don’t make use of SIP redirects in any way, even for SIP. Call forwarding is done through Asterisk, not through redirects.

I’m going to see if storing channels in a DB and using a FIFO queue works out well. Runtime pruning might be an issue but if Pickup has moved away from masquerades, then it sounds like I’d be better off recreating Pickup in dialplan.

On looking further at the code, it does, in fact, look like the B side is being replaced, and it will only work within a Dial. In that case, I don’t understand why G isn’t working.

Neither do I, that’s why I was a bit puzzled at first.

In any case, I managed to fix this by avoiding the use of Pickup() / PickupChan() entirely - fortunately, call pickup is not really a complicated thing. Here is what I am using instead:

[call-pickup-set] ; ARG1 = pickup group name (which starts with the CENTREX group anyways), ARG2 = called # (for Directed Call Pickup)
exten => s,1,Set(LOCAL(prefix)=callpickup${ARG1}/${EPOCH})
	same => n,Set(LOCAL(suffix)=0)
	same => n,While($[${DB_EXISTS(${prefix}.${suffix})}])
	same => n,Set(LOCAL(suffix)=$[${suffix}+1])
	same => n,EndWhile()
	same => n,Set(DB(${prefix}.${suffix})=${CHANNEL})
	same => n,Set(LOCAL(prefix2)=callpickupdirected${ARG1}${ARG2}/${EPOCH}) ; directed call pickup must still be within the same pickup group
	same => n,Set(LOCAL(suffix2)=0)
	same => n,While($[${DB_EXISTS(${prefix2}.${suffix2})}])
	same => n,Set(LOCAL(suffix2)=$[${suffix2}+1])
	same => n,EndWhile()
	same => n,Set(DB(${prefix2}.${suffix2})=${CHANNEL})
	same => n,Return(${prefix}.${suffix},${prefix2}.${suffix2})

[call-pickup-get] ; ARG1 = pickup group name, ARG2 = called # (for Directed Call Pickup)
exten => s,1,GotoIf($["${ARG2}"!=""]?directed,1:undirected,1)
exten => undirected,1,Set(LOCAL(puchannels)=${DB_KEYS(callpickup${ARG1})})
	same => n,While($["${SET(LOCAL(puchannel)=${SHIFT(puchannels)})}" != ""])
	same => n,ExecIf($["${SET(LOCAL(ret)=${CHANNELS(\b${DB_DELETE(callpickup${ARG1}/${puchannel})})})}"!=""]?Return(${ret})) ; found the earliest channel that exists, return that and delete so nobody else picks this up
	same => n,EndWhile()
	same => n,Return() ; no channels available for pickup
exten => directed,1,Set(LOCAL(puchannels)=${DB_KEYS(callpickupdirected${ARG1}${ARG2})})
	same => n,While($["${SET(LOCAL(puchannel)=${SHIFT(puchannels)})}" != ""])
	same => n,ExecIf($["${SET(LOCAL(ret)=${CHANNELS(\b${DB_DELETE(callpickupdirected${ARG1}${ARG2}/${puchannel})})})}"!=""]?Return(${ret})) ; found the earliest channel that exists, return that and delete so nobody else picks this up
	same => n,EndWhile()
	same => n,Return() ; no channels available for pickup

These can be set and get from the appropriate channels, ensuring that the channel is deleted from the calling channel on a called party hangs up first condition, in case dialplan execution continues.

Now, bridging works as desired, and I can control exactly where each channel goes by using the subroutines to get the channel name and then redirect it to the same place that the G option in dial would have it go.

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.