fix(android): start VoipCallService on accept, stop on hangup/timeout, install end-call listener#7200
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
📜 Recent review details⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
WalkthroughVoIP notification handlers now coordinate the foreground service lifecycle: the service is started when a call is accepted and stopped when a call times out or is terminated remotely. A DDP call-end listener is installed on accept to detect remote hangups and trigger service cleanup. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant VoipNotification
participant VoipCallService
participant DDP as DDP/CallBackend
participant Local as LocalBroadcastManager/Notification
User->>VoipNotification: tap ACCEPT
VoipNotification->>DDP: startListeningForCallEnd(callId)
VoipNotification->>VoipCallService: startService(callId)
VoipNotification->>Local: proceed with accept flow
alt remote hangup detected
DDP->>VoipNotification: onCallEnded(callId)
VoipNotification->>VoipCallService: stopService()
VoipNotification->>Local: cancel notification + send ACTION_DISMISS
end
alt timeout occurs
VoipNotification->>VoipCallService: stopService()
VoipNotification->>Local: cancel notification + send ACTION_TIMEOUT
end
Estimated code review effort🎯 2 (Simple) | ⏱️ ~8 minutes 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Warning Review ran into problems🔥 ProblemsErrors were encountered while retrieving linked issues. Errors (6)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Code Review — PR #7200Files Reviewed: [LOW]
|
| Criterion | Status | Notes |
|---|---|---|
| Accepting a call starts the foreground service | ✅ | VoipCallService.startService() at line 247 |
| Hanging up stops the service | ✅ | VoipCallService.stopService() in DDP listener (~line 498) and handleTimeout (line 137) |
| Accepting from notification installs the DDP end-call listener | ✅ | startListeningForCallEnd at line 244 — H3 fix |
| Accepting from incoming UI still works (no regression) | ✅ | All existing paths unchanged |
Positive Observations
- H3 fix is correct.
startListeningForCallEndnow called inhandleAcceptAction, so notification-accept path (heads-up + IncomingCallActivity) gets the DDP listener. - Service lifecycle is clean. Start on accept, stop on hangup/timeout. Self-contained and symmetric.
- Timeout path stops service.
handleTimeoutstops the service, covering the case where the call times out before the user answers. - DDP listener path stops service. When the remote party hangs up or another device accepts,
VoipCallService.stopService(appContext)is called in the same handler block asdisconnectIncomingCall.
Verdict
LGTM — All 4 acceptance criteria satisfied. Both LOW observations are informational only.
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@android/app/src/main/java/chat/rocket/reactnative/voip/VoipCallService.kt`:
- Around line 48-54: The current stopService method calls
context.startService(intent) which can trigger a background service start and
crash on Android O+; change it to call context.stopService(intent) (i.e., call
the framework lifecycle stop API instead of starting the service with
ACTION_STOP) in the static stopService function of VoipCallService so you won't
start the service when backgrounded; after making this change remove the
now-dead ACTION_STOP handling in onStartCommand and delete the ACTION_STOP
constant (also update any callers such as
VoipNotification.startListeningForCallEnd or handleTimeout to use the new
stopService behavior).
- Around line 113-123: Replace the launcher icon usage in VoipCallService's
notification builder (the code that returns NotificationCompat.Builder in
VoipCallService.kt) so it uses the same monochrome notification drawable as
VoipNotification (ic_notification) instead of getApplicationInfo().icon; load it
via ContextCompat.getDrawable/getDrawableResource or
setSmallIcon(R.drawable.ic_notification). Also move the literal "VoIP Call" and
"Call in progress" into string resources and reference them via
getString(R.string.xxx) to enable i18n.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 88bea798-44f7-40cb-b1d8-5060941923ae
📒 Files selected for processing (3)
android/app/src/main/AndroidManifest.xmlandroid/app/src/main/java/chat/rocket/reactnative/voip/VoipCallService.ktandroid/app/src/main/java/chat/rocket/reactnative/voip/VoipNotification.kt
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: ESLint and Test / run-eslint-and-test
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: CR
Repo: RocketChat/Rocket.Chat.ReactNative PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-07T17:49:17.538Z
Learning: Applies to app/lib/services/voip/**/*.{ts,tsx} : Implement VoIP with WebRTC peer-to-peer audio calls in app/lib/services/voip/ using Zustand stores instead of Redux, with native CallKit (iOS) and Telecom (Android) integration; keep VoIP and VideoConf separate
📚 Learning: 2026-04-07T17:49:17.538Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat.ReactNative PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-07T17:49:17.538Z
Learning: Applies to app/lib/services/voip/**/*.{ts,tsx} : Implement VoIP with WebRTC peer-to-peer audio calls in app/lib/services/voip/ using Zustand stores instead of Redux, with native CallKit (iOS) and Telecom (Android) integration; keep VoIP and VideoConf separate
Applied to files:
android/app/src/main/AndroidManifest.xmlandroid/app/src/main/java/chat/rocket/reactnative/voip/VoipNotification.ktandroid/app/src/main/java/chat/rocket/reactnative/voip/VoipCallService.kt
🔇 Additional comments (2)
android/app/src/main/AndroidManifest.xml (1)
150-155: LGTM — service declaration is consistent with the implementation.
foregroundServiceType="microphone"matchesServiceInfo.FOREGROUND_SERVICE_TYPE_MICROPHONEused inVoipCallService.startForegroundWithNotification, and the correspondingFOREGROUND_SERVICE_MICROPHONEruntime permission is already declared at line 17.exported="false"is correct for an internal service.android/app/src/main/java/chat/rocket/reactnative/voip/VoipNotification.kt (1)
243-248: No action required —putClientsafely handles duplicate callIds.The concern about invoking
startListeningForCallEndtwice for the samecallId(once fromshowIncomingCallat line 661, and again here inhandleAcceptAction) is mitigated by theVoipPerCallDdpRegistryimplementation. ItsputClientmethod explicitly disposes the previous client before inserting the new one, as confirmed by the test caseputClient for the same callId releases the previous client. The registry is thread-safe and designed to hold at most oneDDPClientpercallId, so no resource leak occurs. While callingstartListeningForCallEndtwice is redundant work, it is functionally safe and correct.
| @JvmStatic | ||
| fun stopService(context: android.content.Context) { | ||
| val intent = Intent(context, VoipCallService::class.java).apply { | ||
| action = ACTION_STOP | ||
| } | ||
| context.startService(intent) | ||
| } |
There was a problem hiding this comment.
stopService can crash from background on Android 8+.
Delivering ACTION_STOP via context.startService(intent) counts as a background service start on API 26+ and throws IllegalStateException when the app isn't in a foreground state. The hangup/DDP-end paths (VoipNotification.startListeningForCallEnd, handleTimeout) can fire while the app is backgrounded, so this is reachable in practice. It also needlessly starts the service if it isn't running.
Prefer the framework lifecycle call, which is not a background-start:
🛡️ Suggested fix
`@JvmStatic`
fun stopService(context: android.content.Context) {
- val intent = Intent(context, VoipCallService::class.java).apply {
- action = ACTION_STOP
- }
- context.startService(intent)
+ context.stopService(Intent(context, VoipCallService::class.java))
}With this change the ACTION_STOP branch in onStartCommand becomes dead; you can drop it and the ACTION_STOP constant.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@android/app/src/main/java/chat/rocket/reactnative/voip/VoipCallService.kt`
around lines 48 - 54, The current stopService method calls
context.startService(intent) which can trigger a background service start and
crash on Android O+; change it to call context.stopService(intent) (i.e., call
the framework lifecycle stop API instead of starting the service with
ACTION_STOP) in the static stopService function of VoipCallService so you won't
start the service when backgrounded; after making this change remove the
now-dead ACTION_STOP handling in onStartCommand and delete the ACTION_STOP
constant (also update any callers such as
VoipNotification.startListeningForCallEnd or handleTimeout to use the new
stopService behavior).
| return NotificationCompat.Builder(this, CHANNEL_ID) | ||
| .setContentTitle("VoIP Call") | ||
| .setContentText("Call in progress") | ||
| .setSmallIcon(getApplicationInfo().icon) | ||
| .setContentIntent(pendingIntent) | ||
| .setOngoing(true) | ||
| .setOnlyAlertOnce(true) | ||
| .setPriority(NotificationCompat.PRIORITY_LOW) | ||
| .setCategory(NotificationCompat.CATEGORY_SERVICE) | ||
| .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) | ||
| .build() |
There was a problem hiding this comment.
Use a dedicated status-bar icon instead of the launcher icon.
getApplicationInfo().icon resolves to the launcher (often adaptive) icon, which typically renders as a white/opaque square in the status bar on Android 5+. VoipNotification already uses a proper monochrome drawable (ic_notification, see VoipNotification.kt line 824); reuse it here for a consistent, legible foreground-service indicator. Also consider extracting the title/text to string resources for i18n.
💡 Suggested change
- .setContentTitle("VoIP Call")
- .setContentText("Call in progress")
- .setSmallIcon(getApplicationInfo().icon)
+ .setContentTitle(getString(R.string.voip_call_title))
+ .setContentText(getString(R.string.voip_call_in_progress))
+ .setSmallIcon(resources.getIdentifier("ic_notification", "drawable", packageName))🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@android/app/src/main/java/chat/rocket/reactnative/voip/VoipCallService.kt`
around lines 113 - 123, Replace the launcher icon usage in VoipCallService's
notification builder (the code that returns NotificationCompat.Builder in
VoipCallService.kt) so it uses the same monochrome notification drawable as
VoipNotification (ic_notification) instead of getApplicationInfo().icon; load it
via ContextCompat.getDrawable/getDrawableResource or
setSmallIcon(R.drawable.ic_notification). Also move the literal "VoIP Call" and
"Call in progress" into string resources and reference them via
getString(R.string.xxx) to enable i18n.
Code Review - PR #7200Files reviewed: 3 (VoipCallService.kt new, VoipNotification.kt diff, AndroidManifest.xml diff) Stage 1 - Spec ComplianceAC-1: Accepting a call starts the foreground service
AC-3: Accepting from notification installs DDP end-call listener AC-4: Accepting from incoming UI still works (no regression) AC-2: Hanging up stops the service — PARTIAL FAIL Paths with
Paths without
Stage 2 - Code QualityVoipCallService.kt
VoipNotification.kt diff
AndroidManifest.xml
CRITICAL Issues[CRITICAL] [CRITICAL] JS-layer hangup ( Optimity CheckThe placement of The service start at line 245 is similarly correctly placed — it fires before the potentially-slow REST call, so the foreground service is live as soon as possible. However, the service stop is not on all hangup paths (see CRITICAL issues above). The asymmetry means the service lifecycle does not fully mirror the call lifecycle. VerdictREQUEST CHANGES Two CRITICAL issues where the foreground service can be orphaned:
Both are one-line additions and do not affect any existing behavior. |
Code Review — PR #7200Files Reviewed: 3 By Severity
Issues[CRITICAL]
Since // CURRENT (VoipNotification.kt:150)
fun handleDeclineAction(context: Context, payload: VoipPayload) {
cancelTimeout(payload.callId)
ddpRegistry.stopClient(payload.callId)
// ... REST reject, rejectIncomingCall, cancelById ...
// MISSING: VoipCallService.stopService(context)
}Fix: Add [HIGH] DDP listener
The PR added // CURRENT (VoipNotification.kt:498-511)
Handler(Looper.getMainLooper()).post {
if (!isLiveClient(callId, client)) return@post
cancelTimeout(callId)
disconnectIncomingCall(callId, false)
cancelById(appContext, payload.notificationId)
LocalBroadcastManager.getInstance(appContext).sendBroadcast(...)
ddpRegistry.stopClient(callId)
// MISSING: VoipCallService.stopService(appContext)
}Fix: Add [MEDIUM] PR description claims JS layer stops the service on Android — this is incorrect The PR body states:
Checking // NativeVoip.ts:34-36
stopNativeDDPClient(): void;
// ...
// NativeVoip.ts spec comment: "Android: No-op."// VoipModule.kt:144-147
override fun stopNativeDDPClient() {
Log.d(TAG, "stopNativeDDPClient called, stopping native DDP client")
VoipNotification.stopDDPClient()
}
Positive Observations
Summary
RecommendationREQUEST CHANGES The two service-orphan paths ( Suggested minimal diff for // In handleDeclineAction (line ~163, before rejectIncomingCall):
VoipCallService.stopService(context)
// In DDP handler post block (line ~510, after ddpRegistry.stopClient):
VoipCallService.stopService(appContext)The PR otherwise implements the spec correctly. The |
…, install end-call listener **H3 fix:** Call `startListeningForCallEnd` in `handleAcceptAction` before REST accept. Previously only `showIncomingCall` called it, so the notification-accept path (incoming call via heads-up notification) missed DDP end-call detection. **Foreground service lifecycle:** - `VoipCallService.startService()` called in `handleAcceptAction` on call accept - `VoipCallService.stopService()` called: - On DDP hangup/other-device-accept detection (DDP listener, line ~498) - On incoming call timeout (`handleTimeout`) - The JS layer (`MediaCallEvents.endCall`) also stops the service via native bridge **Service also included (PR-3 dependency):** `VoipCallService.kt` and manifest entry. Fixes: H3 (startListeningForCallEnd not called on notification-accept)
bb780ef to
8fb15d7
Compare
Retraction: two earlier self-review findings were over-statedAfter re-verifying against the post-rebase code ( 1. The service is only started in 2. DDP listener handler missing The fix is present at Still valid: the MEDIUM finding that the PR description over-stated what |
The "H3 fix" label referenced a private audit/plan finding. Rewrote the comment to describe the actual behavior without the identifier, so the code reads self-contained months from now.
…/Decline (#7215) * merge feat.voip-lib * feat(voip): enhance call handling with UUID mapping and event listeners * Base call UI * feat(voip): integrate Zustand for call state management and enhance CallView UI * feat(voip): add simulateCall function for mock call handling in UI development * refactor(CallView): update button handlers and improve UI responsiveness * Add pause-shape-unfilled icon * Base CallHeader * toggleFocus * collapse buttons * Header components * Hide header when no call * Timer * Add use memo * Add voice call item on sidebar * cleanup * Temp use @rocket.chat/media-signaling from .tgz * cleanup * Check module and permissions to enable voip * Refactor stop method to use optional chaining for media signal listeners * voip push first test * Add VoIP call handling with pending call management - Implemented VoIP push notification handling in index.js, including storing call info for later processing. - Added CallKeep event handlers for answering and ending calls from a cold start. - Introduced a new CallIdUUID module to convert call IDs to deterministic UUIDs for compatibility with CallKit. - Created a pending call store to manage incoming calls when the app is not fully initialized. - Updated deep linking actions to include VoIP call handling. - Enhanced MediaSessionInstance to process pending calls and manage call states effectively. * Remove pending store and create getInitialEvents on app/index * Attempt to make iOS calls work from cold state * lint and format * Patch callkeep ios * Temp send iOS voip push token on gcm * Temp fix require cycle * chore: format code and fix lint issues [skip ci] * CallIDUUID module on android and voip push * Add setCallUUID on useCallStore to persist calls accepted on native Android * remove callkeep from notification * Android Incoming Call UI POC * Refactor VoIP handling: Migrate VoIP-related classes to a new package structure, removing deprecated modules and consolidating functionality. Update imports in MainApplication and NotificationIntentHandler to reflect changes. This cleanup enhances code organization and prepares for future VoIP feature enhancements. * Remove VoipForegroundService * cleanup and use caller instead of callerName * Cleanup and make iOS build again * Refactor VoIP handling: Remove unused event emissions for call answered and declined, switch from SharedPreferences to in-memory storage for pending VoIP call data, and update method signatures for better clarity. This cleanup enhances performance and prepares for future VoIP feature improvements. * Refactor VoIP handling: Introduce a new VoipPayload class to encapsulate call data, streamline notification processing, and enhance method signatures across the VoIP module. This update improves code clarity and prepares for future feature enhancements. * Migrate react-native-voip-push-notifications to VoipModule * Refactor VoIP module: Update package structure by moving VoipTurboPackage to the main package and removing the obsolete NativeVoipSpec class. Adjust imports in MainApplication and VoipModule to reflect these changes, enhancing code organization and maintainability. * Unify emitters * Move CallKeep listeners from MediaSessionInstance to getInitialEvents * Clear callkeep on endcall * Unify getInitialEvents logic * getInitialEvents -> MediaCallEvents * chore: format code and fix lint issues [skip ci] * feat(Android): Add full screen incoming call (#6977) * feat: Update call UI (#6990) * feat: Handle audio routing, e.g., Bluetooth headset vs. internal speaker switching (#6992) * fix: empty space when not on call (#6993) * feat: Dialpad (#7000) * action: organized translations * feat: start call (#7024) * chore: format code and fix lint issues * feat: Pre flight (#7038) * action: organized translations * feat: Receive voip push notifications from backend (#7045) * feat: Refactor media session handling and improve disconnect logic (#7065) * feat: Control incoming call from native (#7066) * feat: Voice message blocks (#7057) * feat: native accept success event (#7068) * feat(voip): call waiting, busy detection, and videoconf blocking (#7077) * action: organized translations * feat(voip): tap-to-hide call controls with animations (#7078) * feat(voip): navigate to call DM from message button and header (#7082) * feat(voip): tablet and landscape layout (#7110) * chore: develop into feat.voip-lib-new (RN 81 + Expo 54 + reanimated 4 + true-sheet + iOS 26) (#7114) * chore: format code and fix lint issues * feat(voip): android landscape layout for IncomingCallActivity (#7116) * Update agents files * feat(voip): Support a11y (#7106) * Fix content cutting on iOS on some edge cases * pods * Ignore .worktrees on jest * chore: Merge develop into feat.voip-lib-new (#7129) * fix(voip): show CallKit UI when call is active in background (#7128) * chore: Update media-signaling to 0.2.0 (#7153) * feat(voip): migrate iOS accept/reject from DDP to REST (#7124) * Fix icons * feat(voip): migrate Android accept/reject from DDP to REST (#7127) * test(voip): integration tests for CallView pipeline (#7161) * feat(voip): display video conf provider as subtitle (#7160) * fix(voip): CallView button grid and correct landscape/dialpad layouts (#7164) * fix(voip): prevent stale MMKV cache on Android first-install accept MMKVKeyManager.initialize ran in MainApplication.onCreate before the JS engine started and opened the default MMKV file via the Tencent 1.2 JAR when it was still empty. Tencent caches instances per-ID in a singleton registry, so that empty-state view was held for the rest of the process. JS later wrote credentials through react-native-mmkv (MMKV Core 2.0), which has its own separate registry. When a VoIP push arrived, Ejson.getMMKV() got the cached empty Tencent instance and reported "No userId found in MMKV for server". Closing and reopening the app cleared the cache, which is why only the very first call after install failed. Drop the open/verify block — the encryption key is already cached from SecureKeystore, so no MMKV handle is needed here. The first Tencent instance is now created inside Ejson.getMMKV() after JS has written, so it scans the file fresh. * fix(voip): prevent duplicate ringtone on Android incoming call (#7158) * fix(voip): set explicit snaps for NewMediaCall bottom sheet (#7165) * Update app/lib/services/voip/MediaSessionStore.ts Co-authored-by: Pierre Lehnen <55164754+pierre-lehnen-rc@users.noreply.github.com> * fix: make startVoipFork reactive to permissions-changed (#7151) * fix(android): remove MediaProjectionService from merged manifest (#7190) * fix(voip): Phone account creation (#7170) * feat: add Enable Mobile Ringing toggle in user preferences (#7155) * fix(voip): ship blockers for PushKit, licensing, outbound calls, push tokens (#7167) * fix(android): Play Store mic discoverability, safer FCM logs, avatar auth via headers (#7171) * fix(ios): serialize VoipService bridge statics (#7169) * fix(voip): Android DDP thread safety and VoipPayload bundle parity (#7168) * chore(voip): dead-code and hygiene sweep (#7174) * refactor(voip): decouple navigateToCallRoom from Redux and backfill REST/connect tests (#7176) * test(voip): tighten ringing endCall assertion and add VideoConf VoIP-lock saga coverage (#7177) * fix(ios): harden VoIP DDP WebSocket client on receive failures and TLS (#7173) * refactor(voip): MediaCallEvents Redux adapters and resetVoipState (#7178) * refactor(voip): decouple peer autocomplete from Redux; simplify NewMediaCall (#7175) * fix(ios): add NS_SWIFT_NAME to Challenge.runChallenge for Swift 6.2 compatibility Swift 6.2 (Xcode 26.x / macos-26 runner) auto-renames the Objective-C method runChallenge:didReceiveChallenge:completionHandler: to run(_:didReceive:completionHandler:) when imported into Swift. Add NS_SWIFT_NAME to explicitly pin the Swift import name, preventing the compiler from applying its heuristics. This keeps the existing Swift call site in DDPClient.swift working without changes. * fix(ios): cancel old URLSession/webSocketTask before reconnecting in DDPClient.connect (#7197) * fix(ios): add NSLock to nativeAcceptHandledCallIds and 10s REST timeout to handleNativeAccept (#7198) * feat(android): create VoipCallService with FOREGROUND_SERVICE_MICROPHONE (#7199) * fix(android): start VoipCallService on accept, stop on hangup/timeout, install end-call listener (#7200) * fix(voip): enable DM nav for users with SIP extension (#7203) * fix(android): handle null VoiceConnection in answerIncomingCall, notify JS (#7201) * fix(voip): resolve closure capture ordering in handleNativeAccept (#7209) * fix(android): integrate VoIP modules with SSL-pinned OkHttpClient (#7208) * fix(push): gate id and voipToken behind server version checks, fix VideoConf caller extra (#7210) * fix(voip): remove sensitive data from production logs (#7207) * fix(android): remove isRunning guard + add double-tap guard on Accept/Decline - VoipCallService: remove if (!isRunning) guard, call startForeground unconditionally (idempotent on Android, fixes Android 14+ foreground service requirement) - IncomingCallActivity: add AtomicBoolean guard on handleAccept/handleDecline to prevent double-tap from triggering multiple service starts --------- Co-authored-by: diegolmello <diegolmello@users.noreply.github.com> Co-authored-by: Pierre Lehnen <55164754+pierre-lehnen-rc@users.noreply.github.com>
Summary
Notification-accept DDP listener: Call
startListeningForCallEndinhandleAcceptActionbefore REST accept. Previously onlyshowIncomingCallcalled it, so the notification-accept path (accept via heads-up notification or IncomingCallActivity) missed DDP end-call detection.Foreground service lifecycle:
VoipCallService.startService()called inhandleAcceptActionon call acceptVoipCallService.stopService()called on:handleTimeout)Note:
VoipCallService.ktandAndroidManifest.xmlservice declaration landed in PR-3 (#7199) and are no longer part of this PR's diff.Changes
VoipNotification.kt: AddedstartListeningForCallEnd(context, payload)call inhandleAcceptActionVoipNotification.kt: AddedVoipCallService.stopService(context)in DDP hangup handler andhandleTimeoutVoipNotification.kt: AddedVoipCallService.startService(context, payload.callId)inhandleAcceptActionTypes of changes
Checklist
Merge Order
This is PR 4 of 6. DEPENDS ON PR-3 (already merged).
PR-2: fix(ios) DDP cleanup — independent← mergedPR-1: fix(ios) NSLock + timeout — independent← mergedPR-3: feat(android) VoipCallService — independent← mergedAcceptance Criteria
VoipCallService.startService()inhandleAcceptActionVoipCallService.stopService()in DDP listener +handleTimeoutstartListeningForCallEndcalled inhandleAcceptActionSummary by CodeRabbit