Reputation: 111
When making a call to AudioManager.startBluetoothSCO() while targeting API level 18 or above in the Manifest, the documentation states that a raw audio connection is established, and if targeting API 17 or below a virtual voice call is used.
Up until API level 20 (Android L Preview) this worked fine, targeting any API. However, when using the latest Android Lollipop build LPX13D and targeting API level 18 or above I get a crash with the following stack trace:
E/AndroidRuntime(31705): Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String android.bluetooth.BluetoothDevice.getAddress()' on a null object reference E/AndroidRuntime(31705): at android.os.Parcel.readException(Parcel.java:1546) E/AndroidRuntime(31705): at android.os.Parcel.readException(Parcel.java:1493) E/AndroidRuntime(31705): at android.media.IAudioService$Stub$Proxy.startBluetoothSco(IAudioService.java:1587) E/AndroidRuntime(31705): at android.media.AudioManager.startBluetoothSco(AudioManager.java:1468)
If I target API level 17 or below on Android Lollipop everything works as expected.
I believe the source of the problem lies in a change to Android's audio code that happened in API level 21 in the file AudioService.java line 2392:
public void startBluetoothSco(IBinder cb, int targetSdkVersion) {
int scoAudioMode =
(targetSdkVersion < Build.VERSION_CODES.JELLY_BEAN_MR2) ?
SCO_MODE_VIRTUAL_CALL : SCO_MODE_UNDEFINED;
startBluetoothScoInt(cb, scoAudioMode);
}
It looks like SCO_MODE_UNDEFINED should instead be SCO_MODE_RAW. If you look through the file you can see that SCO_MODE_RAW is checked for in a few places, but is never actually passed in anywhere.
Is anybody else experiencing this crash? Does anyone know of a better fix than downgrading the target SDK to 17? If not, could you please star the bug report I filed with Google to increase the chance that it will be looked at :-)
Upvotes: 11
Views: 3004
Reputation: 2560
As @xsveda wrote if there is no headset connected, you will receive NPE on Lollipop.
You can try to check bluetooth headset connection first:
mAudioManager.isWiredHeadsetOn()
As doc described isWiredHeadsetOn()
(doc link) is depricated and uses only to check is a headset is connected or not.
And after this you can use startBluetoothSco()
connection. As for me I used this code:
This one for start:
if(mAudioManager.isWiredHeadsetOn())
mAudioManager.startBluetoothSco();
This one for stop:
if(mAudioManager.isBluetoothScoOn())
mAudioManager.stopBluetoothSco();
Hope it helps.
Upvotes: 4
Reputation: 19057
For now what seems to work for me was ignoring the NullPointerException
:
private void tryConnectAudio() {
verifyBluetoothSupport();
try {
mAudioManager.startBluetoothSco();
} catch (NullPointerException e) {
// TODO This is a temp workaround for Lollipop
Log.d(TAG, "startBluetoothSco() failed. no bluetooth device connected.");
}
}
@Julian Claudino, This works for me and routing through the Bluetooth mic, make sure that the Bluetooth device is recognized and connected:
private void verifyBluetoothSupport() {
getActivity().registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
int state = intent.getIntExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, -1);
Log.d(TAG, "Audio SCO state: " + state);
if (AudioManager.SCO_AUDIO_STATE_CONNECTED == state) {
Toast.makeText(getActivity(), "Bluetooth Connected", Toast.LENGTH_SHORT).show();
getActivity().unregisterReceiver(this);
}
}
}, new IntentFilter(AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED));
}
Hope this helps someone !
Upvotes: 0
Reputation: 17884
After few days of despair I've found a simple workaround:
startBluetoothSco()
throws NPE only if there is no Bluetooth device connected so it can be caught and ignored as there is "no one to talk to". If i.e. a BT headset is connected, SCO is started successfully and playback is working!
Upvotes: 0