fix(callkit): add voip background mode so incoming calls work#84
Conversation
Greptile SummaryThis PR fixes incoming CallKit calls by adding the missing
Confidence Score: 5/5Safe to merge — the Info.plist additions are minimal and correct, and the new test script is a dev-only debug tool with no production impact. Both changes are narrow and well-understood. The No files require special attention. Important Files Changed
Sequence DiagramsequenceDiagram
participant Caller as voice_caller.py (LXST/Sideband)
participant lxmd as lxmd (shared RNS)
participant Columba as ColumbaApp (iOS)
participant CK as CallKit / callservicesd
Note over Caller: RNS.Reticulum() — joins shared lxmd
Caller->>lxmd: phone.announce()
Caller->>lxmd: Transport.request_path(tel_dest)
lxmd-->>Caller: path resolved
Caller->>Columba: phone.dial(id_bytes) via LXST.Telephone
Columba->>CK: reportIncomingCall(uuid, update)
Note over CK: BEFORE fix: callservicesd denies CXXPCCallSource<br/>(missing voip UIBackgroundMode)<br/>→ providerDidReset → currentCallUUID=nil → dismiss
Note over CK: AFTER fix: voip + audio in UIBackgroundModes<br/>+ NSMicrophoneUsageDescription present
CK-->>Columba: provider(_:perform: CXAnswerCallAction)
Columba-->>Caller: call connected, Opus audio active
Reviews (4): Last reviewed commit: "address greptile review feedback (greplo..." | Re-trigger Greptile |
|
@greptile review |
…alls work
Incoming voice calls were unusable: the CallKit screen flashed the caller's
name, auto-dismissed, reappeared as 'Unknown', and couldn't be answered.
Root cause: the app's UIBackgroundModes was missing 'voip', so callservicesd
DENIED creation of the CallKit call source and reset the provider
(providerDidReset → CallManager.handleCallKitReset → currentCallUUID=nil +
callState=.ended). Found via the simulator's com.apple.calls.callkit unified
log ('Denying creation of CXXPCCallSource … Not accepting connection'); the
device diag.log only showed the effect (providerDidReset).
- Info.plist: add 'voip' (+ 'audio') to UIBackgroundModes; add
NSMicrophoneUsageDescription (the 'no audio' half — mic capture needs it).
'voip' is just an Info.plist key, no provisioning capability required.
- Tests/interop/voice_caller.py: headless LXST caller (Sideband's
ReticulumTelephone, wire-identical) that dials the sim's telephony dest to
reproduce the incoming-call/CallKit path on the simulator — where CallKit
runs, so the bug + the framework deny reason are observable (unlike on device).
Verified on sim: with the fix, reportIncomingCall succeeds + callservicesd
'Created CXXPCCallSource' — no deny, no providerDidReset.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- voice_caller.py: drop unused 'threading' import; make LXST/Sideband checkout paths overridable via LXST_SRC/SIDEBAND_SRC (SIDEBAND_SRC matches conftest) with a clear error if missing, instead of hardcoded ~/repos paths. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
53b1d26 to
a89228a
Compare
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
Incoming voice calls were unusable on device: the CallKit screen briefly showed the caller's name, auto-dismissed, reappeared as "Unknown", and couldn't be answered (or had no audio).
Root cause
The app's
UIBackgroundModeswas missingvoip, socallservicesddenied creation of the CallKit call source and reset the provider:That teardown is the auto-dismiss; the wiped
peerHashis why the re-identify shows "Unknown"; the churn is why you can't answer. The devicediag.logonly showed the effect (providerDidReset) — the reason (thecallservicesddeny) was only visible in the simulator'scom.apple.calls.callkitunified log.Fix
Info.plist: addvoip(+audio, standard for a calling app) toUIBackgroundModes; addNSMicrophoneUsageDescription(the "no audio" half — mic capture needs it).voipis just an Info.plist key — no provisioning/entitlement capability required.Tests/interop/voice_caller.py: a headless LXST caller (Sideband'sReticulumTelephone→LXST.Telephone, wire-identical to a Sideband user calling) that joins the shared lxmd instance and dials the sim's telephony dest, reproducing the incoming-call/CallKit path on the simulator — where CallKit runs, so the framework deny reason is observable.Verified
reportIncomingCallsucceeds andcallservicesdlogs "Created CXXPCCallSource … Asked to add call source" — no deny, noproviderDidReset, no teardown.