Skip to content

TechNote_BluetoothAudio

Robert Wu edited this page Jan 13, 2022 · 9 revisions

Bluetooth Audio

Bluetooth audio devices work differently during a phone call and listening to music. Most Bluetooth audio devices expose two endpoints, A2DP and SCO.

A2DP (Advanced Audio Distribution Profile)

A2DP is a protocol supported on most Bluetooth Audio devices. This protocol is used for high quality music.

To achieve high quality music, Android encodes music with a codec. The headset receives this data and decodes it into PCM data. Android and headsets should support the SBC codec. Additional codecs like aptX, AAC, and LDAC are supported on a per device basis.

SCO (Synchronous Connection Oriented Link)

During a voice call, SCO is used. The underlying protocols HSP/HFP are supported on most Bluetooth devices. HSP (HeadSet Profile) is used for voice communication and HFP (Hands-Free Profile) was meant to control a phone from another unit.

Compared to A2DP, SCO has worse audio quality but lower latency.

Enabling SCO

Bluetooth audio devices default to A2DP when not in a voice call. You can add some functionality in your app to add a callback whenever a new audio device is added to see if a SCO device is added.

mAudioManager.registerAudioDeviceCallback(new AudioDeviceCallback() {
    @Override
    public void onAudioDevicesAdded(AudioDeviceInfo[] addedDevices) {

        List<AudioDeviceListEntry> deviceList =
                AudioDeviceListEntry.createListFrom(addedDevices, mDirectionType);
        if (deviceList.size() > 0){
            mDeviceAdapter.addAll(deviceList);
        }
    }
}, null);

You can check whether an audio device is SCO from AudioDeviceInfo.

    @RequiresApi(api = Build.VERSION_CODES.M)
    private boolean isScoDevice(int deviceId) {
        if (deviceId == 0) return false; // Unspecified
        AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
        final AudioDeviceInfo[] devices;
        devices = audioManager.getDevices(AudioManager.GET_DEVICES_ALL);
        for (AudioDeviceInfo device : devices) {
            if (device.getId() == deviceId) {
                return device.getType() == AudioDeviceInfo.TYPE_BLUETOOTH_SCO;
            }
        }
        return false;
    }

You can set setDeviceId() on a stream to set the audio device as SCO. Also, you need to start Bluetooth SCO in Android.

    private void startBluetoothSco() {
        AudioManager myAudioMgr = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
        myAudioMgr.startBluetoothSco();
    }

Please call startBluetoothSco() and setDeviceId() at similar times. Not doing so will result in the lack of sound. See HelloOboe for a full example of turning on a Bluetooth SCO device when the Bluetooth SCO endpoint is selected.

How to reduce the latency of a headset

In most cases, Android itself has a latency between 30-100 ms. Most of the latency comes from buffering on various headsets.

If a specific headset has a high latency, try changing the Bluetooth Audio Codec in developer settings. Also, try toggling "Disable Bluetooth A2DP hardware offload".