- Joined
- Apr 21, 2025
- Messages
- 12
- Reaction score
- 3
Hi all,
we have a reproducible issue where the caller's first word is lost after answering a call on the Android app. We measured it over six weeks across two app versions, two audio engines and two network paths. The numbers are very consistent, so here is the full write-up.
Environment
Verbose logging was enabled for all tests. The tunnel was already up before each call — onTunnelState state=TunnelState(connected=true, secured=true, mediaMode=MediaMode(fastTx=true, fastRx=true)). So no time is spent establishing the tunnel during the call.
Symptom
About a second passes between the answer swipe and audio actually flowing. The caller has already started speaking by then, so their first word is lost.
Measurements from the app logs
Three markers, all read from the same clock, so there is no clock-offset issue:
pcap timeline (test 1, captured on the PBX)
Times relative to the start of the capture. Only the relevant messages; REGISTER/SUBSCRIBE/NOTIFY omitted.
Note that the 216 ms for the 407 challenge is not a local operation — the response has to travel back through the tunnel to the mobile device and return. On mobile data this is the single largest item.
The media path itself is clean
So this does not get written off as a network problem:
Second, independent issue: push latency
From the FcmService: Received PUSH message, sent at ..., now is ... (diff ... ms) lines:
Questions
we have a reproducible issue where the caller's first word is lost after answering a call on the Android app. We measured it over six weeks across two app versions, two audio engines and two network paths. The numbers are very consistent, so here is the full write-up.
Environment
- 3CX: Hosted, <FQDN>, version <PBX-VERSION>
- Caller: Yealink T4x/T5x on the LAN (ext. 180 resp. 230), G.722
- Callee: 3CX Android app, ext. 203
- Device: Google Pixel 8, Android 17 (API 37), Tensor G3
- App versions: 20.6.0.20 (test 1), 20.7.0.5 (tests 2 and 3)
- Connectivity: NOT SIP-registered — push (FCM) + 3CX tunnel
- Engine config: useTunnelOnly=true, maxJbSize=360, framesPerBurst=320
Verbose logging was enabled for all tests. The tunnel was already up before each call — onTunnelState state=TunnelState(connected=true, secured=true, mediaMode=MediaMode(fastTx=true, fastRx=true)). So no time is spent establishing the tunnel during the call.
Symptom
About a second passes between the answer swipe and audio actually flowing. The caller has already started speaking by then, so their first word is lost.
Measurements from the app logs
Three markers, all read from the same clock, so there is no clock-offset issue:
- A = IncomingCallsActivity: OnSwipe answer — user answers
- B = Core.VceLine: makePickupCall destination=..., replaces=... — INVITE leaves the app
- C = Core.VceCallListener: event=established — 3CX confirms setup
- A → B: 90 ms
- B → C: 1.137 s
- A → C: 1.227 s
- A → B: 64 ms
- B → C: 1.146 s
- A → C: 1.210 s
- A → B: 24 ms
- B → C: 0.447 s
- A → C: 0.471 s
- The app-internal share is negligible (24–90 ms). The time is spent between the pickup INVITE leaving the app and the PBX confirming.
- Switching opensl to oboe changed nothing at all (1.137 s vs 1.146 s). The Android audio layer is not the cause.
- The network path accounts for ~700 ms. But 447 ms remain even on Wi-Fi.
pcap timeline (test 1, captured on the PBX)
Times relative to the start of the capture. Only the relevant messages; REGISTER/SUBSCRIBE/NOTIFY omitted.
- t=31.894 — INVITE sip:203, from Yealink 180
- t=31.938 — 180 Ringing to the caller; forked to the clients of ext. 203
- t=32.060 — push sent to the app (server timestamp taken from the push payload)
- t=35.455 — INVITE with Replaces from the app arrives: 127.0.0.1:5080 → 127.0.0.1:5060, CSeq 19020, User-Agent "3CXPhone for Android 20.6.0.20"
- t=35.469 — SIP/2.0 407 Proxy Authentication Required, followed by ACK
- t=35.671 — INVITE CSeq 19021 with Proxy-Authorization — +216 ms for the challenge alone
- t=35.684 — first tunnel SRTP, PBX → app (192.168.200.32:5090 → x.x.x.x:34640)
- t=35.752 — 100 Trying
- t=35.753 — 200 OK
- t=35.880 — first tunnel SRTP, app → PBX
- t=35.895 — ACK from the app
- t=35.969 — second, internal INVITE to sip:[email protected]:5080, CSeq 2
- t=36.030 — 200 OK from the bridge, SDP carrying the actual media address
- t=36.040 — first RTP, PBX → Yealink (G.722)
- t=36.070 — 200 OK to the calling Yealink
- t=36.179 — ACK from the Yealink
- t=36.236 — first RTP, Yealink → PBX
Note that the 216 ms for the 407 challenge is not a local operation — the response has to travel back through the tunnel to the mobile device and return. On mobile data this is the single largest item.
The media path itself is clean
So this does not get written off as a network problem:
- Codec: G.722 on both legs, no transcoding
- Packets: 420 (PBX → Yealink), 417 (Yealink → PBX) over ~8.3 s
- Loss: none
- Largest gap: 68 ms, single occurrence
Second, independent issue: push latency
From the FcmService: Received PUSH message, sent at ..., now is ... (diff ... ms) lines:
- 22 Jun 2026: 1517 ms, 1573 ms
- 07 Aug 2026: 2129 ms, 2142 ms, 2165 ms, 2259 ms
Questions
- Can the digest challenge (407) on the pickup INVITE be skipped for clients already authenticated through the tunnel? It costs a full extra round trip to the device.
- Why is a second INVITE to the 3cxBridge needed after the 200 OK, and could media negotiation be started in parallel with signalling?
- Are ~447 ms between pickup INVITE and "established" the expected figure for a push/tunnel client on the same LAN as the PBX, or is ours too high?
- Is the ~2.2 s average push delivery time a known issue? Is there anything server-side that affects the send timestamp?
- Does useTunnelOnly=true need to apply even when the client is demonstrably on the same network as the PBX? Could this be relaxed per extension?