cd /news/ai-products/voipcall-a-native-android-dialer-for… · home topics ai-products article
[ARTICLE · art-138217] src=dev.to ↗ pub= topic=ai-products verified=true sentiment=· neutral

VoIPCall: A Native Android Dialer for PSTN Calls on Vobiz

A developer has released Vobiz VoIP, a native Kotlin Android dialer that places and receives PSTN calls through the Vobiz platform using SIP over secure WebSocket signaling and WebRTC audio. The app, targeting Android 12 and later, uses Firebase Cloud Messaging to wake the handset for inbound calls even after the process has been terminated, with a Node.js backend handling Vobiz webhooks and keeping the Vobiz Auth ID and Auth Token out of the APK. The source and setup documentation are published on GitHub under the nuvyntralabs organization.

by read7 min views2 publishedSep 23, 2026

A field app that places a phone-network call should look like the phone people already use, and it should still ring after the process has been swiped away.

Vobiz VoIP is that dialer. It is a native Kotlin app for Android 12 and later. Calls go through Vobiz. Signaling is SIP over a secure WebSocket. Audio is WebRTC. A TypeScript backend answers Vobiz webhooks and wakes the handset with Firebase Cloud Messaging.

This post is the walkthrough: what the dialer does, how an outbound call leaves the keypad, how an inbound call arrives when the app is not running, and where this proof sits next to Plugin.Maui.VoipCore.

Vobiz VoIP is a public Android proof. The application id is com.Vobiz.vobizvoip. The UI is Jetpack Compose. One Vobiz SIP endpoint is registered per device. One Vobiz number, in E.164, is that device’s caller ID.

The phone holds the SIP username and password. The Vobiz Auth ID and Auth Token stay in the backend. They are not compiled into the APK.

| App | Vobiz VoIP · com.Vobiz.vobizvoip | | Platform | Android 12+ (API 31) · Kotlin · Jetpack Compose | | Signaling | SIP over wss://registrar.vobiz.ai:5063/ | | Media | WebRTC audio (DTLS-SRTP) | | Wake-up | Firebase Cloud Messaging, including a terminated process | | Source | github.com/nuvyntralabs/VOBIZ-VOIP-Call | | Research | nuvyntralabs.github.io/research/vobiz-voip-call/ | | Setup | SETUP_GUIDE.md |

The repository contains the Android app and a local Node.js backend. The backend exposes the Answer URL, call state, and the FCM send. A tunnel publishes that process at an HTTPS origin Vobiz can reach.

Vobiz ships a Flutter Android sample and a Java REST SDK for the Voice API. The REST SDK does not open a WebRTC audio session, and the sample is not a Kotlin dialer. This proof is the native client: OkHttp for the SIP WebSocket, the Android WebRTC library for media, and a phone-shaped UI on top.

Android dialer
    |  SIP over WSS          WebRTC audio
    v
Vobiz registrar  +  media edge
    |
    v
Public phone network
    ^
    |  Answer and Hangup webhooks
Backend  --Firebase push-->  the same phone

Call phases on the device run IDLEOUTGOINGRINGINGINCOMINGCONNECTINGACTIVEENDINGFAILED. A finished call is stored as COMPLETED, MISSED, DECLINED, CANCELED, or FAILED. That history is the app’s own log. It is not written to Android’s system CallLog.

The shell matches a handset dialer.

A leading 0 is replaced with the country code. A bare national number gets the country code prepended. A number that already starts with + is left unchanged. The country code comes from the SIM. With no SIM, Settings Default country is used. The last fallback is India (+91).

What was typed What is dialed
+9198… Unchanged
0091… 00 becomes+
098… Leading 0 becomes+ and the country code
987… Country code prepended
* or# feature codes Left as entered

Outbound starts on the keypad, from a contact, from a recent row, or from another app. The dialer normalizes the number, then asks the backend to start the call. Vobiz dials the PSTN number. When the other phone answers, the WebRTC session carries both directions of audio.

On the call screen the user can mute, route audio to the speaker, send DTMF, and hang up. While a native cellular call is off-hook, the VoIP microphone mutes itself.

Every inbound PSTN call is parked in a short-lived Vobiz conference, then delivered by push. The same path runs in the foreground, in the background, and after the app has been removed from Recents.

MISSED. Declining writes DECLINED. The caller hears silence, or the conference wait sound, for the few seconds between the push and the app joining. A DID with no registered device is rejected. The backend logs No device registered this DID.

Do not test by calling the configured caller ID from the same device.

Save and connect stays disabled until the required fields are valid. SIP credentials are encrypted with an Android Keystore AES-GCM key.

Settings field What it is
SIP username and password This device’s Vobiz SIP endpoint. One endpoint per phone.
Registrar WSS URL wss://registrar.vobiz.ai:5063/
SIP domain registrar.vobiz.ai
Public backend HTTPS URL The tunnel in front of the local backend.
POC device token The same DEVICE_TOKEN asbackend/.env .
Vobiz caller ID This device’s DID, E.164, starting with + .
Default country Used only when the device has no SIM.
Record calls On by default. Playback is available from Recents.

A healthy status row reads Registered and ready for calls for SIP, and Online with Firebase ready for the backend. The Vobiz Auth ID, Auth Token, and webhook token are backend secrets. The Answer and Hangup URLs are set on the Vobiz Voice Application, not in the Android form.

Grant microphone, notifications, contacts, phone state, and nearby-devices (Bluetooth) when prompted.

The app does not register as the OS default dialer. Other apps open it with an intent. Numbers are normalized before dialing.

// Open the dialer, optionally pre-filled
startActivity(Intent(Intent.ACTION_DIAL, Uri.parse("tel:+919876543210")))

// Place the call
startActivity(
    Intent("com.Vobiz.vobizvoip.action.CALL").apply {
        setPackage("com.Vobiz.vobizvoip")
        putExtra("number", "09876543210")
    },
)

The call history, including a recording path when recording is on, is a content provider. The caller declares com.Vobiz.vobizvoip.permission.READ_CALL_LOG and queries content://com.Vobiz.vobizvoip.provider.calllog/calls. Columns include number, display_name, type (1 incoming, 2 outgoing, 3 missed), date, duration, and recording_path. The provider streams the recording. The backend auth token stays inside Vobiz VoIP.

What you see What to do
SIP stays disconnected Confirm the endpoint username, password, wss:// registrar, and domain. Use the SIP refresh action in Settings.
Backend stays offline The tunnel URL must be the current PUBLIC_URL , and the backend process must be running.GET /health on that origin should succeed.
Inbound never rings, backend is online GET /health reports Firebase only when the service-account key loaded. The app needsgoogle-services.json , notifications, full-screen incoming calls, and unrestricted battery.
Caller hears an error and drops No device has registered that DID. Open the app on the phone that owns the number so it can register.
The other device rings You called device 1’s caller ID. Each handset has its own DID and its own SIP username.
Call ends with no answer The pending inbound call expires after 30 seconds.
Two-way audio fails on some networks STUN alone does not cross every mobile or symmetric NAT. Production media needs TURN.
A second open of the same SIP user fights the first One SIP endpoint per device. Do not share an endpoint across phones.

This repository is the Android proof for one vendor path. It is not a MAUI package, and it is not pulled in by nuvyn init.

Need Tool
A native Android dialer for Vobiz PSTN, with recents, keypad, and push wake-up Vobiz VoIPVOBIZ-VOIP-Call
A MAUI session model: call state, hold, mute, pluggable signaling Plugin.Maui.VoipCore
The written map of this proof Research: VOBIZ VoIP Call

Use Vobiz VoIP when the product is an Android handset on Vobiz and the UI has to be the dialer. Use VoipCore when a .NET MAUI app needs a session model and the signaling stack stays replaceable.

You need JDK 17, Android SDK 36, a physical Android 12+ device, Node.js 22+, a tunnel, one Vobiz number, one SIP endpoint for that device, and a Firebase project for com.Vobiz.vobizvoip. A second phone on the public network is the other leg.

./gradlew testDebugUnitTest assembleDebug
adb install -r app/build/outputs/apk/debug/app-debug.apk

Then, in order: status shows ready, an outbound call to the second phone has two-way audio, an inbound call works in the foreground, the same inbound works after the app is swiped away, and a second device rings only when its own caller ID is dialed.

By Niladri

── more in #ai-products 4 stories · sorted by recency
── more on @vobiz 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
Live at https://your-agent.zahid.host
Get free account → Pricing
from €0/mo · no card required
LIVE [news/voipcall-a-native-an…] indexed:0 read:7min 2026-09-23 ·