Skip to content

Bugfix/1688/fix call background death - #6334

Draft
mahibi wants to merge 26 commits into
masterfrom
bugfix/1688/fixCallBackgroundDeath
Draft

Bugfix/1688/fix call background death#6334
mahibi wants to merge 26 commits into
masterfrom
bugfix/1688/fixCallBackgroundDeath

Conversation

@mahibi

@mahibi mahibi commented Jun 11, 2026

Copy link
Copy Markdown
Collaborator

🏁 Checklist

  • ⛑️ Tests (unit and/or integration) are included or not needed
  • 🔖 Capability is checked or not needed
  • 🔙 Backport requests are created or not needed: /backport to stable-xx.x
  • 📅 Milestone is set
  • 🌸 PR title is meaningful (if it should be in the changelog: is it meaningful to users?)

🤖 AI (if applicable)

  • The content of this PR was partly or fully generated using AI

@github-actions

Copy link
Copy Markdown
Contributor

APK file: https://github.com/nextcloud/talk-android/actions/runs/27347821297/artifacts/7570066996
To test this change/fix you can simply download above APK file and install and test it in parallel to your existing Nextcloud app.
qrcode (please click on link to get QR code displayed)

@backfield79

Copy link
Copy Markdown

APK file: https://github.com/nextcloud/talk-android/actions/runs/27347821297/artifacts/7570066996
To test this change/fix you can simply download above APK file and install and test it in parallel to your existing Nextcloud app.
qrcode (please click on link to get QR code displayed)

Hello!

Tried to download APK to test, but there is no page behind the link: "Didn’t find anything here!"

@mahibi

mahibi commented Jun 17, 2026

Copy link
Copy Markdown
Collaborator Author

Hello!

Tried to download APK to test, but there is no page behind the link: "Didn’t find anything here!"

Thanks for the hint, will have a look at the CI process and check whats going wrong

@mahibi
mahibi force-pushed the bugfix/1688/fixCallBackgroundDeath branch from 1fc30a4 to 9e5e28b Compare June 23, 2026 18:02
@github-actions

Copy link
Copy Markdown
Contributor

APK file: https://github.com/nextcloud/talk-android/actions/runs/28046415142/artifacts/7829411523
To test this change/fix you can simply download above APK file and install and test it in parallel to your existing Nextcloud app.
qrcode (please click on link to get QR code displayed)

@mahibi
mahibi force-pushed the bugfix/1688/fixCallBackgroundDeath branch from 9e5e28b to 243836d Compare August 7, 2026 10:22
@mahibi

mahibi commented Aug 7, 2026

Copy link
Copy Markdown
Collaborator Author

I rebased on master and fixed merge conflicts.
The PR is not forgotten but giving it a quick test i immediately ended up with a disturbing bug: Starting a call ended up in two app instances. I heared the ring (coming from the other instance, which started the calll) but the instance that i came from did noting after clicking the call icon.
If this branch gets picked up: Be careful and test/fix! Do not yolo merge!

@mahibi
mahibi marked this pull request as draft August 7, 2026 10:27
@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

APK file: https://github.com/nextcloud/talk-android/actions/runs/31169790300/artifacts/8991699234
To test this change/fix you can simply download above APK file and install and test it in parallel to your existing Nextcloud app.
qrcode (please click on link to get QR code displayed)

Tarek Loubani added 15 commits August 14, 2026 19:31
…round, with notification controls for managing the call

- Add CallForegroundService with persistent notification
- Support calls in background without requiring picture-in-picture mode
- Add "Return to call" and "End call" action buttons to CallForegroundService notification with corresponding PendingIntent
- Handle proper foreground service types for microphone/camera permissions
- Add notification permission and fallback messaging.
- Add EndCallReceiver to handle end call broadcasts from notification action
- Use existing ic_baseline_close_24 drawable for end call action icon
- Register broadcast receiver in CallActivity to handle end call requests from notification using ReceiverFlag.NotExported for Android 14+ compatibility
- Add proper cleanup flow: notification action → EndCallReceiver → CallActivity → proper hangup sequence
- Track intentional call leaving to prevent unwanted service restarts
- Release proximity sensor lock properly during notification-triggered hangup
- Add diagnostic logging throughout the end call flow for debugging

The implementation follows Android best practices:
- Uses NotExported receiver flag for internal app-only broadcasts
- Properly unregisters receivers in onDestroy to prevent leaks
- Uses immutable PendingIntents for security
- Maintains proper state management during call termination

Signed-off-by: Tarek Loubani <tarek@tarek.org>
…tyle

Signed-off-by: Tarek Loubani <tarek@tarek.org>
Signed-off-by: Tarek Loubani <tarek@tarek.org>
Signed-off-by: Tarek Loubani <tarek@tarek.org>
Signed-off-by: Tarek Loubani <tarek@tarek.org>
Signed-off-by: Tarek Loubani <tarek@tarek.org>
Signed-off-by: Tarek Loubani <tarek@tarek.org>
Signed-off-by: Tarek Loubani <tarek@tarek.org>
…do a rapid gesture switch back.

For the quick-switch gesture and the recents-to-chat path, the OS starts animating the transition before onPause() fires. By the time onPause()
runs, the window has already been moved off-screen by the gesture animation, so enterPictureInPictureMode() silently fails — Android requires the
window to still be visible.

Why onTopResumedActivityChanged(false) fixes it:
This callback fires when any other activity (including ChatActivity in the same app) takes the "top resumed" slot. Critically, it fires before
onPause() and before any transition animation begins — the window is still fully on-screen. enterPictureInPictureMode() succeeds at this point.

Why back-button worked but this didn't:
Back button goes through OnBackPressedCallback.handleOnBackPressed() synchronously, which calls enterPipMode() before any transition, not in a
lifecycle callback. onTopResumedActivityChanged puts the task-switch path on the same footing.

API compatibility: On API 26–28, onTopResumedActivityChanged is never called by the system (it didn't exist in Activity before API 29), so
onPause() remains the fallback. Older devices primarily use button navigation and won't have the gesture quick-switch anyway.

Signed-off-by: Tarek Loubani <tarek@tarek.org>
Use observeForever for leaveRoom observer so cleanup runs even when
activity is paused. Move ApplicationWideCurrentRoomHolder.clear() into
the leave success callback to avoid premature state clearing. Guard
against double leaveRoom calls with isLeavingRoom flag.

Signed-off-by: Tarek Loubani <tarek@tarek.org>
…c logging

- Prevent spurious roomJoined events from re-running performCall() when
  already IN_CONVERSATION, fixing call reconnection when ChatActivity
  resumes behind PIP or task switch
- Remove setAutoEnterEnabled(true) which conflicts with manual
  enterPictureInPictureMode() calls causing invisible PIP windows
- Set aspect ratio in initial PIP params (onCreate) so PIP params are
  always valid
- Add isInPipMode guard to onUserLeaveHint to prevent redundant PIP
  entry attempts
- Add diagnostic logging to CallBaseActivity lifecycle methods and
  CallActivity PIP/call state transitions
- Add unit tests documenting PIP race conditions and leaveRoom
  lifecycle behavior

Signed-off-by: Tarek Loubani <tarek@tarek.org>
…tivityChanged fallback

The previous approach had three competing PIP entry mechanisms on API 31+
(auto-enter, onTopResumedActivityChanged, onPause) that raced against each
other, and onTopResumedActivityChanged toggled setAutoEnterEnabled off/on
which broke smooth transitions.

New layered approach per the Android PIP documentation:
- API 31+: setAutoEnterEnabled(true) as primary for home/recents gestures
- API 29+: onTopResumedActivityChanged as fallback (fires while window is
  still visible, catches quick-switch gestures auto-enter misses)
- API 26-30: onUserLeaveHint for home/recents, onPause fallback for 26-28
- All APIs: OnBackPressedCallback for back gesture (only manual entry point)

Key fix: onTopResumedActivityChanged no longer disables auto-enter. It checks
isInPictureInPictureMode() so if auto-enter already handled it, the manual
call is skipped. No races, no toggling.

Also removes shouldFinishOnStop, pipFallbackHandler, topResumedLostTime, and
onCreateTime which were artifacts of the old racing approach.

Signed-off-by: Tarek Loubani <tarek@tarek.org>
Remove excludeFromRecents=true from CallActivity manifest entry. This
attribute caused Android to destroy the entire call task ~5s after the
user navigated away via task switch, killing the call.

Guard all teardown in onDestroy (signaling listeners, localStream,
foreground service, proximity sensor, broadcast receiver) so that
system-initiated destruction during task switching doesn't tear down
active call resources. The foreground service keeps the process alive.

Simplify onTopResumedActivityChanged to only enter PIP on API 29-30.
On API 31+, auto-enter handles swipe-up; onUserLeaveHint moves the task
to back as a safety net for task switching.

Signed-off-by: Tarek Loubani <tarek@tarek.org>
Use Android CallStyle notification (API 31+) to show green status bar
chip with call duration timer, matching the native phone app experience.
Falls back to standard notification on older API levels.

The notification is updated every second via startForeground() to keep
the call duration accurate, using callStartTime from
ApplicationWideCurrentRoomHolder.

Signed-off-by: Tarek Loubani <tarek@tarek.org>
…uent calls

Start foreground service at the beginning of prepareCall() before heavy
initialization, stop it in hangup() and unconditionally in onDestroy(),
cancel stale periodic handlers in onStartCommand(), and reset
callStartTime between calls to prevent state leakage.

Signed-off-by: Tarek Loubani <tarek@tarek.org>
Tarek Loubani and others added 9 commits August 14, 2026 19:31
…space, generic catch)

- Break long log lines to respect 120 char limit
- Remove unused imports (LiveData, assertFalse)
- Remove trailing whitespace
- Merge duplicate test to reduce class function count below threshold
- Catch IllegalArgumentException instead of generic Exception
- Ensure EndCallReceiver.kt ends with newline

Signed-off-by: Tarek Loubani <tarek@tarek.org>
Signed-off-by: Tarek Loubani <tarek@tarek.org>
Signed-off-by: Tarek Loubani <tarek@tarek.org>
Signed-off-by: Tarek Loubani <tarek@tarek.org>
Signed-off-by: Tarek Loubani <tarek@tarek.org>
…ervice

Signed-off-by: Tarek Loubani <tarek@tarek.org>
…ervice

Signed-off-by: Tarek Loubani <tarek@tarek.org>
Signed-off-by: Tarek Loubani <tarek@tarek.org>
invoking funToCallWhenLeaveSuccess went to viewModel, now named as functionToCallAfterLeave

Signed-off-by: Marcel Hibbe <dev@mhibbe.de>
@mahibi
mahibi force-pushed the bugfix/1688/fixCallBackgroundDeath branch from 243836d to 8e8bdfe Compare August 14, 2026 17:31
@github-actions

Copy link
Copy Markdown
Contributor

APK file: https://github.com/nextcloud/talk-android/actions/runs/31824423710/artifacts/9228523528
To test this change/fix you can simply download above APK file and install and test it in parallel to your existing Nextcloud app.
qrcode (please click on link to get QR code displayed)

…l restart

Restore excludeFromRecents on CallActivity so the call task no longer
appears as a second app instance in recents. Handle onNewIntent so a
reused backgrounded CallActivity brings the current call to front or
hands off to a fresh instance for a different room. Always clean up
WebRTC state on destroy, restore the voice-room navigation branch, and
fix misleading permission log wording.

Assisted-by: OpenCode:Kimi-K3
Signed-off-by: Tarek Loubani <tarek@tarek.org>
@tareko

tareko commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

@mahibi I picked this up and addressed the bug you found plus the remaining review points with the commit I just made. As with all my other work, this is heavily AI assisted, though this time with Kimi K3.

The "two app instances" bug had three root causes:

  1. excludeFromRecents had been removed from CallActivity, so the call task (which is singleTask with its own taskAffinity) showed up as a second Talk instance in recents. Restored it — the way back to a backgrounded call is the persistent notification from CallForegroundService, which is exactly what this PR adds.
  2. CallActivity never overrode onNewIntent. That was harmless before because the activity was always finished when you left it, but now that moveTaskToBack() keeps it alive, tapping the call icon again routed the intent to the stale backgrounded instance and dropped it — that's why the instance you were looking at "did nothing" while the other one kept ringing. onNewIntent now handles this: same room → bring the existing call to the front; different room → hang up and hand off to a fresh instance so no stale state is reused.
  3. onDestroy kept the mic stream, signaling listeners and receivers alive on "system-initiated destroy" while simultaneously stopping the foreground service — a half-dead ghost call. Since WebRTC state can't survive the activity being destroyed anyway, onDestroy now always cleans up and hangs up. Background survival still works because moveTaskToBack/PiP don't destroy the activity.
    Also fixed while in there: the orphaned voice-room navigation branch in hangupNetworkCalls, a dead else that would have called prepareCall() twice, the notification's "Return to call" losing its extras during timer updates, the misleading "permission was denied" logs from your earlier review (camera/bluetooth/notification this time), and a stuck isLeavingRoom flag in ChatActivity if a leave-room request failed.

Builds clean, detekt/ktlintCheck pass, and the existing CallBaseActivityPipTest/ChatActivityLeaveRoomLifecycleTest pass. I tested on-device with the QA build.

I'm happy to adjust anything, and hopefully this helps get us closer to the finish line.

onUserLeaveHint also fires when a transient overlay such as the runtime
permission dialog appears at call start. Calling moveTaskToBack there
threw the just-started call to the background while it kept ringing,
leaving the user back in chat with no visible call. Backgrounding via
home or task switch never destroys the activity, so this was
unnecessary; the back button case remains handled by the
OnBackPressedCallback.

Assisted-by: OpenCode:Kimi-K3
Signed-off-by: Tarek Loubani <tarek@tarek.org>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

continue call when minimizing app and PIP is not available/enabled

3 participants