Ошибка AudioManager.startBluetoothSco() на Android Lollipop
При обращении к AudioManager.startBluetoothSCO() при таргетинге API уровня 18 или выше в манифесте документация гласит, что необработанное аудио соединение установлено, и если используется таргетинг на API 17 или ниже виртуального голосового вызова.
До уровня API 20 (Android L Preview) это работало отлично, ориентируясь на любой API. Тем не менее, при использовании последней версии Lollipop для Android LXL13D и ориентированного на уровень API уровня 18 или выше я получаю сбой со следующей трассировкой стека:
E/AndroidRuntime (31705): вызвано: java.lang.NullPointerException: попытка вызвать виртуальный метод 'java.lang.String android.bluetooth.BluetoothDevice.getAddress()' для ссылки на нулевой объект
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): на android.media.IAudioService $Stub $Proxy.startBluetoothSco(IAudioService.java:1587)
E/AndroidRuntime (31705): на android.media.AudioManager.startBluetoothSco(AudioManager.java:1468)
Если я нацелен на уровень API 17 или ниже на Android Lollipop, все работает так, как ожидалось.
Я считаю, что источником проблемы является изменение аудио-кода Android, которое произошло на уровне API 21 в файле AudioService.java, строка 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);
}
Похоже, SCO_MODE_UNDEFINED должен быть SCO_MODE_RAW. Если вы просмотрите файл, вы увидите, что SCO_MODE_RAW проверяется в нескольких местах, но никогда не передается нигде.
Кто-нибудь еще переживает этот крах? Кто-нибудь знает, что лучше исправить, чем понизить целевой SDK до 17? Если нет, можете ли вы, пожалуйста, опубликовать отчет об ошибке , который я подал в Google, чтобы увеличить вероятность того, что он будет выглядеть: -)
Ответы
Ответ 1
Как писал @xsveda, если нет подключенной гарнитуры, вы получите NPE на Lollipop.
Сначала вы можете проверить подключение Bluetooth-гарнитуры:
mAudioManager.isWiredHeadsetOn()
Как описано в doc isWiredHeadsetOn()
(ссылка doc) лишается и используется только для проверки подключения минигарнитуры или нет.
И после этого вы можете использовать соединение startBluetoothSco()
. Что касается меня, я использовал этот код:
Этот для начала:
if(mAudioManager.isWiredHeadsetOn())
mAudioManager.startBluetoothSco();
Этот для остановки:
if(mAudioManager.isBluetoothScoOn())
mAudioManager.stopBluetoothSco();
Надеюсь, что это поможет.
Ответ 2
После нескольких дней отчаяния я нашел простой способ:
startBluetoothSco()
выбрасывает NPE только в том случае, если нет подключенного устройства Bluetooth, поэтому его можно поймать и проигнорировать, так как "никто не разговаривает". Если подключена BT-гарнитура, SCO успешно запущен и воспроизведение работает!
Ответ 3
В настоящее время, похоже, для меня работает игнорирование 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, это работает для меня и прокладывает через микрофон Bluetooth, убедитесь, что устройство Bluetooth распознано и подключено:
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));
}
Надеюсь, это поможет кому-то!