Loading android/app/res/values/config.xml +1 −0 Original line number Diff line number Diff line Loading @@ -82,6 +82,7 @@ <integer name="a2dp_source_codec_priority_aptx_hd">4001</integer> <integer name="a2dp_source_codec_priority_ldac">5001</integer> <integer name="a2dp_source_codec_priority_lc3">6001</integer> <integer name="a2dp_source_codec_priority_opus">7001</integer> <!-- For enabling the AVRCP Target Cover Artowrk feature--> <bool name="avrcp_target_enable_cover_art">true</bool> Loading android/app/src/com/android/bluetooth/a2dp/A2dpCodecConfig.java +23 −5 Original line number Diff line number Diff line Loading @@ -37,6 +37,9 @@ class A2dpCodecConfig { private static final boolean DBG = true; private static final String TAG = "A2dpCodecConfig"; // TODO(b/240635097): remove in U private static final int SOURCE_CODEC_TYPE_OPUS = 6; private Context mContext; private A2dpNativeInterface mA2dpNativeInterface; Loading @@ -53,6 +56,8 @@ class A2dpCodecConfig { BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT; private @CodecPriority int mA2dpSourceCodecPriorityLc3 = BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT; private @CodecPriority int mA2dpSourceCodecPriorityOpus = BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT; private BluetoothCodecConfig[] mCodecConfigOffloading = new BluetoothCodecConfig[0]; Loading Loading @@ -243,6 +248,16 @@ class A2dpCodecConfig { mA2dpSourceCodecPriorityLc3 = value; } try { value = resources.getInteger(R.integer.a2dp_source_codec_priority_opus); } catch (NotFoundException e) { value = BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT; } if ((value >= BluetoothCodecConfig.CODEC_PRIORITY_DISABLED) && (value < BluetoothCodecConfig.CODEC_PRIORITY_HIGHEST)) { mA2dpSourceCodecPriorityOpus = value; } BluetoothCodecConfig codecConfig; BluetoothCodecConfig[] codecConfigArray = new BluetoothCodecConfig[6]; Loading Loading @@ -272,8 +287,9 @@ class A2dpCodecConfig { .build(); codecConfigArray[4] = codecConfig; codecConfig = new BluetoothCodecConfig.Builder() .setCodecType(BluetoothCodecConfig.SOURCE_CODEC_TYPE_LC3) .setCodecPriority(mA2dpSourceCodecPriorityLc3) // TODO(b/240635097): update in U .setCodecType(SOURCE_CODEC_TYPE_OPUS) .setCodecPriority(mA2dpSourceCodecPriorityOpus) .build(); codecConfigArray[5] = codecConfig; Loading @@ -282,14 +298,16 @@ class A2dpCodecConfig { public void switchCodecByBufferSize( BluetoothDevice device, boolean isLowLatency, int currentCodecType) { if ((isLowLatency && currentCodecType == BluetoothCodecConfig.SOURCE_CODEC_TYPE_LC3) || (!isLowLatency && currentCodecType != BluetoothCodecConfig.SOURCE_CODEC_TYPE_LC3)) { // TODO(b/240635097): update in U if ((isLowLatency && currentCodecType == SOURCE_CODEC_TYPE_OPUS) || (!isLowLatency && currentCodecType != SOURCE_CODEC_TYPE_OPUS)) { return; } BluetoothCodecConfig[] codecConfigArray = assignCodecConfigPriorities(); for (int i = 0; i < codecConfigArray.length; i++){ BluetoothCodecConfig codecConfig = codecConfigArray[i]; if (codecConfig.getCodecType() == BluetoothCodecConfig.SOURCE_CODEC_TYPE_LC3) { // TODO(b/240635097): update in U if (codecConfig.getCodecType() == SOURCE_CODEC_TYPE_OPUS) { if (isLowLatency) { codecConfig.setCodecPriority(BluetoothCodecConfig.CODEC_PRIORITY_HIGHEST); } else { Loading android/app/src/com/android/bluetooth/a2dp/A2dpService.java +37 −0 Original line number Diff line number Diff line Loading @@ -75,6 +75,9 @@ public class A2dpService extends ProfileService { private static final boolean DBG = true; private static final String TAG = "A2dpService"; // TODO(b/240635097): remove in U private static final int SOURCE_CODEC_TYPE_OPUS = 6; private static A2dpService sA2dpService; private AdapterService mAdapterService; Loading Loading @@ -599,6 +602,7 @@ public class A2dpService extends ProfileService { // This needs to happen before we inform the audio manager that the device // disconnected. Please see comment in updateAndBroadcastActiveDevice() for why. updateAndBroadcastActiveDevice(device); updateLowLatencyAudioSupport(device); BluetoothDevice newActiveDevice = null; synchronized (mStateMachines) { Loading Loading @@ -818,6 +822,7 @@ public class A2dpService extends ProfileService { Log.e(TAG, "enableOptionalCodecs: Codec status is null"); return; } updateLowLatencyAudioSupport(device); mA2dpCodecConfig.enableOptionalCodecs(device, codecStatus.getCodecConfig()); } Loading Loading @@ -848,6 +853,7 @@ public class A2dpService extends ProfileService { Log.e(TAG, "disableOptionalCodecs: Codec status is null"); return; } updateLowLatencyAudioSupport(device); mA2dpCodecConfig.disableOptionalCodecs(device, codecStatus.getCodecConfig()); } Loading Loading @@ -1207,6 +1213,34 @@ public class A2dpService extends ProfileService { } } /** * Check for low-latency codec support and inform AdapterService * * @param device device whose audio low latency will be allowed or disallowed */ @VisibleForTesting public void updateLowLatencyAudioSupport(BluetoothDevice device) { synchronized (mStateMachines) { A2dpStateMachine sm = mStateMachines.get(device); if (sm == null) { return; } BluetoothCodecStatus codecStatus = sm.getCodecStatus(); boolean lowLatencyAudioAllow = false; BluetoothCodecConfig lowLatencyCodec = new BluetoothCodecConfig.Builder() .setCodecType(SOURCE_CODEC_TYPE_OPUS) // remove in U .build(); if (codecStatus != null && codecStatus.isCodecConfigSelectable(lowLatencyCodec) && getOptionalCodecsEnabled(device) == BluetoothA2dp.OPTIONAL_CODECS_PREF_ENABLED) { lowLatencyAudioAllow = true; } mAdapterService.allowLowLatencyAudio(lowLatencyAudioAllow, device); } } private void connectionStateChanged(BluetoothDevice device, int fromState, int toState) { if ((device == null) || (fromState == toState)) { return; Loading Loading @@ -1637,6 +1671,9 @@ public class A2dpService extends ProfileService { } public void switchCodecByBufferSize(BluetoothDevice device, boolean isLowLatency) { if (getOptionalCodecsEnabled(device) != BluetoothA2dp.OPTIONAL_CODECS_PREF_ENABLED) { return; } mA2dpCodecConfig.switchCodecByBufferSize( device, isLowLatency, getCodecStatus(device).getCodecConfig().getCodecType()); } Loading android/app/src/com/android/bluetooth/a2dp/A2dpStateMachine.java +12 −0 Original line number Diff line number Diff line Loading @@ -76,6 +76,9 @@ final class A2dpStateMachine extends StateMachine { private static final boolean DBG = true; private static final String TAG = "A2dpStateMachine"; // TODO(b/240635097): remove in U private static final int SOURCE_CODEC_TYPE_OPUS = 6; static final int CONNECT = 1; static final int DISCONNECT = 2; @VisibleForTesting Loading Loading @@ -480,6 +483,7 @@ final class A2dpStateMachine extends StateMachine { // codecs (perhaps it's had a firmware update, etc.) and save that state if // it differs from what we had saved before. mA2dpService.updateOptionalCodecsSupport(mDevice); mA2dpService.updateLowLatencyAudioSupport(mDevice); broadcastConnectionState(mConnectionState, mLastConnectionState); // Upon connected, the audio starts out as stopped broadcastAudioState(BluetoothA2dp.STATE_NOT_PLAYING, Loading Loading @@ -652,6 +656,7 @@ final class A2dpStateMachine extends StateMachine { // for this codec change event. mA2dpService.updateOptionalCodecsSupport(mDevice); } mA2dpService.updateLowLatencyAudioSupport(mDevice); if (mA2dpOffloadEnabled) { boolean update = false; BluetoothCodecConfig newCodecConfig = mCodecStatus.getCodecConfig(); Loading @@ -666,6 +671,13 @@ final class A2dpStateMachine extends StateMachine { && (prevCodecConfig.getCodecSpecific1() != newCodecConfig.getCodecSpecific1())) { update = true; } else if ((newCodecConfig.getCodecType() == SOURCE_CODEC_TYPE_OPUS) // TODO(b/240635097): update in U && (prevCodecConfig != null) // check framesize field && (prevCodecConfig.getCodecSpecific1() != newCodecConfig.getCodecSpecific1())) { update = true; } if (update) { mA2dpService.codecConfigUpdated(mDevice, mCodecStatus, false); Loading android/app/src/com/android/bluetooth/btservice/AdapterService.java +18 −1 Original line number Diff line number Diff line Loading @@ -168,6 +168,7 @@ public class AdapterService extends Service { private static final int MIN_OFFLOADED_FILTERS = 10; private static final int MIN_OFFLOADED_SCAN_STORAGE_BYTES = 1024; private static final Duration PENDING_SOCKET_HANDOFF_TIMEOUT = Duration.ofMinutes(1); private static final Duration GENERATE_LOCAL_OOB_DATA_TIMEOUT = Duration.ofSeconds(2); private final Object mEnergyInfoLock = new Object(); private int mStackReportedState; Loading Loading @@ -4204,15 +4205,31 @@ public class AdapterService extends Service { if (mOobDataCallbackQueue.peek() != null) { try { callback.onError(BluetoothStatusCodes.ERROR_ANOTHER_ACTIVE_OOB_REQUEST); return; } catch (RemoteException e) { Log.e(TAG, "Failed to make callback", e); } return; } mOobDataCallbackQueue.offer(callback); mHandler.postDelayed(() -> removeFromOobDataCallbackQueue(callback), GENERATE_LOCAL_OOB_DATA_TIMEOUT.toMillis()); generateLocalOobDataNative(transport); } private synchronized void removeFromOobDataCallbackQueue(IBluetoothOobDataCallback callback) { if (callback == null) { return; } if (mOobDataCallbackQueue.peek() == callback) { try { mOobDataCallbackQueue.poll().onError(BluetoothStatusCodes.ERROR_UNKNOWN); } catch (RemoteException e) { Log.e(TAG, "Failed to make OobDataCallback to remove callback from queue", e); } } } /* package */ synchronized void notifyOobDataCallback(int transport, OobData oobData) { if (mOobDataCallbackQueue.peek() == null) { Log.e(TAG, "Failed to make callback, no callback exists"); Loading Loading
android/app/res/values/config.xml +1 −0 Original line number Diff line number Diff line Loading @@ -82,6 +82,7 @@ <integer name="a2dp_source_codec_priority_aptx_hd">4001</integer> <integer name="a2dp_source_codec_priority_ldac">5001</integer> <integer name="a2dp_source_codec_priority_lc3">6001</integer> <integer name="a2dp_source_codec_priority_opus">7001</integer> <!-- For enabling the AVRCP Target Cover Artowrk feature--> <bool name="avrcp_target_enable_cover_art">true</bool> Loading
android/app/src/com/android/bluetooth/a2dp/A2dpCodecConfig.java +23 −5 Original line number Diff line number Diff line Loading @@ -37,6 +37,9 @@ class A2dpCodecConfig { private static final boolean DBG = true; private static final String TAG = "A2dpCodecConfig"; // TODO(b/240635097): remove in U private static final int SOURCE_CODEC_TYPE_OPUS = 6; private Context mContext; private A2dpNativeInterface mA2dpNativeInterface; Loading @@ -53,6 +56,8 @@ class A2dpCodecConfig { BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT; private @CodecPriority int mA2dpSourceCodecPriorityLc3 = BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT; private @CodecPriority int mA2dpSourceCodecPriorityOpus = BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT; private BluetoothCodecConfig[] mCodecConfigOffloading = new BluetoothCodecConfig[0]; Loading Loading @@ -243,6 +248,16 @@ class A2dpCodecConfig { mA2dpSourceCodecPriorityLc3 = value; } try { value = resources.getInteger(R.integer.a2dp_source_codec_priority_opus); } catch (NotFoundException e) { value = BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT; } if ((value >= BluetoothCodecConfig.CODEC_PRIORITY_DISABLED) && (value < BluetoothCodecConfig.CODEC_PRIORITY_HIGHEST)) { mA2dpSourceCodecPriorityOpus = value; } BluetoothCodecConfig codecConfig; BluetoothCodecConfig[] codecConfigArray = new BluetoothCodecConfig[6]; Loading Loading @@ -272,8 +287,9 @@ class A2dpCodecConfig { .build(); codecConfigArray[4] = codecConfig; codecConfig = new BluetoothCodecConfig.Builder() .setCodecType(BluetoothCodecConfig.SOURCE_CODEC_TYPE_LC3) .setCodecPriority(mA2dpSourceCodecPriorityLc3) // TODO(b/240635097): update in U .setCodecType(SOURCE_CODEC_TYPE_OPUS) .setCodecPriority(mA2dpSourceCodecPriorityOpus) .build(); codecConfigArray[5] = codecConfig; Loading @@ -282,14 +298,16 @@ class A2dpCodecConfig { public void switchCodecByBufferSize( BluetoothDevice device, boolean isLowLatency, int currentCodecType) { if ((isLowLatency && currentCodecType == BluetoothCodecConfig.SOURCE_CODEC_TYPE_LC3) || (!isLowLatency && currentCodecType != BluetoothCodecConfig.SOURCE_CODEC_TYPE_LC3)) { // TODO(b/240635097): update in U if ((isLowLatency && currentCodecType == SOURCE_CODEC_TYPE_OPUS) || (!isLowLatency && currentCodecType != SOURCE_CODEC_TYPE_OPUS)) { return; } BluetoothCodecConfig[] codecConfigArray = assignCodecConfigPriorities(); for (int i = 0; i < codecConfigArray.length; i++){ BluetoothCodecConfig codecConfig = codecConfigArray[i]; if (codecConfig.getCodecType() == BluetoothCodecConfig.SOURCE_CODEC_TYPE_LC3) { // TODO(b/240635097): update in U if (codecConfig.getCodecType() == SOURCE_CODEC_TYPE_OPUS) { if (isLowLatency) { codecConfig.setCodecPriority(BluetoothCodecConfig.CODEC_PRIORITY_HIGHEST); } else { Loading
android/app/src/com/android/bluetooth/a2dp/A2dpService.java +37 −0 Original line number Diff line number Diff line Loading @@ -75,6 +75,9 @@ public class A2dpService extends ProfileService { private static final boolean DBG = true; private static final String TAG = "A2dpService"; // TODO(b/240635097): remove in U private static final int SOURCE_CODEC_TYPE_OPUS = 6; private static A2dpService sA2dpService; private AdapterService mAdapterService; Loading Loading @@ -599,6 +602,7 @@ public class A2dpService extends ProfileService { // This needs to happen before we inform the audio manager that the device // disconnected. Please see comment in updateAndBroadcastActiveDevice() for why. updateAndBroadcastActiveDevice(device); updateLowLatencyAudioSupport(device); BluetoothDevice newActiveDevice = null; synchronized (mStateMachines) { Loading Loading @@ -818,6 +822,7 @@ public class A2dpService extends ProfileService { Log.e(TAG, "enableOptionalCodecs: Codec status is null"); return; } updateLowLatencyAudioSupport(device); mA2dpCodecConfig.enableOptionalCodecs(device, codecStatus.getCodecConfig()); } Loading Loading @@ -848,6 +853,7 @@ public class A2dpService extends ProfileService { Log.e(TAG, "disableOptionalCodecs: Codec status is null"); return; } updateLowLatencyAudioSupport(device); mA2dpCodecConfig.disableOptionalCodecs(device, codecStatus.getCodecConfig()); } Loading Loading @@ -1207,6 +1213,34 @@ public class A2dpService extends ProfileService { } } /** * Check for low-latency codec support and inform AdapterService * * @param device device whose audio low latency will be allowed or disallowed */ @VisibleForTesting public void updateLowLatencyAudioSupport(BluetoothDevice device) { synchronized (mStateMachines) { A2dpStateMachine sm = mStateMachines.get(device); if (sm == null) { return; } BluetoothCodecStatus codecStatus = sm.getCodecStatus(); boolean lowLatencyAudioAllow = false; BluetoothCodecConfig lowLatencyCodec = new BluetoothCodecConfig.Builder() .setCodecType(SOURCE_CODEC_TYPE_OPUS) // remove in U .build(); if (codecStatus != null && codecStatus.isCodecConfigSelectable(lowLatencyCodec) && getOptionalCodecsEnabled(device) == BluetoothA2dp.OPTIONAL_CODECS_PREF_ENABLED) { lowLatencyAudioAllow = true; } mAdapterService.allowLowLatencyAudio(lowLatencyAudioAllow, device); } } private void connectionStateChanged(BluetoothDevice device, int fromState, int toState) { if ((device == null) || (fromState == toState)) { return; Loading Loading @@ -1637,6 +1671,9 @@ public class A2dpService extends ProfileService { } public void switchCodecByBufferSize(BluetoothDevice device, boolean isLowLatency) { if (getOptionalCodecsEnabled(device) != BluetoothA2dp.OPTIONAL_CODECS_PREF_ENABLED) { return; } mA2dpCodecConfig.switchCodecByBufferSize( device, isLowLatency, getCodecStatus(device).getCodecConfig().getCodecType()); } Loading
android/app/src/com/android/bluetooth/a2dp/A2dpStateMachine.java +12 −0 Original line number Diff line number Diff line Loading @@ -76,6 +76,9 @@ final class A2dpStateMachine extends StateMachine { private static final boolean DBG = true; private static final String TAG = "A2dpStateMachine"; // TODO(b/240635097): remove in U private static final int SOURCE_CODEC_TYPE_OPUS = 6; static final int CONNECT = 1; static final int DISCONNECT = 2; @VisibleForTesting Loading Loading @@ -480,6 +483,7 @@ final class A2dpStateMachine extends StateMachine { // codecs (perhaps it's had a firmware update, etc.) and save that state if // it differs from what we had saved before. mA2dpService.updateOptionalCodecsSupport(mDevice); mA2dpService.updateLowLatencyAudioSupport(mDevice); broadcastConnectionState(mConnectionState, mLastConnectionState); // Upon connected, the audio starts out as stopped broadcastAudioState(BluetoothA2dp.STATE_NOT_PLAYING, Loading Loading @@ -652,6 +656,7 @@ final class A2dpStateMachine extends StateMachine { // for this codec change event. mA2dpService.updateOptionalCodecsSupport(mDevice); } mA2dpService.updateLowLatencyAudioSupport(mDevice); if (mA2dpOffloadEnabled) { boolean update = false; BluetoothCodecConfig newCodecConfig = mCodecStatus.getCodecConfig(); Loading @@ -666,6 +671,13 @@ final class A2dpStateMachine extends StateMachine { && (prevCodecConfig.getCodecSpecific1() != newCodecConfig.getCodecSpecific1())) { update = true; } else if ((newCodecConfig.getCodecType() == SOURCE_CODEC_TYPE_OPUS) // TODO(b/240635097): update in U && (prevCodecConfig != null) // check framesize field && (prevCodecConfig.getCodecSpecific1() != newCodecConfig.getCodecSpecific1())) { update = true; } if (update) { mA2dpService.codecConfigUpdated(mDevice, mCodecStatus, false); Loading
android/app/src/com/android/bluetooth/btservice/AdapterService.java +18 −1 Original line number Diff line number Diff line Loading @@ -168,6 +168,7 @@ public class AdapterService extends Service { private static final int MIN_OFFLOADED_FILTERS = 10; private static final int MIN_OFFLOADED_SCAN_STORAGE_BYTES = 1024; private static final Duration PENDING_SOCKET_HANDOFF_TIMEOUT = Duration.ofMinutes(1); private static final Duration GENERATE_LOCAL_OOB_DATA_TIMEOUT = Duration.ofSeconds(2); private final Object mEnergyInfoLock = new Object(); private int mStackReportedState; Loading Loading @@ -4204,15 +4205,31 @@ public class AdapterService extends Service { if (mOobDataCallbackQueue.peek() != null) { try { callback.onError(BluetoothStatusCodes.ERROR_ANOTHER_ACTIVE_OOB_REQUEST); return; } catch (RemoteException e) { Log.e(TAG, "Failed to make callback", e); } return; } mOobDataCallbackQueue.offer(callback); mHandler.postDelayed(() -> removeFromOobDataCallbackQueue(callback), GENERATE_LOCAL_OOB_DATA_TIMEOUT.toMillis()); generateLocalOobDataNative(transport); } private synchronized void removeFromOobDataCallbackQueue(IBluetoothOobDataCallback callback) { if (callback == null) { return; } if (mOobDataCallbackQueue.peek() == callback) { try { mOobDataCallbackQueue.poll().onError(BluetoothStatusCodes.ERROR_UNKNOWN); } catch (RemoteException e) { Log.e(TAG, "Failed to make OobDataCallback to remove callback from queue", e); } } } /* package */ synchronized void notifyOobDataCallback(int transport, OobData oobData) { if (mOobDataCallbackQueue.peek() == null) { Log.e(TAG, "Failed to make callback, no callback exists"); Loading