Skip to content

Comments

Improve incoming call flow for missing camera/audio permissions#1612

Merged
aleksandar-apostolov merged 2 commits intodevelopfrom
bugfix/rahullohra/fix-no-permissions-incoming-call
Feb 11, 2026
Merged

Improve incoming call flow for missing camera/audio permissions#1612
aleksandar-apostolov merged 2 commits intodevelopfrom
bugfix/rahullohra/fix-no-permissions-incoming-call

Conversation

@rahul-lohra
Copy link
Contributor

@rahul-lohra rahul-lohra commented Feb 10, 2026

Goal

Improve incoming call flow for missing camera/audio permissions

  1. Improved permission handling for incoming calls by validating camera and audio permissions across the PN accept flow and service lifecycle.
  2. Improved notification UX to correctly reflect foreground behavior when the user enters the call screen from the incoming-call notification.

Implementation

Improved permission handling

  1. Made the service foreground early before service teardown when permissions are missing.
  2. Skip Audio/Video Permission checking when service is started with TRIGGER_INCOMING_CALL. As we don't need camera/audio just to render Incoming Calling Screen
  3. Add permission check in destination activity when Notification's call accept button is fired
  4. Ensure CallState.owncapabilities is updated before joining an incoming call flow which came from Push notification

Improved notification UX

  1. Update the notification to right after accept button is tapped to respect NotificationConfig.hideRingingNotificationInForeground

🎨 UI Changes

None.

Testing

Flow 1: Tap on incoming Call Notification

  1. Revoke permission camera & microphone premission and set to ask everytime
  2. Get an incoming call
  3. Start testing by tapping on Notification Button
  4. Incoming Call Screen should open up and it should ask for permission and notification should respect hideRingingNotificationInForeground

Flow 2: Tap on incoming Call's Notification's Accept Button

  1. Revoke permission camera & microphone premission and set to ask everytime
  2. Get an incoming call
  3. Start testing by tapping on Notification Button
  4. Incoming Call Screen should open up and it should ask for permission and notification should respect hideRingingNotificationInForeground

Summary by CodeRabbit

  • New Features

    • Added permission validation before accepting incoming calls
    • Implemented intelligent foreground service management based on call state
  • Improvements

    • Enhanced error handling for call initialization with detailed error callbacks
    • Improved permission checks for audio and video capabilities
    • Better notification handling and lifecycle logging

@rahul-lohra rahul-lohra self-assigned this Feb 10, 2026
@rahul-lohra rahul-lohra requested a review from a team as a code owner February 10, 2026 08:11
@rahul-lohra rahul-lohra added the pr:bug Fixes a bug label Feb 10, 2026
@github-actions
Copy link
Contributor

github-actions bot commented Feb 10, 2026

PR checklist ✅

All required conditions are satisfied:

  • Title length is OK (or ignored by label).
  • At least one pr: label exists.
  • Sections ### Goal, ### Implementation, and ### Testing are filled.

🎉 Great job! This PR is ready for review.

@coderabbitai
Copy link

coderabbitai bot commented Feb 10, 2026

Walkthrough

The changes enhance the notification and permission handling system for incoming calls. They introduce permission-aware call acceptance, add notification ID retrieval utilities, improve error handling in call initialization, and implement permission validation across the call service layer.

Changes

Cohort / File(s) Summary
Notification ID Management
CallState.kt, ServiceNotificationRetriever.kt
Exposed notificationIdFlow as public API with @InternalStreamVideoApi annotation. Added getNotificationId utility function to compute notification IDs based on trigger type and call state.
Call Service Enhancements
CallService.kt
Added notification ID retrieval via ServiceNotificationRetriever, introduced promoteToFgServiceIfNoActiveCall helper to manage foreground service promotion, implemented conditional permission verification (skipped for incoming calls), and enhanced logging for service lifecycle events.
Error Handling
CallServiceLifecycleManager.kt
Updated initializeCallAndSocket to pass typed Error? to error callback instead of void callback, enabling detailed error propagation through call initialization chain.
UI Call Acceptance
StreamCallActivity.kt
Replaced direct call acceptance with permission-aware handleAcceptCallIntent helper. Added fallback to update foreground notification when required permissions are missing but notification permission is granted. Integrated permission checks via PermissionManager.
Permission Validation
PermissionManager.kt
Introduced hasRequiredCallPermissions to validate call-specific permissions (RECORD_AUDIO for audio, CAMERA for video) based on call capabilities, and hasNotificationPermission to check POST_NOTIFICATIONS permission. Includes defensive capability fetching and logging.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant CallActivity as StreamCallActivity
    participant PermMgr as PermissionManager
    participant CallService
    participant NotifRetriever as ServiceNotificationRetriever
    participant Lifecycle as CallServiceLifecycleManager

    User->>CallActivity: Accept incoming call
    CallActivity->>PermMgr: hasRequiredCallPermissions(call)?
    PermMgr->>PermMgr: Check RECORD_AUDIO/CAMERA
    PermMgr-->>CallActivity: Return permission status
    
    alt Permissions Granted
        CallActivity->>CallService: Accept call
        CallService->>NotifRetriever: getNotificationId(trigger)
        NotifRetriever-->>CallService: Return notification ID
        CallService->>CallService: promoteToFgServiceIfNoActiveCall
        CallService->>Lifecycle: initializeCallAndSocket(onError)
        Lifecycle-->>CallService: Error or success callback
    else Permissions Denied
        CallActivity->>PermMgr: hasNotificationPermission()?
        PermMgr-->>CallActivity: Return permission status
        CallActivity->>CallActivity: updateNotificationWhenAppComesToForeground
        CallActivity->>CallService: Fetch call state (GET)
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Poem

🐰 A little hare hopped through the code,
With permissions checked along the road,
Notifications bloom, foreground services dance,
Error handling woven through each glance,
Call acceptance now asks "may I please?" before its advance! 📱✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 36.84% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main objective of the changeset: improving incoming call flow handling when camera and audio permissions are missing.
Description check ✅ Passed The PR description covers the goal and implementation clearly, with specific testing flows provided, but lacks changelog updates, contributor/reviewer checklists, and UI comparison sections from the template.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch bugfix/rahullohra/fix-no-permissions-incoming-call

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
stream-video-android-ui-core/src/main/kotlin/io/getstream/video/android/ui/common/permission/PermissionManager.kt (1)

19-33: ⚠️ Potential issue | 🟡 Minor

Duplicate license header.

The file contains two license headers (lines 1–15 and lines 19–33). Remove the second one.

🤖 Fix all issues with AI agents
In
`@stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/notifications/internal/service/CallService.kt`:
- Around line 655-677: promoteToFgServiceIfNoActiveCall currently skips
startForeground when videoClient.getSettingUpCallNotification() returns null
(NoOpNotificationHandler), which can leave a service started via
ContextCompat.startForegroundService() without a foreground call and cause
ForegroundServiceDidNotStartInTimeException; update
promoteToFgServiceIfNoActiveCall to always call startForegroundWithServiceType
(or startForeground) when !hasActiveCall by providing a fallback notification
(build a minimal/default Notification) if getSettingUpCallNotification() returns
null, so the service is promoted to foreground even when the custom handler
returns null; keep references to videoClient.getSettingUpCallNotification(),
startForegroundWithServiceType(...,
permissionManager.getServiceType(baseContext, trigger)), and ensure this change
preserves the normal path to startForegroundForCall() and safe
stopServiceGracefully() behavior.

In
`@stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/notifications/internal/service/managers/CallServiceLifecycleManager.kt`:
- Around line 52-56: The error message concatenates a nullable from
update.errorOrNull(), producing e.g. "Fail to perform
initializeCallAndSocketnull" and lacking a separator; update the onError
invocation in CallServiceLifecycleManager (the initializeCallAndSocket error
path) to safely extract the error text and include a separator — e.g., use a
safe call and fallback (error?.message ?: error.toString() ?: "unknown") or a
default string, and prepend with a separator like ": " or " - " so the logged
message becomes "Fail to perform initializeCallAndSocket: <error details>"
instead of concatenating a nullable directly.

In
`@stream-video-android-ui-core/src/main/kotlin/io/getstream/video/android/ui/common/permission/PermissionManager.kt`:
- Around line 100-129: In hasRequiredCallPermissions, fix the typo in the
TODO/log comment ("exiting" → "existing") and change the failure handling for
call.get(): when result.isFailure, log the error at error level including the
failure details (use logger.e with the exception or result.error) instead of
logger.w, and return false (do not bypass permission checks) so callers will
enforce permissions; keep the rest of the capability checks unchanged and
reference call.get(), call.state.ownCapabilities, and logger for locating the
change.

In
`@stream-video-android-ui-core/src/main/kotlin/io/getstream/video/android/ui/common/StreamCallActivity.kt`:
- Around line 571-587: The current logic in handleAcceptCallIntent silently does
nothing when hasRequiredCallPermissions(...) is false and
hasNotificationPermission(...) is also false; change it so that when call
permissions are missing you always invoke get(call, onError=..., onSuccess=...)
to surface the incoming-call UI for permission prompting, and only call
updateNotificationWhenAppComesToForeground(call) beforehand when
PermissionManager.hasNotificationPermission(...) is true; keep the existing
accept(call, ...) path when permissions are present and preserve
onError/onSuccess propagation for both accept and get.
🧹 Nitpick comments (5)
stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/notifications/internal/service/managers/CallServiceLifecycleManager.kt (1)

32-41: Leftover TODO with developer name.

This TODO references a specific developer (Rahul) and contains speculative notes. Consider converting to a proper actionable TODO or removing before merge.

stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/notifications/internal/service/ServiceNotificationRetriever.kt (1)

135-159: Notification ID resolution logic is duplicated with getNotificationPair.

The when block here mirrors the ID derivation in getNotificationPair (lines 70–126). Consider extracting getNotificationId first and then calling it from getNotificationPair to keep a single source of truth for ID resolution.

Sketch
 // In getNotificationPair, replace inline ID resolution with:
 TRIGGER_ONGOING_CALL -> {
     logger.d { "[getNotificationPair] Creating ongoing call notification" }
-    val notificationId = call.state.notificationIdFlow.value
-        ?: streamCallId.getNotificationId(NotificationType.Ongoing)
+    val notificationId = getNotificationId(trigger, streamVideo, streamCallId)
     Pair(
         first = streamVideo.getOngoingCallNotification(...),
         second = notificationId,
     )
 }
 // ... and similarly for the other branches
stream-video-android-ui-core/src/main/kotlin/io/getstream/video/android/ui/common/StreamCallActivity.kt (1)

589-600: @SuppressLint("MissingPermission") hides a runtime risk.

This method suppresses the lint check for POST_NOTIFICATIONS, but the only call-site protection is an if branch in handleAcceptCallIntent. If this method is ever called from another location without the guard, notify() will silently fail on API 33+. Consider adding a runtime guard inside this method or documenting the precondition clearly.

stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/notifications/internal/service/CallService.kt (2)

237-237: Destructured pair discards the notification ID — intentional and correct.

The notification ID is now retrieved earlier (line 210) via getNotificationId, so ignoring the second element of the pair is fine. However, this redundancy reinforces the suggestion to refactor getNotificationPair in ServiceNotificationRetriever to avoid computing the ID twice.


308-308: Leftover TODO — track or resolve before merge.

This TODO raises a concern about livestream guest/host getting stuck on a null notification. Consider filing an issue to track this if it's not being addressed in this PR.

Would you like me to open an issue to track this?

@github-actions
Copy link
Contributor

github-actions bot commented Feb 10, 2026

SDK Size Comparison 📏

SDK Before After Difference Status
stream-video-android-core 11.96 MB 11.96 MB 0.00 MB 🟢
stream-video-android-ui-xml 5.68 MB 5.68 MB 0.00 MB 🟢
stream-video-android-ui-compose 6.27 MB 6.28 MB 0.02 MB 🟢

@rahul-lohra rahul-lohra force-pushed the bugfix/rahullohra/fix-no-permissions-incoming-call branch from 48903f0 to b79a592 Compare February 10, 2026 09:16
@rahul-lohra rahul-lohra force-pushed the bugfix/rahullohra/fix-no-permissions-incoming-call branch from b79a592 to 98e764e Compare February 10, 2026 09:19
Copy link
Contributor

@aleksandar-apostolov aleksandar-apostolov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Critical fixes verified:

  • Foreground service promoted early before permission check
  • Silent no-op fixed — get() always called when permissions missing
  • Incoming call trigger skips audio/video permission check correctly

Minor: error message string concat could use interpolation, but not blocking.

@sonarqubecloud
Copy link

Quality Gate Failed Quality Gate failed

Failed conditions
4.5% Coverage on New Code (required ≥ 80%)

See analysis details on SonarQube Cloud

@aleksandar-apostolov aleksandar-apostolov merged commit 08eddd5 into develop Feb 11, 2026
11 of 12 checks passed
@aleksandar-apostolov aleksandar-apostolov deleted the bugfix/rahullohra/fix-no-permissions-incoming-call branch February 11, 2026 08:42
@rahul-lohra rahul-lohra changed the title Improve incoming call flow for missing camera/audio permissions [AND-1059] Improve incoming call flow for missing camera/audio permissions Feb 11, 2026
@rahul-lohra rahul-lohra changed the title [AND-1059] Improve incoming call flow for missing camera/audio permissions Improve incoming call flow for missing camera/audio permissions Feb 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pr:bug Fixes a bug

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants