Accepting a Call via Bluetooth Headset
Asked Answered
S

3

14

i am working on a VoIP-Android-App. I would like to accept and decline Calls via a connnected Bluetooth Headset in an Activity.

What I have tried so far:

  • Using a Media Session to receive Media Button clicks.

    Problem: If we start BluetoothSCO we do not receive any Media Button clicks. If we do not start BluetoothSCO we do receive Media Button clicks but we cannot differentiate long and short button clicks because downtime is always 0, the keycode is always KEYCODE_MEDIA_PLAY and the ACTION_DOWN is immediately followed by ACTION_UP. Those problems only occur if we are connected via Bluetooth. If we are connnected over a cable Headset we do get the appropriate keycodes (KEYCODE_HEADSETHOOK) and the downtime is not 0.

  • Using a BroadcastReceiver to listen for Bluetooth SCO connection changes.

    private val scoReceiver = object : BroadcastReceiver() {
        fun onReceive(context: Context, intent: Intent) {
            val state = intent.getIntExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, -1)
            val previousState = intent.getIntExtra(AudioManager.EXTRA_SCO_AUDIO_PREVIOUS_STATE, -1)
            if (state == AudioManager.SCO_AUDIO_STATE_DISCONNECTED && previousState == AudioManager.SCO_AUDIO_STATE_CONNECTED) {
                Log.e(TAG, "SCO Disconnected")
                hangupCall()
            }
        }
    }
    
    protected fun onStart() {
        super.onStart()
        val intentFilter = IntentFilter()
        intentFilter.addAction(AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED)
        registerReceiver(scoReceiver, intentFilter)
    }
    

    With this approach i can detect when the user wants to hang up the call, for example a long press on the bluetooth headset because this triggers the SCO to disconnect.

    Problem: We can not detect if the user wants to accept an incoming call.

  • Using dispatchKeyEvent, onKeyDown and onKeyUp.

    Problem: They never get called at all.

Does anyone has any advice or a best practice how to correctly handle bluetooth headsets? Any help is very appreciated. Thanks in advance!

Semiyearly answered 18/7, 2018 at 10:28 Comment(4)
Did you try to read for any input you get from the bluetooth device? Using an InputStream? Maybe the device sends some data when you press a button.Libbylibeccio
Did you find the answer? I am faced with the same problem, if SCO is enabled, my first click while ringing is simply ignored.Eboat
@Semiyearly did you try the solution I answered ? please try onceKwa
@SergeyZabelnikov did you try the solution I answered ? please try once.Kwa
M
2

During normal and virtual voice call (including ringing) all events of Bluetooth headset unit buttons are processed by Bluetooth Headset Service internaly and not broadcasted as button events. Bluetooth Headset Service redirects these events into Telecom framework (answer/hangupCall).

Magdalen answered 18/7, 2018 at 14:26 Comment(3)
So, to make it work we need to listen to telephony phone state? Isn't it?Eboat
Could you give us an example how that could work? Would be very helpful.Semiyearly
@Semiyearly For example:incoming call. Audio Gateway (smartphone side) sends unsolicited results RING via serial port and, if in-band ring feature is enabled, establishes SCO connection and sends ringing sounds to it. In such context pressing buttons on Hans-Free unit will cause sending ATA (accept) or AT+CHUP (reject) command via serial port (older Headset profile knows the only command AT+CKPD=200 for all cases). All these commands will be processed by HeadsetStateMachine and forwarded to Telecom framework.Magdalen
F
2

These events are handled internally in HeadsetStateMachine (under packages/apps/Bluetooth).

These events are forwarded to IBluetoothHeadsetPhone interface. The single application to which all the events are forwarded is defined at run-time by following binding code in HeadsetStateMachine.java. This is to allow phone manufacturers to forward them to custom phone application instead of default one in cases where default one is not used.

Intent intent = new Intent(IBluetoothHeadsetPhone.class.getName());
intent.setComponent(intent.resolveSystemService(context.getPackageManager(), 0));
if (intent.getComponent() == null || !context.bindService(intent, mConnection, 0)) {
  Log.e(TAG, "Could not bind to Bluetooth Headset Phone Service");
}

To make the events get forwarded to your application instead of default phone application you would have to modify aosp code. You would need to intercept the events at one of HeadsetStateMachine , BluetoothHeadsetPhone proxy or the phone application.

Unfortunately what you are looking for is currently not possible without modifying AOSP code. Some headsets like Plantronics have custom BT events which are forwarded to all applications - some of the existing VoIP applications support these custom intents to support at-least answering calls for some of the headsets.

Fahrenheit answered 7/8, 2018 at 8:33 Comment(2)
It does make sense, but really strange that it can't be achieved without patching AOSPEboat
@Fahrenheit does android have done any update on this. Still we need to patch AOSP?Gershom
C
0

You should use android Telecom API and implement android.telecom.ConnectionService and android.telecom.Connection where you should override onAnswer() callback which will be called when you try to answer a call via bluetooth headset.

For more details read docs - https://developer.android.com/guide/topics/connectivity/telecom/selfManaged

Coppola answered 26/12, 2022 at 12:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.