Interest in implementing SIP push notification

The main problem I’m having with my phone system is that my softphones (installed on Android based mobile devices) keep loosing registration and failing to ring on incoming calls. See my previous post here.

The issue is only set to get worse with Android 8 further restricting long running background services. One solution seems to be using push notifications to wake devices when incoming calls come in. There is a draft of an RFC to enable this functionality that has been in development for a little while now.

Does the Asterisk team have any interest in implementing something like this?

I know of noone actively working on such a thing or planning to. As with any changes if someone contributes such a thing, then it would be reviewed like any other change.

How can the push notification find the device? The whole point of registration is to tell the registrar how to reach the device. If there is some other way (e.g statiic address) you can go straight to INVITE without having to receive a registration.

@jcolp, thanks for the confirmation. I appreciate it.

@david551, you might want to checkout the RFC draft. It looks like the push notification token is included in the registration request. Then, when a call comes in, the token is used to send a notification to the device.

I think that I’m solved with next to parameter in sip.conf when definating the user:

qualifyfreq=20; How often to check is it peer online, same as traditional PING command!
qualify=5000; After how many milisecond from last ping to consider that peer is offline! 5000 send 10 rettransmision packets what is 5s, 2packet/sec

You need to provide a link to the draft, but how does the registrar know where to send the token, if there is no active registration and no static address.

Hi all,

If I understood it correctly, the issue is that endpoint just “falls asleep”, thus stopping any communication with Asterisk before the latter even learns about this issue.When an incoming calls comes in, Asterisk looks up the contacts and finds out that the requestedendpoint is not registered anymore. So I am in doubt whether such kind of token would help you.

The issue should be resolved at the endpoint side. Which SIP client are you using? Have you tried to play with internal keep-alives? If you reduce qualify interval, would this help, or the system falls asleep anyway?

P.S. I am extensively using Zoiper with SIP and IAX on various devices running Androind 6 and 7, and this issue occurs very rarely. Unfortunately, I have little experience with Android 8.

@david551 the link to the RFC is in the OP. Here it is again in case that isn’t working for you: https://tools.ietf.org/html/draft-ietf-sipcore-sip-push-11

@Pentium-5 the idea with the push notifications is that it wakes up the device and allows it to re-register just in time to receive the incoming call. The abstract of the RFC explains it nicely. To answer your question, yes, I have tinkered extensively with quality frequencies and keep-alives with no noticeable improvement.

I would love to talk with you about your experience with Zoiper. I chose Linphone because it’s open source, but I may have to look at other options.

  • What Android phones do you use it with?
  • Are they always charging or do they run on battery mostly?
  • How’s the call quality? I have issues where I sound quiet to the other party, and they can sometimes hear themselves talk.
  • Do they ring reliably on incoming calls?
  • Have you ever interacted with the company behind Zoiper? How responsive are they?

Thanks for the tip @easydoor. Unfortunately, I don’t believe it is this simple. As far as I understand it, when using TCP (required for TLS), the endpoint maintains a constant connection with Asterisk. As soon as that connection goes down, the transport is shutdown and the endpoint will not ring on incoming calls.

When an Android device sleeps it brings down all of its network interfaces breaking any active TCP connections regardless of keep-alive activity.

I may be oversimplifying or misunderstanding the situation, but that’s the problem as I understand it.

Hi @ringdom

Sony XPeria Z5, Huawei Honor 10 as well as older models: Sony XPeria Z1, Huawei Honor 7

The run on the battery most of the time. I have not noticed considerable increase in power consumption.

It primarily depends on the quality of the underlying network (bandwidth, delay, jitter). Effects of packet losses and audio distortion may take place on 3G with poor coverage. No issues were noticed on self-managed Wi-Fi with “thick” uplink. It is also important to set the correct parameters in the app itself: echo cancellation, gain conrol etc.

In the vast majority of cases - yes. However, rarely (around 1% of cases) they do not ring, and in all such occurences this issue is related to the loss of registration. App restart solves the issue.

Yes, as a client who ordered full (paid) versions of their software, I have filed several issue requests.
They provide valuable recommendations, but I cannot say that they are fast - it may take from 2 hours to 2 working days to get a response.

P.S. In terms of call quality and operation stability, there is no difference between free and commercial versions. All the difference is their functionality.

One more thing. One of the customers was claiming about frequent losses of registrations for mobile endpoints operating from the outside. We resolved that issue by increasing UDP session timeout on the router (with NAT and firewall) used for connecting Asterisk to the Internet (nothing common with routers in local networks where mobile endpoints operate).

Thank you so much @Pentium-5 for the helpful information. I saw you mentioned UDP session timeout. Do you have any experience using TCP or TLS as the transport? I prefer to use TLS exclusively which rules out UDP.

You really have to use UDP for the media, if you want any sort of quality on a complex network.

I am a strong proponent of UDP - it has more predictable sound quality. My experience with TCP is not so solid…

Thanks for the replies. Maybe I’m missing something, but as I understand it, when I use TLS for the transport I’m really only talking about the SIP signalling layer not the media. The SRTP media stream still uses UDP, correct?

SRTP uses UDP, but some people try to tunnel the media through TCP.

It can be done using AGI scripts in extensions.conf as long as you get the push token first from the registered phone. That will depend on the client used. With Linphone it’s easy as the push token is sent in the Contact info and stored in Asterisk’s registry database as part of the contact sip info. The token is tagged pn-tok . I created a firebase account, compiled my own copy of Linphone withthe generated firebase api ID from my Firebase project for the client and used a AGI php script to push to the phone first then send the call to the phone (with a delay in between to give the phone time to wake and re-register). The push script first gets the token from the asterisk astdb.sqlite database before it sends out the push. Note, this is my own personal system with very few phones so I have no idea on the scalability here. I also set ignoreregexpire=yes in sip.conf to keep the registration from expiring in the database. Also note that you need to use your own Firebase token to have this work. It won’t work for a pre-packaged voip client as they have their own private keys compiled in.

As another note, there is a gotcha with using Linphone with push notifications in Asterisk (older versions anyhow. I’m using 11). Since the Linphone code sends the pn-tok with other tags in the contact line, it ends up being over a 255 byte character limit. This will cause an inbound call to fail to linphone since the ACK will be sending a truncated contact field out which will be invalid and cause a loss of connection 30 seconds in. I modified the Linphone code to remove the uneeded pn-type and pn-silent tags from the contact line to bring it under that 255byte limit.

1 Like

Wow, it seems like you built exactly what I’m trying to. Could you share your AGI scripts/configuration? Also, I see that you’re using chan_sip. Any idea if this would run into any problems with pjsip?

I don’t see pjsip being an issue. I based the method I used from this example:
https://www.zoiper.com/en/tutorials/push-notifications


extensions.conf entry:

[inbound]
exten => _1000,1,Set(ph=<your_phone_extension>)
exten => _1000,n,Answer()
exten => _1000,n,Set(tok=${DB(SIP/Registry/${ph})})
exten => _1000,n,AGI(/opt/etc/asterisk/push.php,SIP/Registry/${ph}, ${tok})
exten => _1000,n,Wait(4)
exten => _1000,n,Dial(SIP/${ph},30,rt)


push.php script:

#!/opt/bin/php-cli -q
<?php

$db2=new SQLite3('/opt/etc/asterisk/tokens.sqlite3');
$API_ACCESS_KEY='<YOUR_FIREBASE_API_SERVER_KEY>';
$f=fopen("/tmp/log.txt","w");
$key="";
$k=$argv[1];
if (isset($argv[2])) {
        $v=$argv[2];
        if (preg_match('/pn-tok=([^;]+)/i', $v,$token)) {
                $v=$token[1];
                $db2->query("delete from keys where key='$k'");
                $db2->query("insert into keys values( '$k','$v')");
                fwrite($f,"got token $v\r\n");

        }
        fwrite($f,"k=$k, v=$v\r\n");
}
//we read here in case we didnt get a new token so use the stored value
$results=$db2->query("SELECT value FROM keys where key = '$k'");
if ($row=$results->fetchArray())
        $key=$row["value"];
if ($key != "") {

fwrite($f,"key=$key\r\n");

$registrationIds =  array($key) ;

$msg = array
(
    'title'     => 'Notification',
    'body'        =>'Wake Up'

);

$fields = array
(
    'registration_ids'    => $registrationIds,
    'data'                => $msg,
    'priority' =>10
);

$headers = array
(
    'Authorization: key=' . $API_ACCESS_KEY,
    'Content-Type: application/json'
);

$ch = curl_init();
curl_setopt( $ch,CURLOPT_URL, 'https://fcm.googleapis.com/fcm/send'
);
curl_setopt( $ch,CURLOPT_POST, true );
curl_setopt( $ch,CURLOPT_HTTPHEADER, $headers );
curl_setopt( $ch,CURLOPT_RETURNTRANSFER, true );
curl_setopt( $ch,CURLOPT_SSL_VERIFYPEER, false );
curl_setopt( $ch,CURLOPT_POSTFIELDS, json_encode( $fields ) );
$result = curl_exec($ch );

curl_close( $ch );

fwrite($f,"$result\r\n");
} // if key
fclose($f);
?>
1 Like

Thanks! I really appreciate it.