-
Notifications
You must be signed in to change notification settings - Fork 572
TechNote_BluetoothAudio
Bluetooth audio devices work differently during a phone call and listening to music. Most Bluetooth audio devices expose two endpoints, A2DP and SCO.
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.
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.
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.
Remember to call stopBluetoothSco() when the headset is disconnected. By also adding an onAudioDevicesRemoved() callback to registerAudioDeviceCallback, you can figure out when this happens.
startBluetoothSco() and stopBluetoothSco() is recently deprecated. The alternate method is calling setCommunicationDevice() and clearCommunicationDevice() introduced in S. Below is a snippet calling setCommuicationDevice() with a device id. Please call isScoDevice() first because setCommuicationDevice() will throw if the device type is not a communication device.
@RequiresApi(api = Build.VERSION_CODES.S)
private void setCommunicationDevice(int deviceId) {
AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
final AudioDeviceInfo[] devices;
devices = audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS);
for (AudioDeviceInfo device : devices) {
if (device.getId() == deviceId) {
audioManager.setCommunicationDevice(device);
return;
}
}
}
Similar to setBluetoothSco(), you should clear the communication device when something else is in use. That function is clearCommunicationDevice(). The HelloOboe example also covers setCommunicationDevice() and clearCommunicationDevice().
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".
A2DP uses a variety of codecs including SBC, AAC, aptX, aptX HD, and LDAC. This allows raw PCM data to be compressed into less data for better audio quality.
Some Android phones implement Bluetooth A2DP offloading. This allows the encoding to be done on a phone specific DSP.
If there are Bluetooth glitches for a specific device, try switching the codec or disabling A2DP offloading in the developer settings.
- Apps Using Oboe or AAudio
- Tech Notes
- OboeTester Instructions
- Quirks and Bugs
- Developer Notes