From 04ad427b94871ed28eac6457374838d69a59a7db Mon Sep 17 00:00:00 2001 From: rahullohra Date: Wed, 22 Jul 2026 13:29:59 +0530 Subject: [PATCH 1/3] Chore: make incoming call android 17 compatible --- .../video/android/core/StreamVideoClient.kt | 2 +- .../ForegroundServicePermissionManager.kt | 19 ++++++++++ .../ForegroundServicePermissionManagerTest.kt | 35 +++++++++++++++++++ 3 files changed, 55 insertions(+), 1 deletion(-) diff --git a/stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/StreamVideoClient.kt b/stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/StreamVideoClient.kt index ee5e89befb..736e04d580 100644 --- a/stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/StreamVideoClient.kt +++ b/stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/StreamVideoClient.kt @@ -778,7 +778,7 @@ internal class StreamVideoClient internal constructor( logger.d { "[getOrCreateCall] type: $type, id: $id, members: $members" } return apiCall { - coordinatorConnectionModule.api.getOrCreateCall( + coordinatorConnectionModule.api.getOrCreateCall( //noob type = type, id = id, getOrCreateCallRequest = GetOrCreateCallRequest( diff --git a/stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/notifications/internal/service/permissions/ForegroundServicePermissionManager.kt b/stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/notifications/internal/service/permissions/ForegroundServicePermissionManager.kt index 209a5e8ad0..cc693689e2 100644 --- a/stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/notifications/internal/service/permissions/ForegroundServicePermissionManager.kt +++ b/stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/notifications/internal/service/permissions/ForegroundServicePermissionManager.kt @@ -56,6 +56,7 @@ internal open class ForegroundServicePermissionManager { CallService.Companion.TRIGGER_ONGOING_CALL, CallService.Companion.TRIGGER_OUTGOING_CALL, -> calculateServiceType(context) + CallService.Companion.TRIGGER_INCOMING_CALL -> ringingServiceType() else -> noPermissionServiceType() } } @@ -91,6 +92,24 @@ internal open class ForegroundServicePermissionManager { ServiceInfo.FOREGROUND_SERVICE_TYPE_PHONE_CALL } + /** + * Foreground-service type for the incoming/ringing window. + * + * On Android 17+, background audio (the ringtone) is muted under [ServiceInfo.FOREGROUND_SERVICE_TYPE_SHORT_SERVICE], + * so phoneCall-capable VoIP services use phoneCall instead. Other services keep [noPermissionServiceType]. + */ + @SuppressLint("InlinedApi") + internal open fun ringingServiceType(): Int = + // TODO: replace the hardcoded 37 with Build.VERSION_CODES. once compileSdk / + // targetSdk are raised to 37; the constant does not exist at the current compileSdk. + if (requiredForegroundTypes.contains(ServiceInfo.FOREGROUND_SERVICE_TYPE_PHONE_CALL) && + Build.VERSION.SDK_INT >= 37 + ) { + ServiceInfo.FOREGROUND_SERVICE_TYPE_PHONE_CALL + } else { + noPermissionServiceType() + } + @SuppressLint("InlinedApi") internal open fun androidQServiceType(): Int { return if (requiredForegroundTypes.contains( diff --git a/stream-video-android-core/src/test/kotlin/io/getstream/video/android/core/notifications/internal/service/permissions/ForegroundServicePermissionManagerTest.kt b/stream-video-android-core/src/test/kotlin/io/getstream/video/android/core/notifications/internal/service/permissions/ForegroundServicePermissionManagerTest.kt index 30671f36a0..6dcd3b6837 100644 --- a/stream-video-android-core/src/test/kotlin/io/getstream/video/android/core/notifications/internal/service/permissions/ForegroundServicePermissionManagerTest.kt +++ b/stream-video-android-core/src/test/kotlin/io/getstream/video/android/core/notifications/internal/service/permissions/ForegroundServicePermissionManagerTest.kt @@ -27,6 +27,7 @@ import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner import org.robolectric.annotation.Config import org.robolectric.shadows.ShadowApplication +import org.robolectric.util.ReflectionHelpers import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertFalse @@ -99,6 +100,40 @@ class ForegroundServicePermissionManagerTest { assertEquals(0, type) } + @Test + @Config(sdk = [Build.VERSION_CODES.UPSIDE_DOWN_CAKE]) + fun `incoming call keeps short service below android 17`() { + // ringingServiceType only switches to phoneCall from Android 17 up, where the + // background-audio hardening applies. Android 14/15/16 keep their existing behavior. + val type = manager.getServiceType( + context, + CallService.TRIGGER_INCOMING_CALL, + ) + + assertEquals( + ServiceInfo.FOREGROUND_SERVICE_TYPE_SHORT_SERVICE, + type, + ) + } + + @Test + fun `incoming call uses phone call service type on android 17 and above`() { + // Android 17 background-audio hardening mutes the ringtone under SHORT_SERVICE; use the + // while-in-use phoneCall type instead. Robolectric 4.11.1 caps @Config at API 34, so the + // >= 37 branch is exercised by forcing SDK_INT. + ReflectionHelpers.setStaticField(Build.VERSION::class.java, "SDK_INT", 37) + + val type = manager.getServiceType( + context, + CallService.TRIGGER_INCOMING_CALL, + ) + + assertEquals( + ServiceInfo.FOREGROUND_SERVICE_TYPE_PHONE_CALL, + type, + ) + } + @Test @Config(sdk = [Build.VERSION_CODES.UPSIDE_DOWN_CAKE]) fun `no permission uses short service on android 14`() { From 67a92f4c4b90e992e7b7443dabef278789041256 Mon Sep 17 00:00:00 2001 From: rahullohra Date: Thu, 23 Jul 2026 12:04:09 +0530 Subject: [PATCH 2/3] Chore: revert --- .../kotlin/io/getstream/video/android/core/StreamVideoClient.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/StreamVideoClient.kt b/stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/StreamVideoClient.kt index 736e04d580..ee5e89befb 100644 --- a/stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/StreamVideoClient.kt +++ b/stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/StreamVideoClient.kt @@ -778,7 +778,7 @@ internal class StreamVideoClient internal constructor( logger.d { "[getOrCreateCall] type: $type, id: $id, members: $members" } return apiCall { - coordinatorConnectionModule.api.getOrCreateCall( //noob + coordinatorConnectionModule.api.getOrCreateCall( type = type, id = id, getOrCreateCallRequest = GetOrCreateCallRequest( From 0b0bf1de63e9fc7995b4d359117ac2a7a842f4f8 Mon Sep 17 00:00:00 2001 From: rahullohra Date: Thu, 23 Jul 2026 12:26:00 +0530 Subject: [PATCH 3/3] Chore: refactor name --- .../permissions/ForegroundServicePermissionManager.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/notifications/internal/service/permissions/ForegroundServicePermissionManager.kt b/stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/notifications/internal/service/permissions/ForegroundServicePermissionManager.kt index cc693689e2..f6432356ad 100644 --- a/stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/notifications/internal/service/permissions/ForegroundServicePermissionManager.kt +++ b/stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/notifications/internal/service/permissions/ForegroundServicePermissionManager.kt @@ -56,7 +56,7 @@ internal open class ForegroundServicePermissionManager { CallService.Companion.TRIGGER_ONGOING_CALL, CallService.Companion.TRIGGER_OUTGOING_CALL, -> calculateServiceType(context) - CallService.Companion.TRIGGER_INCOMING_CALL -> ringingServiceType() + CallService.Companion.TRIGGER_INCOMING_CALL -> incomingRingingServiceType() else -> noPermissionServiceType() } } @@ -93,13 +93,13 @@ internal open class ForegroundServicePermissionManager { } /** - * Foreground-service type for the incoming/ringing window. + * Foreground-service type for the incoming-ringing window. * * On Android 17+, background audio (the ringtone) is muted under [ServiceInfo.FOREGROUND_SERVICE_TYPE_SHORT_SERVICE], * so phoneCall-capable VoIP services use phoneCall instead. Other services keep [noPermissionServiceType]. */ @SuppressLint("InlinedApi") - internal open fun ringingServiceType(): Int = + internal open fun incomingRingingServiceType(): Int = // TODO: replace the hardcoded 37 with Build.VERSION_CODES. once compileSdk / // targetSdk are raised to 37; the constant does not exist at the current compileSdk. if (requiredForegroundTypes.contains(ServiceInfo.FOREGROUND_SERVICE_TYPE_PHONE_CALL) &&