Softphone Mobile SIP Push Notifications: Fix Missed Calls
Softphone mobile SIP push notifications fix missed calls by waking a suspended app through FCM or APNs when an INVITE arrives, so the phone needs no constant keepalives. Without push, iOS and Android suspend the socket and CGNAT drops the mapping.
- Missed calls on mobile come from three separate failures: a suspended app, an expired NAT mapping, or a network handover that kills the connection.
- Standard Asterisk has no built-in push, so you need a push-capable proxy or a vendor client and PBX pair that supports it.
- A registration expiry of 120 to 600 seconds with push is a good balance; 30 to 60 seconds drains battery and 3600 seconds hides dead NAT mappings.
- Use TLS on port 5061 with SDES SRTP, direct_media=no and rewrite_contact=yes for remote extensions, and protect the PBX with fail2ban and IP rules.
- Capture the traffic with sngrep: INVITE sent with no reply means a dead path or sleeping app, while a re-REGISTER after the INVITE means push was too slow.
Why do mobile SIP softphones miss incoming calls?
A SIP phone on a desk keeps a socket open and waits for INVITEs. A phone in a pocket cannot. iOS suspends background apps within seconds, and Android applies Doze and App Standby, which defer network access. Vendor battery managers on Xiaomi, Huawei and Samsung devices go further and kill the process outright. When the socket is gone, the INVITE from the PBX has nowhere to land.
The second cause sits in the network. On 4G, carrier-grade NAT (CGNAT) creates a mapping when the phone sends REGISTER, and it deletes idle UDP mappings quickly, often in roughly 30 to 120 seconds. TCP and TLS mappings often last a few minutes. After the mapping expires, the PBX still holds a contact that points at a dead public address and port, and the INVITE is silently dropped.
The third cause is handover. When the phone moves from Wi-Fi to 4G, its IP address changes. Existing TCP or TLS connections break, and the PBX keeps the old contact until it expires or a qualify check fails. Registrations only recover when the client notices and registers again.
| Failure mode | Typical symptom | How to confirm |
|---|---|---|
| Suspended or killed app | Phone rings only after you open the app | INVITE leaves the PBX, no response; contact is still shown as reachable |
| Expired NAT mapping | Works for a minute after registering, then calls fail | Contact status turns Unavailable after a qualify; calls work right after a fresh REGISTER |
| Network handover | Calls fail after leaving Wi-Fi or entering a building | Contact source IP differs from the phone's current IP; no new REGISTER seen |
How do softphone mobile SIP push notifications actually work?
Push splits the job in two. The app keeps no connection open. Instead it registers its push token with the PBX or a push proxy. When a call arrives, the proxy sends a message through FCM on Android or APNs on iOS, which the operating system is allowed to deliver even to a sleeping device.
On iOS this must be a PushKit VoIP push, which wakes the app and obliges it to report the call to CallKit immediately. On Android it should be a high-priority FCM data message, since normal-priority messages can be delayed by Doze.
- A caller dials the extension and the PBX or proxy receives the INVITE.
- The proxy finds the stored contact, sees it carries push parameters, and sends the push to FCM or APNs.
- The OS wakes the app, which opens a connection and sends a fresh REGISTER.
- The proxy forwards the held INVITE to the new contact, and the phone rings.
- The user answers and media flows.
Which push options work with Asterisk and FreePBX?
Standard Asterisk has no built-in push gateway. The res_pjsip_notify module only sends NOTIFY messages and cannot wake an app. You need a component that talks to FCM and APNs, and the client has to be built to work with that component.
The client and the push backend must match. A generic softphone from an app store cannot receive push from a PBX that does not know its token format.
| Option | How it delivers push | Notes |
|---|---|---|
| FreePBX with Sangoma Connect (formerly Zulu) or commercial UCP modules | Sangoma-operated push service tied to the Sangoma client | Easiest for FreePBX; client and PBX must be the supported pair and licensed |
| Kamailio or OpenSIPS in front of Asterisk with a push module | Proxy holds the INVITE, sends push, forwards after re-REGISTER | Most flexible and correct; needs SIP proxy skills and your own push credentials or a compatible client |
| Linphone flexisip | Flexisip push notification module, used with the Linphone app | Open source; typically placed as a front proxy for Asterisk |
| Grandstream Wave or Zoiper with a push service | Vendor push relay that the client registers with | Check that the vendor supports your PBX type and region before committing |
| Bria or provider-hosted push (for example from a SIP trunk provider) | Provider-side push proxy | Works when the extension is hosted by the provider, not necessarily your own Asterisk |
| 3CX-style apps | Built in, but tied to that PBX | Not usable with Asterisk |
What PJSIP settings keep a remote extension reachable?
Registration expiry is a trade-off. Very short values such as 30 to 60 seconds keep the NAT mapping fresh but wake the radio constantly and drain the battery. Values like 3600 seconds are easy on the battery but hide a dead mapping for up to an hour. With push, 120 to 600 seconds is a sensible range because the keepalive is no longer what makes calls arrive. Without push, stay near the low end and keep the keepalive below the carrier timeout.
On Asterisk, qualify_frequency sends OPTIONS to the contact and keeps the mapping alive while also marking dead contacts unavailable. Values of 30 to 60 seconds suit UDP behind CGNAT. Clients can also send a double CRLF ping per RFC 5626, which is cheaper than a full OPTIONS exchange.
The settings below are a starting point for a remote extension using TLS and SDES SRTP. Names follow Asterisk PJSIP; adjust the endpoint ID and secrets.
[transport-tls]
type=transport
protocol=tls
bind=0.0.0.0:5061
cert_file=/etc/letsencrypt/live/pbx.example.com/fullchain.pem
priv_key_file=/etc/letsencrypt/live/pbx.example.com/privkey.pem
method=tlsv1_2
external_signaling_address=pbx.example.com
external_media_address=203.0.113.10
local_net=192.168.1.0/24
[2001]
type=endpoint
transport=transport-tls
context=from-internal
disallow=all
allow=opus,ulaw
auth=2001
aors=2001
direct_media=no
rewrite_contact=yes
force_rport=yes
rtp_symmetric=yes
rtp_keepalive=15
media_encryption=sdes
media_encryption_optimistic=no
timers=yes
[2001]
type=aor
max_contacts=1
remove_existing=yes
qualify_frequency=45
minimum_expiration=120
default_expiration=300
maximum_expiration=600
[2001]
type=auth
auth_type=userpass
username=2001
password=Use-A-Long-Random-Secret-HereTLS, SRTP, VPN or WebRTC: which transport should I choose?
TLS on 5061 is the sensible default for plain SIP over the internet. It resists casual interception, and a TCP-based connection typically survives CGNAT longer than idle UDP. Use a valid public certificate such as Let's Encrypt so clients verify the server without installing custom certificates. SDES SRTP encrypts media but exposes keys in signalling, so it is only acceptable over TLS. DTLS-SRTP is used by WebRTC clients. ZRTP has little support among softphones.
TLS does not solve handover. A changed IP breaks the TCP session, so the client must detect the change and register again. Good clients do this on network-change events.
A VPN such as WireGuard keeps the PBX private, so no SIP is exposed. The costs are battery use, the OS sometimes stopping the VPN, and the fact that a sleeping app still needs push to ring reliably. WireGuard's PersistentKeepalive of 25 seconds helps NAT but costs power. Use a VPN when push is unavailable or policy forbids exposing SIP.
| Approach | Battery | Reliability | Security | Setup effort |
|---|---|---|---|---|
| Push-enabled softphone over TLS/SRTP | Good | High | Good | Medium to high |
| Plain SIP over TLS with short keepalive | Poor to fair | Medium; fails when app is killed | Good | Low |
| VPN (WireGuard or OpenVPN) plus softphone | Fair to poor | Medium; still needs push for sleeping apps | Very good, PBX not exposed | Medium |
| WebRTC over WSS client | Fair | Medium; browser or app must stay awake unless push-backed | Good | Medium |
How do I harden an internet-exposed PBX and set up phones?
Once remote phones connect directly, the PBX is a target within minutes. Never leave SIP on UDP 5060 open to the world without protection.
Use strong random secrets, disable anonymous and guest calls (allow_guest=no in the PJSIP general settings), and prefer TLS 5061 over 5060. Run fail2ban against the Asterisk security log, restrict by country or IP where possible, and consider placing Kamailio or an SBC in front for rate limiting. Open only the ports you need, including your RTP range (18000 to 20000 on FreePBX by default; check Settings > Asterisk SIP Settings if you have changed it).
The example below opens TLS signalling and the matching default FreePBX RTP range. If you change the range in Asterisk SIP Settings, change the firewall rule to match.
- Android: set the app's battery usage to Unrestricted, allow notifications, and turn off any vendor optimization (Xiaomi Autostart, Huawei App launch manual management, Samsung Never sleeping apps).
- iOS: enable Background App Refresh and notifications for the app, allow Critical or Time Sensitive alerts where offered, and avoid Low Power Mode on phones that must ring.
- Set the app to start on boot, and keep Wi-Fi on during sleep so home networks do not drop the phone.
- Prefer Opus or G.711 over G.729, which needs licences and degrades under packet loss, and keep the RTP range open and forwarded.
ufw allow 5061/tcp
ufw allow 18000:20000/udpHow do I troubleshoot which failure is causing missed calls?
Work from the PBX outward. First confirm what Asterisk believes about the phone, then look at the packets. Test the same phone on 4G and on Wi-Fi to separate NAT problems from app problems.
The diagnosis in the capture is usually clear. If the INVITE leaves the PBX and nothing comes back, the path is dead or the app is asleep. If a fresh REGISTER arrives only after the INVITE, push worked but too slowly. If the contact's source IP is not the phone's current address, a handover left a stale contact.
- Run pjsip show contacts and check status and RTT; Unavailable or a very old registration points to an expired mapping.
- Turn on pjsip set logger on and place a test call with the app closed.
- Capture with sngrep and watch for INVITE, 100 Trying, 180 Ringing or a 408 Request Timeout and 480 Temporarily Unavailable.
- Compare timestamps between the INVITE and the contact's last REGISTER.
- Repeat on 4G, then on Wi-Fi, then after switching networks mid-session.
- Check the ring time (Dial timeout of 25 to 30 seconds) so voicemail does not answer before the push-woken app can ring.
asterisk -rx "pjsip show contacts"
asterisk -rx "pjsip show endpoint 2001"
asterisk -rx "pjsip set logger on"
asterisk -rx "rtp set debug on"
sngrep -d any port 5061 or port 5060 Common questions
Why does my iPhone SIP softphone not ring when the app is closed?
iOS suspends the app and closes its socket, so the INVITE is never received. Only a PushKit VoIP push from a push-capable PBX or proxy can wake it. A client that registers directly with plain Asterisk and no push backend will not ring when closed.
Does FreePBX have mobile push notifications built in?
Open-source Asterisk does not. FreePBX offers push through Sangoma's commercial clients and services, such as Sangoma Connect, which depend on the matching modules and licensing. Other clients need a separate push proxy in front of the PBX.
Why does a SIP client drain the battery on 4G?
A short registration expiry and frequent keepalives wake the cellular radio again and again. Each wake-up keeps the modem in a high-power state for several seconds. Push lets the client stop frequent keepalives and use longer expiry values.
What keepalive interval should I use behind carrier NAT?
Keep it below the shortest NAT timeout you expect, which can be around 30 seconds for UDP on some carriers. A qualify_frequency of 30 to 60 seconds is common, and TCP or TLS can often use longer intervals. Measure on your own carrier by finding when calls start failing.
Do I still need push if I use a VPN?
Yes, for reliable ringing. The VPN protects traffic and hides the PBX, but the OS can still suspend the app and its tunnel, so incoming INVITEs may not be handled. Push is what wakes the device.