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. 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 https://vobiz.ai . 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 https://github.com/nuvyntralabs/VOBIZ-VOIP-Call | | Research | nuvyntralabs.github.io/research/vobiz-voip-call/ https://nuvyntralabs.github.io/research/vobiz-voip-call/ | | Setup | SETUP GUIDE.md https://github.com/nuvyntralabs/VOBIZ-VOIP-Call/blob/main/DOCS/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 IDLE → OUTGOING → RINGING → INCOMING → CONNECTING → ACTIVE → ENDING → FAILED . 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 as backend/.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 needs google-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 VoIP — VOBIZ-VOIP-Call https://github.com/nuvyntralabs/VOBIZ-VOIP-Call | | A MAUI session model: call state, hold, mute, pluggable signaling | Plugin.Maui.VoipCore https://nuvyntralabs.github.io/packages/plugin-maui-voip-core/ | | The written map of this proof | Research: VOBIZ VoIP Call https://nuvyntralabs.github.io/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 https://www.linkedin.com/in/niladri-padhy-7ab41626/