twilio / voice-quickstart-android

Quickstart app for the Voice Android SDK
https://www.twilio.com/docs/api/voice-sdk/android/getting-started
MIT License
184 stars 140 forks source link

Issue concurrent call SIM/Twilio with ConnectionService #345

Closed Fgabz closed 7 months ago

Fgabz commented 4 years ago

Issue concurrent call SIM/Twilio with ConnectionService

Hello,

We're building an app using the Twilio SDK however, we faced some issues with the microphone when there is a concurrent call between SIM and Twilio. The app is not able to get the focus of the microphone, even if the SIM call is holding

We faced the issue on all OnePlus devices and on Samsung devices we had to manually call audioManager.mode = AudioManager.MODE_IN_COMMUNICATION right after answering the call

Logs showing:

OpenSLESRecorder: Bad OpenSL ES record timing

Device Model

OnePlus

kbagchiGWC commented 4 years ago

@Fgabz

We don't have an OnePlus device with us to reproduce the issue.

Have you tried the same scenario with another VOIP app i.e. FB messenger , Skype, WhatsApp? That way you can confirm if the issue is with your app or the phone app on the device.

I also wanted to point out there seems to be issues with the phone app in OnePlus devices, below are few from their community page :

Please specify the make/model of the Samsung devices that have the same issue.

We have recently released AudioSwitch, a new Android library for managing audio routing in real-time communication apps. This library enables:

The QS app uses this library. You can refer to this PR for the changes required to adopt this library. Let us know if this change resolves the audio focus issue.

Fgabz commented 4 years ago

Hello @kbagchiGWC

So I've tried the AudioSwitch lib, it's working pretty well on Samsung devices even with concurrent calls (Twilio vs SIM), however the issue persists with OnePlus devices

aaalaniz commented 4 years ago

Hey @Fgabz

Glad to know that AudioSwitch is working well for you!

I wanted to follow up on trying these steps on a OnePlus device with WhatsApp, FB Messenger, or Skype to see if the behavior is the same. This would help our triaging efforts.

Thanks!

Fgabz commented 4 years ago

Hey @aaalaniz !

We tried with WhatsApp, FB Messenger and Signal App, unfortunately, they blocked the ability to do this (either they end the app call or block the ability to start one) and I'm not using Skype, so I wasn't able to try it.

However I tried with this app https://play.google.com/store/apps/details?id=com.onoffapp.app&hl=en and got the same issue we got with our app

aaalaniz commented 4 years ago

Hey @Fgabz

I was reading more about ConnectionService and came across this very helpful article Calling Constraints. According to these constraints, your use case may not be supported. Specifically the second bullet calls out the following.

If the user is engaged in regular managed calls (for example, using the built-in Phone or Dialer app), the user cannot be in calls originated from calling apps. This means that if the user is in a regular call using their mobile carrier, they cannot also be in a FooTalk or BarTalk call concurrently.

The article seems to explicitly exclude this use case (even though the call being on hold is not mentioned as in the previous bullet). Nevertheless, these constraints may explain why this behavior is not supported in other commonly used calling apps. Does ending the call versus holding the call significantly impact your use case?

Thanks!

Fgabz commented 4 years ago

Actually the documentation is not very clear on this point because from what we tried It's possible and it's working pretty well and it's handled by the default phone APP UI/UX as a normal concurrent call like two SIM calls (we can switch from one to call to another from default phone APP UI/UX)

I think what the documentation is saying is that we can't have two ongoing calls at the same time (meaning a kind of conference call)

Also on Samsung and Pixel phones, it's working well. It might be an issue with OnePlus devices on how they're focusing audio input

It's really important for us to be able to handle this use case. For now, we disabled this feature on all OnePlus devices but we would like to avoid that since it's impacted a lot of our users.

Also another error that is showing during a concurrent call:

E/AudioSystem-JNI: Command failed for android_media_AudioSystem_setForceUse: -1

Fgabz commented 4 years ago

If you want to try on your device (I assume it will work if you don't have a OnePlus) https://github.com/kbagchiGWC/voice-quickstart-android/tree/connection_service_example_makecall

We have pretty much the same configuration on our app.

aaalaniz commented 4 years ago

Hey @Fgabz

We don't have a OnePlus device on hand, but our data shows that the OnePlus 6 would be a good representative of field devices. Do you know if the issue is reproducible on this device? Is this issue reproducible on all OnePlus devices or just a subset?

We can order a device and investigate. However, in the meantime, I recommend ending the PSTN call on OnePlus devices instead of putting on hold (as you have already done).

Also, have you tried filing an issue with google? They will be able to provide more insight into the expected behavior of applications that use ConnectionService. I agree that the documentation could be more explicit about putting ongoing PSTN calls on hold and how that impacts VoIP app calls.

Thanks!

Fgabz commented 4 years ago

Hello @aaalaniz!

You will be able to reproduce the error on the OnePlus6. We tried different devices from OnePlus 3 to OnePlus 7, and they all show the same audio input issue. In the meantime, I'll fill a bug in their issue tracker to get more insight from Google.

Thanks!

Fgabz commented 4 years ago

Hello @aaalaniz!

Were you able to test the use case on a OnePlus ?

Thanks!

kbagchiGWC commented 4 years ago

Hi @Fgabz

We got a OnePlus Device and will try to reproduce the issue and see if there is a workaround. We will keep you posted on our findings.

Thanks.

kbagchiGWC commented 4 years ago

Hi @Fgabz

We experimented with the OnePlus 6 device and wanted to share some of our findings. We have realized that the behavior differs based on how the VoIP application is integrated with ConnectionService.

Following is the use case you described :

  1. Be in a SIM call
  2. Receive an incoming VoIP call
  3. Accept it

We can easily reproduce the audio experience described here, if the VoIP app does not integrate incoming call with ConnectionService. After step 3, the VoIP caller can’t hear the callee and the issue is reproduced. Once the SIM call is disconnected and the VoIP app is brought to the foreground the two way audio works as expected for the VoIP Call.

We have also experimented with the VoIP app when the incoming call is integrated with ConnectionService. With this configuration, the SIM call goes on hold as soon as the the VoIP call is answered and the audio flows both ways without any issue. If ConnectionService is integrated correctly, you should be able to switch between the SIM call and the VoIP call from the native phone app itself.

We need some clarifications :

Thanks.

Fgabz commented 4 years ago

Hello @kbagchiGWC and thanks for your help!

So what we did:

As a reminder the sound is working(we can hear the caller) but the voice recording isn't working meaning that the caller can't hear the app, but as soon as we are ending the SIM call everything works (the caller can hear us).

I've put some error logs here, that we can see only when there is a SIM call holding with an ongoing VOIP call. It might be related.

0-07-21 12:24:59.916 799-7495/? E/audio_hw_primary: start_input_stream: pcm_prepare returned -1 2020-07-21 12:24:59.920 799-7495/? E/audio_route: unable to find path 'audio-record-voip voice-dmic-ef' 2020-07-21 12:24:59.920 799-7495/? W/sound_trigger_hw: sound_trigger_hw_call_back: Unknown event 17 2020-07-21 12:24:59.996 799-7495/? W/msm8974_platform: platform_check_capture_codec_backend_cfg:txbecf: afe: Use default bw and sr for voice/voip calls and for unprocessed/camera source 2020-07-21 12:24:59.996 799-7495/? W/sound_trigger_hw: sound_trigger_hw_call_back: Unknown event 17 2020-07-21 12:24:59.996 799-7495/? E/audio_route: unable to find path 'echo-reference handset' 2020-07-21 12:25:00.002 799-7495/? E/ACDB-LOADER: Error: ACDB AudProc vol returned = -19 2020-07-21 12:25:00.003 799-7495/? E/ACDB-LOADER: Error: ACDB_CMD_GET_AFE_COMMON_TABLE_SIZE Returned = -19 2020-07-21 12:25:00.003 799-7495/? E/ACDB-LOADER: Error: ACDB AFE returned = -19

kbagchiGWC commented 4 years ago

Hi @Fgabz

We found the behavior explained below different during our testing because the SIM call goes on hold as soon as the the VoIP call is answered.

As a reminder the sound is working(we can hear the caller) but the voice recording isn't working meaning that the caller can't hear the app, but as soon as we are ending the SIM call everything works (the caller can hear us).

Thanks.

Fgabz commented 4 years ago

I'll try tomorrow the code above and share my findings at the same time.

Fgabz commented 4 years ago

Hello @kbagchiGWC

So what I did and found:

I/System.out: [App - Concurrent Call] isIncomingCallPermitted => true
/System.out: [App - Concurrent Call] isOutgoingCallPermitted => false
I/System.out: [App - Concurrent Call] isInCall =>true

The phoneaccount is provided this way

fun providePhoneAccountHandleSelfManaged(context: Context): PhoneAccountHandle {
        return PhoneAccountHandle(ComponentName(context, SelfConnectionService::class.java), PHONE_ACCOUNT_HANDLE_ID_SELF_MANAGED)
    }

The connection object =>

private fun createConnection(request: ConnectionRequest?): SelfConnection {
        val serviceClassLoader = this.classLoader
        router.bindViewDelegate(this)
        isRunning = true

        return selfConnectionDelegate.get().apply {

            request?.extras?.let {
                it.getBundle(TelecomManager.EXTRA_INCOMING_CALL_EXTRAS)?.let { extras ->
                    extras.classLoader = serviceClassLoader

                    putExtras(extras)
                }
            }

            request?.address?.let {
                setCallerDisplayName(application.appName, TelecomManager.PRESENTATION_ALLOWED)
                this.setAddress(it, TelecomManager.PRESENTATION_ALLOWED)
            }

            audioModeIsVoip = true

            statusHints = StatusHints(application.appName, Icon.createWithResource(applicationContext, application.appIconRes), Bundle())

            if (extras == null || !extras.getBoolean(EXTRA_USER_ALREADY_IN_CALL)) {
                connectionProperties = Connection.PROPERTY_SELF_MANAGED
            }

            val capabilities = connectionCapabilities
            connectionCapabilities = capabilities or Connection.CAPABILITY_HOLD or
                Connection.CAPABILITY_SUPPORT_HOLD or
                Connection.CAPABILITY_MUTE

            onCallInitialized()
        }
    }

The registration =>

 val phoneAccount =
                PhoneAccount.Builder(phoneAccountHandleSystemManaged, "Concurrent Call Account - Aircall")
                    .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER)
                    .build()
            telecomManager.registerPhoneAccount(phoneAccount)

How the call is added to phone stack =>

telecomManager.addNewIncomingCall(phoneAccountHandleSystemManaged, callInfo)

kbagchiGWC commented 4 years ago

@hi @Fgabz Does this happen on all OnePlus 6 devices or only on one?

The error logs happened on the OnePlus 6

Our ConnectionService implementation looks the same as yours.

Please provide call sids from unique devices that are having this issue.

Fgabz commented 4 years ago

Hello @kbagchiGWC and sorry for the delay,

Here an example of call made with a OnePlus 6T

CAbb72bcc189a3ca149b1d9e3842b82318

kbagchiGWC commented 4 years ago

@Fgabz

Is this the only device that has the issue? We are not able to reproduce the issue using our OnePlus 6 test device.

Fgabz commented 4 years ago

@kbagchiGWC the issue is on all OnePlus we tested(6T to 7T).

kbagchiGWC commented 4 years ago

@Fgabz

Thanks for the information. Unfortunately we are not able to suggest a fix as we can't reproduce the issue on the OnePlus 6T we have. We will work on a quickstart example with ConnectionService that may help to debug the issue. In the mean time can you share your implementation of how you are passing the Twilio incoming call to ConnectionService?

// interested to know what is being passed in the bundle 
telecomManager.addNewIncomingCall(handle, bundle);
Fgabz commented 4 years ago

Sure @kbagchiGWC

`val isAlreadyInCall = telecomManagerProvider.isAlreadyInCall()

        val extraIncomingCallInfo = bundleOf(
            EXTRA_INBOUND_CALL_INVITATION to callInvitation as ParcelableCallInvitation,
            EXTRA_INBOUND_CALL_METADATA to metadataMapper.entityToParcelable(call),
            EXTRA_USER_ALREADY_IN_CALL to isAlreadyInCall
        )

        val callInfo = Bundle()

        val externalParticipant = call.getActiveParticipant(user.id)
        val phoneNumber = externalParticipant?.from ?: callInvitation.from

        callInfo.putParcelable(
            TelecomManager.EXTRA_INCOMING_CALL_ADDRESS,
            Uri.fromParts(PhoneAccount.SCHEME_TEL, phoneNumber, null)
        )

        callInfo.putBundle(TelecomManager.EXTRA_INCOMING_CALL_EXTRAS, extraIncomingCallInfo)

        if (isAlreadyInCall) {
            try {
                telecomManager.addNewIncomingCall(phoneAccountHandleSystemManaged, callInfo)
            } catch (err: SecurityException) {
                Timber.e(err, "Concurrent call not supported by this device")
            }
        } else {
            telecomManager.addNewIncomingCall(phoneAccountHandleSelfManaged, callInfo)
        }`
4brunu commented 3 years ago

@kbagchiGWC any news on the ConnectionService sample? Those are a few examples that can help in creating the ConnectionService sample. https://github.com/nigma-dev/voip-twilio https://github.com/lakshaydulani/android-connectionservice-kotlin-basic Thanks

cybex-dev commented 1 year ago

Sure @kbagchiGWC

`val isAlreadyInCall = telecomManagerProvider.isAlreadyInCall()

        val extraIncomingCallInfo = bundleOf(
            EXTRA_INBOUND_CALL_INVITATION to callInvitation as ParcelableCallInvitation,
            EXTRA_INBOUND_CALL_METADATA to metadataMapper.entityToParcelable(call),
            EXTRA_USER_ALREADY_IN_CALL to isAlreadyInCall
        )

        val callInfo = Bundle()

        val externalParticipant = call.getActiveParticipant(user.id)
        val phoneNumber = externalParticipant?.from ?: callInvitation.from

        callInfo.putParcelable(
            TelecomManager.EXTRA_INCOMING_CALL_ADDRESS,
            Uri.fromParts(PhoneAccount.SCHEME_TEL, phoneNumber, null)
        )

        callInfo.putBundle(TelecomManager.EXTRA_INCOMING_CALL_EXTRAS, extraIncomingCallInfo)

        if (isAlreadyInCall) {
            try {
                telecomManager.addNewIncomingCall(phoneAccountHandleSystemManaged, callInfo)
            } catch (err: SecurityException) {
                Timber.e(err, "Concurrent call not supported by this device")
            }
        } else {
            telecomManager.addNewIncomingCall(phoneAccountHandleSelfManaged, callInfo)
        }`

Hi @Fgabz (and @4brunu). I've been transitioning a library to the native Android "callkit" i.e. ConnectionService implementation for Twilio Voice, however I found myself stuck at the ConnectionService.onCreateIncomingConnection(PhoneAccountHandle?, ConnectionRequest?) bit, specifically

myBundle.containsKey(Constants.INCOMING_CALL_INVITE)

or

var ci: CallInvite? = null
// ...
ci = myBundle.getParcelable<CallInvite>(Constants.INCOMING_CALL_INVITE)  /// next problem arrives over here

In your example/provided code, it seems you're passing the CallInvite (Parcel) to the ConnectionService without an issue (or atleast mention of it).

Question

To confirm, were you successful in "getting" the CallInvite from bundled extras in your onCreateIncomingConnection function?


My attempt below, results in a ClassNotFoundException com.twilio.voice.CallInvite when calling either myBundle.containsKey or myBundle.getParcelable<CallInvite>.

Snippets

Below I present 2 key snippets:

In this second snippet, I have 2 comments showing where the problem occurs.

File: VoiceFirebaseMessagingService.kt

private fun handleFCMCallInvite(callInvite: CallInvite, notificationId: Int) {
    // Get telecom manager
    val telecomManager = getSystemService(TELECOM_SERVICE) as TelecomManager

    // Get PhoneAccountHandle
    val caller = callInvite.from!!.toString()
    val componentName = ComponentName(applicationContext.packageName, TwilioVoiceConnectionService::class.java.name)
    val phoneAccountHandle = PhoneAccountHandle(componentName, caller)

    // Create my Bundle containing information e.g. notificationId and callInvite
    val myBundle = Bundle()
    myBundle.putParcelable(Constants.INCOMING_CALL_INVITE, callInvite)
    myBundle.putInt(Constants.INCOMING_CALL_NOTIFICATION_ID, notificationId)

    // Add new incoming call to the telecom manager
    telecomManager.addNewIncomingCall(phoneAccountHandle, Bundle().apply {
        putBundle(EXTRA_INCOMING_CALL_EXTRAS, myBundle)
        putParcelable(EXTRA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle)
    })
}

File: TwilioVoiceConnectionService.kt

override fun onCreateIncomingConnection(connectionManagerPhoneAccount: PhoneAccountHandle?, request: ConnectionRequest?): Connection {
    super.onCreateIncomingConnection(connectionManagerPhoneAccount, request)
    Log.d(TAG, "onCreateIncomingConnection")
    val connection: Connection = VoipConnection(applicationContext)
    connection.extras = request?.extras

    var ci: CallInvite? = null
    val myBundle: Bundle? = connection.extras.getBundle(TelecomManager.EXTRA_INCOMING_CALL_EXTRAS);
    if(myBundle != null) {
        Log.d(TAG, "onCreateIncomingConnection: myBundle is not null")
        if (myBundle.containsKey(Constants.INCOMING_CALL_INVITE) ) {
            Log.d(TAG, "onCreateIncomingConnection: myBundle contains INCOMING_CALL_INVITE")
            ci = myBundle.getParcelable<CallInvite>(Constants.INCOMING_CALL_INVITE)
        } else {
            Log.d(TAG, "onCreateIncomingConnection: myBundle does not contain INCOMING_CALL_INVITE")
        }
    } else {
        Log.d(TAG, "onCreateIncomingConnection: myBundle is null")
    }

    // ...
   // return VoipConnection(...)
}

Hi @kbagchiGWC, maybe you (or a fellow colleague) could provide some insight into this issue (I'm assuming I may be missing something):

The above results in the following exception:

Class not found when unmarshalling: com.twilio.voice.CallInvite
java.lang.ClassNotFoundException: com.twilio.voice.CallInvite
    at java.lang.Class.classForName(Native Method)
    at java.lang.Class.forName(Class.java:454)
    at android.os.Parcel.readParcelableCreator(Parcel.java:3403)
    at android.os.Parcel.readParcelable(Parcel.java:3337)
    at android.os.Parcel.readValue(Parcel.java:3239)
    at android.os.Parcel.readArrayMapInternal(Parcel.java:3636)
    at android.os.BaseBundle.initializeFromParcelLocked(BaseBundle.java:292)
    at android.os.BaseBundle.unparcel(BaseBundle.java:236)
    at android.os.BaseBundle.containsKey(BaseBundle.java:516)
    at com.twilio.twilio_voice.connectionservice.TwilioVoiceConnectionService.onCreateIncomingConnection(TwilioVoiceConnectionService.kt:43)
    at android.telecom.ConnectionService.createConnection(ConnectionService.java:2061)
    at android.telecom.ConnectionService.access$400(ConnectionService.java:96)
    at android.telecom.ConnectionService$2$1.loggedRun(ConnectionService.java:914)
    at android.telecom.Logging.Runnable$1.run(Runnable.java:37)
    at android.telecom.ConnectionService.onAccountsInitialized(ConnectionService.java:3272)
    at android.telecom.ConnectionService.access$5000(ConnectionService.java:96)
    at android.telecom.ConnectionService$5$1.loggedRun(ConnectionService.java:2577)
    at android.telecom.Logging.Runnable$1.run(Runnable.java:37)
    at android.os.Handler.handleCallback(Handler.java:938)
    at android.os.Handler.dispatchMessage(Handler.java:99)
    at android.os.Looper.loopOnce(Looper.java:226)
    at android.os.Looper.loop(Looper.java:313)
    at android.app.ActivityThread.main(ActivityThread.java:8663)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:567)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1135)
Caused by: java.lang.ClassNotFoundException: com.twilio.voice.CallInvite
    at java.lang.Class.classForName(Native Method)
    at java.lang.BootClassLoader.findClass(ClassLoader.java:1358)
    at java.lang.BootClassLoader.loadClass(ClassLoader.java:1418)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:312)
    at java.lang.Class.classForName(Native Method) 
    at java.lang.Class.forName(Class.java:454) 
    at android.os.Parcel.readParcelableCreator(Parcel.java:3403) 
    at android.os.Parcel.readParcelable(Parcel.java:3337) 
    at android.os.Parcel.readValue(Parcel.java:3239) 
    at android.os.Parcel.readArrayMapInternal(Parcel.java:3636) 
    at android.os.BaseBundle.initializeFromParcelLocked(BaseBundle.java:292) 
    at android.os.BaseBundle.unparcel(BaseBundle.java:236) 
    at android.os.BaseBundle.containsKey(BaseBundle.java:516) 
    at com.twilio.twilio_voice.connectionservice.TwilioVoiceConnectionService.onCreateIncomingConnection(TwilioVoiceConnectionService.kt:43) 
    at android.telecom.ConnectionService.createConnection(ConnectionService.java:2061) 
    at android.telecom.ConnectionService.access$400(ConnectionService.java:96) 
    at android.telecom.ConnectionService$2$1.loggedRun(ConnectionService.java:914) 
    at android.telecom.Logging.Runnable$1.run(Runnable.java:37) 
    at android.telecom.ConnectionService.onAccountsInitialized(ConnectionService.java:3272) 
    at android.telecom.ConnectionService.access$5000(ConnectionService.java:96) 
    at android.telecom.ConnectionService$5$1.loggedRun(ConnectionService.java:2577) 
    at android.telecom.Logging.Runnable$1.run(Runnable.java:37) 
    at android.os.Handler.handleCallback(Handler.java:938) 
    at android.os.Handler.dispatchMessage(Handler.java:99) 
    at android.os.Looper.loopOnce(Looper.java:226) 
    at android.os.Looper.loop(Looper.java:313) 
    at android.app.ActivityThread.main(ActivityThread.java:8663) 
    at java.lang.reflect.Method.invoke(Native Method) 
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:567) 
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1135) 
Caused by: java.lang.NoClassDefFoundError: Class not found using the boot class loader; no stack trace available

Libraries, etc

build.gradle (module)

buildscript {
    ext.kotlin_version = '1.8.0'

    repositories {
        google()
        mavenCentral()
    }

    dependencies {
        classpath 'com.android.tools.build:gradle:7.4.2'
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
    }
}
...
android {
    compileSdk 33
    ...
}
...
dependencies {
    ...
    implementation("com.twilio:voice-android:6.2.1")
    implementation("com.google.firebase:firebase-messaging-ktx:23.2.1")
    ...
}
4brunu commented 1 year ago

Hey @cybex-dev,

I think I got the same issue that you have, and the problem from what I can remember is that you can't pass a parcelable to with the key TelecomManager.EXTRA_INCOMING_CALL_EXTRAS.

So what I ended up doing is converting the parcelable that I want to pass there to a json string, and passing the string itself instead of the parcelable.

Something like this.

// https://stackoverflow.com/questions/66362729/how-to-receive-the-bundle-extras-passed-in-when-calling-telecommanager-addnewi
val jsonString = Serializer.kotlinxSerializationJson
    .encodeToString(parcelableClassThatIWantToPass)

telecomManager.addNewIncomingCall(
    phoneAccountHandle,
    Bundle().apply {
        putString(TelecomManager.EXTRA_INCOMING_CALL_EXTRAS, jsonString)
        putParcelable(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle)
    },
)

Then on the method onCreateIncomingConnection I got the json string and convert it back to a class. val jsonString = request?.extras?.getString(TelecomManager.EXTRA_INCOMING_CALL_EXTRAS).

Hope it helped.

cybex-dev commented 1 year ago

Hi @4brunu

Thanks for getting back - I came across this thread, it was quite helpful.

Problem

I found the solution, the issue was disjointed from Twilio, well-documented in #561.

To quote from @afalls-twilio :

One idea could be to capture the class loader from the main activity's onCreate, where the Voice SDK is first invoked.

Cause

This set me on the right track, the Voice SDK's ClassLoader seemed to be initialised (set) on the wrong thread.

Solution

var ci: CallInvite? = null
val myBundle: Bundle? = connection.extras.getBundle(TelecomManager.EXTRA_INCOMING_CALL_EXTRAS);
// Add class loader for CallInvite
myBundle.classLoader = CallInvite::class.java.classLoader

Though it resolves the immediate problem, a solution (long-term) is as @afalls-twilio suggests, to capture the loader where it was initialised.

anikitin-intermedia commented 3 months ago

@Fgabz @kbagchiGWC

It seems, that the issue is in SIM card carrier. I have similar issue, but with Google Pixel 3/7. Here is the details. Google recommends to not allow 3d party app place calls while there is GSM call for such operators. Unfortunately, Google did not list all of such operators.