react-native-webrtc / react-native-incall-manager

Handling media-routes/sensors/events during a audio/video chat on React Native
ISC License
555 stars 192 forks source link

Audio is not routed to speaker on incoming call on Android #230

Open wilmxre opened 12 months ago

wilmxre commented 12 months ago

My scenario is the following:

I am using react-native-incall-manager with react-native-callkeep.

When initiating an outgoing video call i am calling InCallManager.start({ media: 'video', auto: true, ringback: '_BUNDLE_', }); in CallKeep's didActivateAudioSession event, which plays a ringback sound, then when the video call is connected the audio is playing through the speakers, at is should.

But on incoming calls, when the call is answered the video call is playing through the earpiece. On CallKeep's showIncomingCallUi event i am calling InCallManager.start({ media: 'video' });, to have all the events around the video call be handled automatically by IncallManager.

I think the issue might happen because IncallManager is used with CallKeep and maybe there are some overriding issues.

These are my logs, starting from when the incoming call is first registered to when the video call was answered and connected:

RNCallKeep D [RNCallKeepModule] displayIncomingCall, uuid: 2dbb2069-6dfb-4745-a1bf-ffb8ed676220, number: 909c6991-96c6-4844-a925-accd3a0fdd94, callerName: Io, hasVideo: true, payload: null
RNCallKeep D [RNCallKeepModule] listenToNativeCallsState
RNCallKeep D [VoiceConnectionService] Constructor
RNCallKeep D [VoiceConnectionService] onCreateIncomingConnection, name: Io, numbertel: 909c6991-96c6-4844-a925-accd3a0fdd94, isForeground: true, isReachable: false, timeout: null
RNCallKeep D [VoiceConnectionService] createConnection, callerNumber: tel:909c6991-96c6-4844-a925-accd3a0fdd94
RNCallKeep D [VoiceConnectionService] PhoneAccount is SELF_MANAGED, so connection will be too
RNCallKeep D [VoiceConnection] onStateChanged called, state : 0
RNCallKeep D [VoiceConnection] onStateChanged called, state : 2
RNCallKeep D [VoiceConnection] onStateChanged called, state : 1
RNCallKeep D [VoiceConnectionService] startForegroundService
RNCallKeep D [VoiceConnectionService] Starting foreground service
RNCallKeep D [VoiceConnection] onShowIncomingCallUi
RNCallKeep D [RNCallKeepModule][onReceive] ACTION_SHOW_INCOMING_CALL_UI
RNCallKeep V [RNCallKeepModule] sendEventToJS, eventName: RNCallKeepShowIncomingCallUi, bound: true, hasListeners: true args : {"hasVideo":"true","name":"Io","callUUID":"2dbb2069-6dfb-4745-a1bf-ffb8ed676220","handle":"909c6991-96c6-4844-a925-accd3a0fdd94"}
RNCallKeep D [VoiceConnection] onCallAudioStateChanged muted :false
RNCallKeep D [RNCallKeepModule][onReceive] ACTION_DID_CHANGE_AUDIO_ROUTE
RNCallKeep V [RNCallKeepModule] sendEventToJS, eventName: RNCallKeepDidChangeAudioRoute, bound: true, hasListeners: true args : {"output":"EARPIECE","callUUID":"2dbb2069-6dfb-4745-a1bf-ffb8ed676220","handle":"909c6991-96c6-4844-a925-accd3a0fdd94"}
RNCallKeep D [VoiceConnection] onCallAudioStateChanged muted :false
RNCallKeep D [RNCallKeepModule][onReceive] ACTION_DID_CHANGE_AUDIO_ROUTE
RNCallKeep V [RNCallKeepModule] sendEventToJS, eventName: RNCallKeepDidChangeAudioRoute, bound: true, hasListeners: true args : {"output":"EARPIECE","callUUID":"2dbb2069-6dfb-4745-a1bf-ffb8ed676220","handle":"909c6991-96c6-4844-a925-accd3a0fdd94"}
InCallManager D start audioRouteManager
InCallManager D storeOriginalAudioSetup()
InCallManager D requestAudioFocus(): res = AUDIOFOCUS_REQUEST_FAILED
InCallManager D startWiredHeadsetEvent()
InCallManager D startNoisyAudioEvent()
InCallManager D startMediaButtonEvent()
InCallManager D startProximitySensor()
InCallManager D setKeepScreenOn() true
InCallManager D --- updateAudioDeviceState: wired headset=false, BT state=HEADSET_UNAVAILABLE
InCallManager D Device status: available=[SPEAKER_PHONE], selected=SPEAKER_PHONE, user selected=NONE
InCallManager D --hasEarpiece: NO
InCallManager D --- updateAudioDeviceState done | isSpeakerPhoneOn: false
InCallManager D setSpeakerphoneOn(): true
RNCallKeep D [VoiceConnection] onCallAudioStateChanged muted :false
RNCallKeep D [RNCallKeepModule][onReceive] ACTION_DID_CHANGE_AUDIO_ROUTE
RNCallKeep V [RNCallKeepModule] sendEventToJS, eventName: RNCallKeepDidChangeAudioRoute, bound: true, hasListeners: true args : {"output":"SPEAKER","callUUID":"2dbb2069-6dfb-4745-a1bf-ffb8ed676220","handle":"909c6991-96c6-4844-a925-accd3a0fdd94"}
InCallManager D --- updateAudioDeviceState: wired headset=false, BT state=HEADSET_UNAVAILABLE
InCallManager D Device status: available=[], selected=NONE, user selected=NONE
InCallManager D --hasEarpiece: NO
InCallManager D setAudioDeviceInternal(device=SPEAKER_PHONE)
InCallManager D New device status: available=[SPEAKER_PHONE], selected=SPEAKER_PHONE
InCallManager D --- updateAudioDeviceState done | isSpeakerPhoneOn: true
InCallManager D onPause()
InCallManager D onResume()
RNCallKeep D [VoiceConnection] onCallAudioStateChanged muted :false
RNCallKeep D [RNCallKeepModule][onReceive] ACTION_DID_CHANGE_AUDIO_ROUTE
RNCallKeep V [RNCallKeepModule] sendEventToJS, eventName: RNCallKeepDidChangeAudioRoute, bound: true, hasListeners: true args : {"output":"EARPIECE","callUUID":"2dbb2069-6dfb-4745-a1bf-ffb8ed676220","handle":"909c6991-96c6-4844-a925-accd3a0fdd94"}
RNCallKeep D [RNCallKeepModule] setCurrentCallActive, uuid: 2dbb2069-6dfb-4745-a1bf-ffb8ed676220
RNCallKeep D [VoiceConnection] onStateChanged called, state : 4

As you can see in

InCallManager D Device status: available=[], selected=NONE, user selected=NONE
InCallManager D --hasEarpiece: NO
InCallManager D setAudioDeviceInternal(device=SPEAKER_PHONE)
InCallManager D New device status: available=[SPEAKER_PHONE], selected=SPEAKER_PHONE
InCallManager D --- updateAudioDeviceState done | isSpeakerPhoneOn: true

the speaker is selected and the audio device is updated, but the audio is still not routed to the speakers, despite the logs.

p.s. --hasEarpiece: NO is because i hardcoded hasEarpiece() return value to false, to make it impossible to route it to the earpiece

I tried calling IncallManager.setForceSpeakerPhoneOn right after IncallManager.start({ media: 'video' }), that gave me

Device status: available=[], selected=NONE, user selected=NONE
D  setForceSpeakerphoneOn() flag: 1
E  selectAudioDevice() Can not select SPEAKER_PHONE from available []

so for some reason, on incoming call the SPEAKER_PHONE is not available.

I event tried calling CallKeep's toggleAudioRouteSpeaker, but that didn't help either.

I would take any advice happily.

Lucas0liveir commented 11 months ago

Same to me. When coming from a call, the speaker dont back to on. I noticed that on Android 11 this works normally, on Android 12 the problem occurs. Any solution?

wilmxre commented 11 months ago

i did two things:

  1. i called the start function only after a delay after the call has connected so incoming call -> call was answered -> call is connecting -> after call connected, i wait, in my case i had an event onIceCompleted, when that fired i called IncallManager.start({media:'video'})
  2. i gave the user the possibility to change the audio route from the UI
Lucas0liveir commented 11 months ago

In my case, I listener an Device event emitter when the audio focus coming back to my app and set speaker to true, example:

DeviceEventEmitter.addListener('onAudioFocusChange', function (data) {
            // --- do something with events
            switch (data?.eventText) {
                case 'AUDIOFOCUS_GAIN':
                    InCallManager.setSpeakerphoneOn(true)
                    break;
                default:
                    break;
            }
        })
Lucas0liveir commented 11 months ago

This is not a bug, but rather a standard behavior of the operating system.