Loading android/app/src/com/android/bluetooth/le_audio/LeAudioService.java +125 −14 Original line number Original line Diff line number Diff line Loading @@ -3019,7 +3019,11 @@ public class LeAudioService extends ProfileService { mDialingOutTimeoutEvent = null; mDialingOutTimeoutEvent = null; } } void notifyAudioFrameworkForCodecConfigUpdate(int groupId, LeAudioGroupDescriptor descriptor) { void notifyAudioFrameworkForCodecConfigUpdate( int groupId, LeAudioGroupDescriptor descriptor, boolean outputCodecOrFreqChanged, boolean inputCodecOrFreqChanged) { Log.i(TAG, "notifyAudioFrameworkForCodecConfigUpdate groupId: " + groupId); Log.i(TAG, "notifyAudioFrameworkForCodecConfigUpdate groupId: " + groupId); if (!Flags.leaudioCodecConfigCallbackOrderFix()) { if (!Flags.leaudioCodecConfigCallbackOrderFix()) { Loading @@ -3027,7 +3031,7 @@ public class LeAudioService extends ProfileService { return; return; } } if (mActiveAudioOutDevice != null) { if (mActiveAudioOutDevice != null && outputCodecOrFreqChanged) { int volume = getAudioDeviceGroupVolume(groupId); int volume = getAudioDeviceGroupVolume(groupId); final BluetoothProfileConnectionInfo connectionInfo; final BluetoothProfileConnectionInfo connectionInfo; Loading @@ -3042,14 +3046,115 @@ public class LeAudioService extends ProfileService { mActiveAudioOutDevice, mActiveAudioOutDevice, connectionInfo); mActiveAudioOutDevice, mActiveAudioOutDevice, connectionInfo); } } if (mActiveAudioInDevice != null) { if (mActiveAudioInDevice != null && inputCodecOrFreqChanged) { mAudioManager.handleBluetoothActiveDeviceChanged( mAudioManager.handleBluetoothActiveDeviceChanged( mActiveAudioOutDevice, mActiveAudioInDevice, mActiveAudioOutDevice, mActiveAudioInDevice, BluetoothProfileConnectionInfo.createLeAudioInfo(false, false)); BluetoothProfileConnectionInfo.createLeAudioInfo(false, false)); } } } } boolean isOutputCodecOfSampleFrequencyChanged( BluetoothLeAudioCodecStatus previous, BluetoothLeAudioCodecStatus next) { if ((previous == null) && (next == null)) { return false; } if ((previous == null) || (next == null)) { Log.d(TAG, previous + " != " + next); return true; } if ((previous.getOutputCodecConfig() == null) && (next.getOutputCodecConfig() == null)) { /* Nothing changed here.*/ return false; } if ((previous.getOutputCodecConfig() == null || next.getOutputCodecConfig() == null)) { Log.d( TAG, "New output codec: " + (previous.getOutputCodecConfig() + " != " + next.getOutputCodecConfig())); return true; } if (previous.getOutputCodecConfig().getCodecType() != next.getOutputCodecConfig().getCodecType()) { Log.d( TAG, "Different output codec type: " + (previous.getOutputCodecConfig().getCodecType() + " != " + next.getOutputCodecConfig().getCodecType())); return true; } if (previous.getOutputCodecConfig().getSampleRate() != next.getOutputCodecConfig().getSampleRate()) { Log.d( TAG, "Different output samplerate: " + (previous.getOutputCodecConfig().getSampleRate() + " != " + next.getOutputCodecConfig().getSampleRate())); return true; } return false; } boolean isInputCodecOfSampleFrequencyChanged( BluetoothLeAudioCodecStatus previous, BluetoothLeAudioCodecStatus next) { if ((previous == null) && (next == null)) { return false; } if ((previous == null) || (next == null)) { Log.d(TAG, previous + " != " + next); return true; } if ((previous.getInputCodecConfig() == null) && (next.getInputCodecConfig() == null)) { /* Nothing changed here.*/ return false; } if ((previous.getInputCodecConfig() == null) || (next.getInputCodecConfig() == null)) { Log.d( TAG, "New input codec: " + (previous.getInputCodecConfig() + " != " + next.getInputCodecConfig())); return true; } if (previous.getInputCodecConfig().getCodecType() != next.getInputCodecConfig().getCodecType()) { Log.d( TAG, "Different input codec type: " + (previous.getInputCodecConfig().getCodecType() + " != " + next.getInputCodecConfig().getCodecType())); return true; } if (previous.getInputCodecConfig().getSampleRate() != next.getInputCodecConfig().getSampleRate()) { Log.d( TAG, "Different input samplerate: " + (previous.getInputCodecConfig().getSampleRate() + " != " + next.getInputCodecConfig().getSampleRate())); return true; } return false; } // Suppressed since this is part of a local process // Suppressed since this is part of a local process @SuppressLint("AndroidFrameworkRequiresPermission") @SuppressLint("AndroidFrameworkRequiresPermission") void messageFromNative(LeAudioStackEvent stackEvent) { void messageFromNative(LeAudioStackEvent stackEvent) { Loading Loading @@ -3199,18 +3304,24 @@ public class LeAudioService extends ProfileService { descriptor.mInputSelectableConfig, descriptor.mInputSelectableConfig, descriptor.mOutputSelectableConfig); descriptor.mOutputSelectableConfig); if (descriptor.mCodecStatus != null) { boolean outputCodecOrFreqChanged = Log.d(TAG, " Replacing codec status for group: " + groupId); isOutputCodecOfSampleFrequencyChanged(descriptor.mCodecStatus, status); } else { boolean inputCodecOrFreqChanged = Log.d(TAG, " New codec status for group: " + groupId); isInputCodecOfSampleFrequencyChanged(descriptor.mCodecStatus, status); } Log.d( TAG, ("Codec update for group:" + groupId) + (", outputCodecOrFreqChanged: " + outputCodecOrFreqChanged) + (", inputCodecOrFreqChanged: " + inputCodecOrFreqChanged)); descriptor.mCodecStatus = status; descriptor.mCodecStatus = status; mHandler.post(() -> notifyUnicastCodecConfigChanged(groupId, status)); mHandler.post(() -> notifyUnicastCodecConfigChanged(groupId, status)); if (descriptor.isActive()) { if (descriptor.isActive() && (outputCodecOrFreqChanged || inputCodecOrFreqChanged)) { // Audio framework needs to be notified so it get new codec config // Audio framework needs to be notified so it get new codec config notifyAudioFrameworkForCodecConfigUpdate(groupId, descriptor); notifyAudioFrameworkForCodecConfigUpdate( groupId, descriptor, outputCodecOrFreqChanged, inputCodecOrFreqChanged); } } } else if (stackEvent.type == LeAudioStackEvent.EVENT_TYPE_AUDIO_CONF_CHANGED) { } else if (stackEvent.type == LeAudioStackEvent.EVENT_TYPE_AUDIO_CONF_CHANGED) { int direction = stackEvent.valueInt1; int direction = stackEvent.valueInt1; Loading android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioServiceTest.java +123 −2 Original line number Original line Diff line number Diff line Loading @@ -52,8 +52,8 @@ import android.media.BluetoothProfileConnectionInfo; import android.os.Handler; import android.os.Handler; import android.os.Looper; import android.os.Looper; import android.os.ParcelUuid; import android.os.ParcelUuid; import android.sysprop.BluetoothProperties; import android.platform.test.flag.junit.SetFlagsRule; import android.platform.test.flag.junit.SetFlagsRule; import android.sysprop.BluetoothProperties; import androidx.test.filters.MediumTest; import androidx.test.filters.MediumTest; import androidx.test.platform.app.InstrumentationRegistry; import androidx.test.platform.app.InstrumentationRegistry; Loading Loading @@ -2171,7 +2171,8 @@ public class LeAudioServiceTest { /** Test native interface group status message handling */ /** Test native interface group status message handling */ @Test @Test public void testMessageFromNativeGroupCodecConfigChanged() { public void testMessageFromNativeGroupCodecConfigChangedNonActiveDevice() { mSetFlagsRule.enableFlags(Flags.FLAG_LEAUDIO_CODEC_CONFIG_CALLBACK_ORDER_FIX); onGroupCodecConfChangedCallbackCalled = false; onGroupCodecConfChangedCallbackCalled = false; injectLocalCodecConfigCapaChanged(INPUT_CAPABILITIES_CONFIG, OUTPUT_CAPABILITIES_CONFIG); injectLocalCodecConfigCapaChanged(INPUT_CAPABILITIES_CONFIG, OUTPUT_CAPABILITIES_CONFIG); Loading Loading @@ -2213,15 +2214,135 @@ public class LeAudioServiceTest { injectGroupSelectableCodecConfigChanged( injectGroupSelectableCodecConfigChanged( testGroupId, INPUT_SELECTABLE_CONFIG, OUTPUT_SELECTABLE_CONFIG); testGroupId, INPUT_SELECTABLE_CONFIG, OUTPUT_SELECTABLE_CONFIG); // Inject configuration and check that AF is NOT notified. injectGroupCurrentCodecConfigChanged(testGroupId, LC3_16KHZ_CONFIG, LC3_48KHZ_CONFIG); injectGroupCurrentCodecConfigChanged(testGroupId, LC3_16KHZ_CONFIG, LC3_48KHZ_CONFIG); TestUtils.waitForLooperToFinishScheduledTask(mService.getMainLooper()); TestUtils.waitForLooperToFinishScheduledTask(mService.getMainLooper()); assertThat(onGroupCodecConfChangedCallbackCalled).isTrue(); assertThat(onGroupCodecConfChangedCallbackCalled).isTrue(); verify(mAudioManager, times(0)) .handleBluetoothActiveDeviceChanged( any(), any(), any(BluetoothProfileConnectionInfo.class)); onGroupCodecConfChangedCallbackCalled = false; // Now inject again new configuration and check that AF is not notified. testCodecStatus = new BluetoothLeAudioCodecStatus( LC3_16KHZ_CONFIG, LC3_16KHZ_CONFIG, INPUT_CAPABILITIES_CONFIG, OUTPUT_CAPABILITIES_CONFIG, INPUT_SELECTABLE_CONFIG, OUTPUT_SELECTABLE_CONFIG); injectGroupCurrentCodecConfigChanged(testGroupId, LC3_16KHZ_CONFIG, LC3_16KHZ_CONFIG); TestUtils.waitForLooperToFinishScheduledTask(mService.getMainLooper()); assertThat(onGroupCodecConfChangedCallbackCalled).isTrue(); verify(mAudioManager, times(0)) .handleBluetoothActiveDeviceChanged( any(), any(), any(BluetoothProfileConnectionInfo.class)); onGroupCodecConfChangedCallbackCalled = false; onGroupCodecConfChangedCallbackCalled = false; mService.mLeAudioCallbacks.unregister(leAudioCallbacks); mService.mLeAudioCallbacks.unregister(leAudioCallbacks); } } /** Test native interface group status message handling */ @Test public void testMessageFromNativeGroupCodecConfigChangedActiveDevice_DifferentConfiguration() { mSetFlagsRule.enableFlags(Flags.FLAG_LEAUDIO_CODEC_CONFIG_CALLBACK_ORDER_FIX); onGroupCodecConfChangedCallbackCalled = false; injectLocalCodecConfigCapaChanged(INPUT_CAPABILITIES_CONFIG, OUTPUT_CAPABILITIES_CONFIG); doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class)); connectTestDevice(mSingleDevice, testGroupId); testCodecStatus = new BluetoothLeAudioCodecStatus( LC3_16KHZ_CONFIG, LC3_48KHZ_CONFIG, INPUT_CAPABILITIES_CONFIG, OUTPUT_CAPABILITIES_CONFIG, INPUT_SELECTABLE_CONFIG, OUTPUT_SELECTABLE_CONFIG); IBluetoothLeAudioCallback leAudioCallbacks = new IBluetoothLeAudioCallback.Stub() { @Override public void onCodecConfigChanged(int gid, BluetoothLeAudioCodecStatus status) { onGroupCodecConfChangedCallbackCalled = true; assertThat(status.equals(testCodecStatus)).isTrue(); } @Override public void onGroupStatusChanged(int gid, int gStatus) {} @Override public void onGroupNodeAdded(BluetoothDevice device, int gid) {} @Override public void onGroupNodeRemoved(BluetoothDevice device, int gid) {} @Override public void onGroupStreamStatusChanged(int groupId, int groupStreamStatus) {} }; synchronized (this.mService.mLeAudioCallbacks) { mService.mLeAudioCallbacks.register(leAudioCallbacks); } injectGroupSelectableCodecConfigChanged( testGroupId, INPUT_SELECTABLE_CONFIG, OUTPUT_SELECTABLE_CONFIG); injectGroupCurrentCodecConfigChanged(testGroupId, LC3_16KHZ_CONFIG, LC3_48KHZ_CONFIG); injectAudioConfChanged( testGroupId, BluetoothLeAudio.CONTEXT_TYPE_MEDIA | BluetoothLeAudio.CONTEXT_TYPE_CONVERSATIONAL, 3); injectGroupStatusChange(testGroupId, LeAudioStackEvent.GROUP_STATUS_ACTIVE); TestUtils.waitForLooperToFinishScheduledTask(mService.getMainLooper()); assertThat(onGroupCodecConfChangedCallbackCalled).isTrue(); verify(mAudioManager, times(2)) .handleBluetoothActiveDeviceChanged( any(), any(), any(BluetoothProfileConnectionInfo.class)); onGroupCodecConfChangedCallbackCalled = false; reset(mAudioManager); // Now inject configuration different sample rate on one direction testCodecStatus = new BluetoothLeAudioCodecStatus( LC3_16KHZ_CONFIG, LC3_16KHZ_CONFIG, INPUT_CAPABILITIES_CONFIG, OUTPUT_CAPABILITIES_CONFIG, INPUT_SELECTABLE_CONFIG, OUTPUT_SELECTABLE_CONFIG); injectGroupCurrentCodecConfigChanged(testGroupId, LC3_16KHZ_CONFIG, LC3_16KHZ_CONFIG); TestUtils.waitForLooperToFinishScheduledTask(mService.getMainLooper()); assertThat(onGroupCodecConfChangedCallbackCalled).isTrue(); verify(mAudioManager, times(1)) .handleBluetoothActiveDeviceChanged( any(), any(), any(BluetoothProfileConnectionInfo.class)); synchronized (this.mService.mLeAudioCallbacks) { mService.mLeAudioCallbacks.unregister(leAudioCallbacks); } onGroupCodecConfChangedCallbackCalled = false; reset(mAudioManager); } /** Test native interface group status message handling */ /** Test native interface group status message handling */ @Test @Test public void testMessageFromNativeGroupCodecConfigChanged_OneDirectionOnly() { public void testMessageFromNativeGroupCodecConfigChanged_OneDirectionOnly() { Loading system/bta/le_audio/client.cc +12 −12 Original line number Original line Diff line number Diff line Loading @@ -856,13 +856,15 @@ public: ccid_contexts.source.unset(LeAudioContextType::CONVERSATIONAL); ccid_contexts.source.unset(LeAudioContextType::CONVERSATIONAL); } } bool group_is_streaming = group->IsStreaming(); BidirectionalPair<std::vector<uint8_t>> ccids = { BidirectionalPair<std::vector<uint8_t>> ccids = { .sink = ContentControlIdKeeper::GetInstance()->GetAllCcids(ccid_contexts.sink), .sink = ContentControlIdKeeper::GetInstance()->GetAllCcids(ccid_contexts.sink), .source = ContentControlIdKeeper::GetInstance()->GetAllCcids(ccid_contexts.source)}; .source = ContentControlIdKeeper::GetInstance()->GetAllCcids(ccid_contexts.source)}; if (group->IsPendingConfiguration()) { if (group->IsPendingConfiguration()) { return groupStateMachine_->ConfigureStream(group, configuration_context_type_, return groupStateMachine_->ConfigureStream(group, configuration_context_type_, remote_contexts, ccids); remote_contexts, ccids); } else if (group->GetState() != AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) { } else if (!group_is_streaming) { stream_setup_start_timestamp_ = bluetooth::common::time_get_os_boottime_us(); stream_setup_start_timestamp_ = bluetooth::common::time_get_os_boottime_us(); } } Loading @@ -870,7 +872,7 @@ public: * when there would be request to stream unicast. * when there would be request to stream unicast. */ */ if (com::android::bluetooth::flags::leaudio_broadcast_audio_handover_policies() && if (com::android::bluetooth::flags::leaudio_broadcast_audio_handover_policies() && !sink_monitor_mode_ && source_monitor_mode_ && !group->IsStreaming()) { !sink_monitor_mode_ && source_monitor_mode_ && !group_is_streaming) { callbacks_->OnUnicastMonitorModeStatus(bluetooth::le_audio::types::kLeAudioDirectionSource, callbacks_->OnUnicastMonitorModeStatus(bluetooth::le_audio::types::kLeAudioDirectionSource, UnicastMonitorModeStatus::STREAMING_REQUESTED); UnicastMonitorModeStatus::STREAMING_REQUESTED); } } Loading @@ -878,6 +880,13 @@ public: bool result = groupStateMachine_->StartStream(group, configuration_context_type, bool result = groupStateMachine_->StartStream(group, configuration_context_type, remote_contexts, ccids); remote_contexts, ccids); if (result && !group_is_streaming) { /* Notify Java about new configuration when start stream has been accepted and * it is not metadata update */ SendAudioGroupCurrentCodecConfigChanged(group); } return result; return result; } } Loading Loading @@ -4374,6 +4383,7 @@ public: /* Need to reconfigure stream */ /* Need to reconfigure stream */ group->SetPendingConfiguration(); group->SetPendingConfiguration(); groupStateMachine_->StopStream(group); groupStateMachine_->StopStream(group); SendAudioGroupCurrentCodecConfigChanged(group); return true; return true; } } Loading Loading @@ -5280,22 +5290,12 @@ public: std::bind(&LeAudioClientImpl::UpdateAudioConfigToHal, weak_factory_.GetWeakPtr(), std::bind(&LeAudioClientImpl::UpdateAudioConfigToHal, weak_factory_.GetWeakPtr(), std::placeholders::_1, std::placeholders::_2)); std::placeholders::_1, std::placeholders::_2)); /* When at least one direction is started we can assume new * configuration here */ bool new_configuration = false; if (audio_sender_state_ == AudioState::READY_TO_START) { if (audio_sender_state_ == AudioState::READY_TO_START) { StartSendingAudio(group_id); StartSendingAudio(group_id); new_configuration = true; } } if (audio_receiver_state_ == AudioState::READY_TO_START) { if (audio_receiver_state_ == AudioState::READY_TO_START) { StartReceivingAudio(group_id); StartReceivingAudio(group_id); new_configuration = true; } if (new_configuration) { /* Notify Java about new configuration */ SendAudioGroupCurrentCodecConfigChanged(group); } } break; break; } } Loading Loading
android/app/src/com/android/bluetooth/le_audio/LeAudioService.java +125 −14 Original line number Original line Diff line number Diff line Loading @@ -3019,7 +3019,11 @@ public class LeAudioService extends ProfileService { mDialingOutTimeoutEvent = null; mDialingOutTimeoutEvent = null; } } void notifyAudioFrameworkForCodecConfigUpdate(int groupId, LeAudioGroupDescriptor descriptor) { void notifyAudioFrameworkForCodecConfigUpdate( int groupId, LeAudioGroupDescriptor descriptor, boolean outputCodecOrFreqChanged, boolean inputCodecOrFreqChanged) { Log.i(TAG, "notifyAudioFrameworkForCodecConfigUpdate groupId: " + groupId); Log.i(TAG, "notifyAudioFrameworkForCodecConfigUpdate groupId: " + groupId); if (!Flags.leaudioCodecConfigCallbackOrderFix()) { if (!Flags.leaudioCodecConfigCallbackOrderFix()) { Loading @@ -3027,7 +3031,7 @@ public class LeAudioService extends ProfileService { return; return; } } if (mActiveAudioOutDevice != null) { if (mActiveAudioOutDevice != null && outputCodecOrFreqChanged) { int volume = getAudioDeviceGroupVolume(groupId); int volume = getAudioDeviceGroupVolume(groupId); final BluetoothProfileConnectionInfo connectionInfo; final BluetoothProfileConnectionInfo connectionInfo; Loading @@ -3042,14 +3046,115 @@ public class LeAudioService extends ProfileService { mActiveAudioOutDevice, mActiveAudioOutDevice, connectionInfo); mActiveAudioOutDevice, mActiveAudioOutDevice, connectionInfo); } } if (mActiveAudioInDevice != null) { if (mActiveAudioInDevice != null && inputCodecOrFreqChanged) { mAudioManager.handleBluetoothActiveDeviceChanged( mAudioManager.handleBluetoothActiveDeviceChanged( mActiveAudioOutDevice, mActiveAudioInDevice, mActiveAudioOutDevice, mActiveAudioInDevice, BluetoothProfileConnectionInfo.createLeAudioInfo(false, false)); BluetoothProfileConnectionInfo.createLeAudioInfo(false, false)); } } } } boolean isOutputCodecOfSampleFrequencyChanged( BluetoothLeAudioCodecStatus previous, BluetoothLeAudioCodecStatus next) { if ((previous == null) && (next == null)) { return false; } if ((previous == null) || (next == null)) { Log.d(TAG, previous + " != " + next); return true; } if ((previous.getOutputCodecConfig() == null) && (next.getOutputCodecConfig() == null)) { /* Nothing changed here.*/ return false; } if ((previous.getOutputCodecConfig() == null || next.getOutputCodecConfig() == null)) { Log.d( TAG, "New output codec: " + (previous.getOutputCodecConfig() + " != " + next.getOutputCodecConfig())); return true; } if (previous.getOutputCodecConfig().getCodecType() != next.getOutputCodecConfig().getCodecType()) { Log.d( TAG, "Different output codec type: " + (previous.getOutputCodecConfig().getCodecType() + " != " + next.getOutputCodecConfig().getCodecType())); return true; } if (previous.getOutputCodecConfig().getSampleRate() != next.getOutputCodecConfig().getSampleRate()) { Log.d( TAG, "Different output samplerate: " + (previous.getOutputCodecConfig().getSampleRate() + " != " + next.getOutputCodecConfig().getSampleRate())); return true; } return false; } boolean isInputCodecOfSampleFrequencyChanged( BluetoothLeAudioCodecStatus previous, BluetoothLeAudioCodecStatus next) { if ((previous == null) && (next == null)) { return false; } if ((previous == null) || (next == null)) { Log.d(TAG, previous + " != " + next); return true; } if ((previous.getInputCodecConfig() == null) && (next.getInputCodecConfig() == null)) { /* Nothing changed here.*/ return false; } if ((previous.getInputCodecConfig() == null) || (next.getInputCodecConfig() == null)) { Log.d( TAG, "New input codec: " + (previous.getInputCodecConfig() + " != " + next.getInputCodecConfig())); return true; } if (previous.getInputCodecConfig().getCodecType() != next.getInputCodecConfig().getCodecType()) { Log.d( TAG, "Different input codec type: " + (previous.getInputCodecConfig().getCodecType() + " != " + next.getInputCodecConfig().getCodecType())); return true; } if (previous.getInputCodecConfig().getSampleRate() != next.getInputCodecConfig().getSampleRate()) { Log.d( TAG, "Different input samplerate: " + (previous.getInputCodecConfig().getSampleRate() + " != " + next.getInputCodecConfig().getSampleRate())); return true; } return false; } // Suppressed since this is part of a local process // Suppressed since this is part of a local process @SuppressLint("AndroidFrameworkRequiresPermission") @SuppressLint("AndroidFrameworkRequiresPermission") void messageFromNative(LeAudioStackEvent stackEvent) { void messageFromNative(LeAudioStackEvent stackEvent) { Loading Loading @@ -3199,18 +3304,24 @@ public class LeAudioService extends ProfileService { descriptor.mInputSelectableConfig, descriptor.mInputSelectableConfig, descriptor.mOutputSelectableConfig); descriptor.mOutputSelectableConfig); if (descriptor.mCodecStatus != null) { boolean outputCodecOrFreqChanged = Log.d(TAG, " Replacing codec status for group: " + groupId); isOutputCodecOfSampleFrequencyChanged(descriptor.mCodecStatus, status); } else { boolean inputCodecOrFreqChanged = Log.d(TAG, " New codec status for group: " + groupId); isInputCodecOfSampleFrequencyChanged(descriptor.mCodecStatus, status); } Log.d( TAG, ("Codec update for group:" + groupId) + (", outputCodecOrFreqChanged: " + outputCodecOrFreqChanged) + (", inputCodecOrFreqChanged: " + inputCodecOrFreqChanged)); descriptor.mCodecStatus = status; descriptor.mCodecStatus = status; mHandler.post(() -> notifyUnicastCodecConfigChanged(groupId, status)); mHandler.post(() -> notifyUnicastCodecConfigChanged(groupId, status)); if (descriptor.isActive()) { if (descriptor.isActive() && (outputCodecOrFreqChanged || inputCodecOrFreqChanged)) { // Audio framework needs to be notified so it get new codec config // Audio framework needs to be notified so it get new codec config notifyAudioFrameworkForCodecConfigUpdate(groupId, descriptor); notifyAudioFrameworkForCodecConfigUpdate( groupId, descriptor, outputCodecOrFreqChanged, inputCodecOrFreqChanged); } } } else if (stackEvent.type == LeAudioStackEvent.EVENT_TYPE_AUDIO_CONF_CHANGED) { } else if (stackEvent.type == LeAudioStackEvent.EVENT_TYPE_AUDIO_CONF_CHANGED) { int direction = stackEvent.valueInt1; int direction = stackEvent.valueInt1; Loading
android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioServiceTest.java +123 −2 Original line number Original line Diff line number Diff line Loading @@ -52,8 +52,8 @@ import android.media.BluetoothProfileConnectionInfo; import android.os.Handler; import android.os.Handler; import android.os.Looper; import android.os.Looper; import android.os.ParcelUuid; import android.os.ParcelUuid; import android.sysprop.BluetoothProperties; import android.platform.test.flag.junit.SetFlagsRule; import android.platform.test.flag.junit.SetFlagsRule; import android.sysprop.BluetoothProperties; import androidx.test.filters.MediumTest; import androidx.test.filters.MediumTest; import androidx.test.platform.app.InstrumentationRegistry; import androidx.test.platform.app.InstrumentationRegistry; Loading Loading @@ -2171,7 +2171,8 @@ public class LeAudioServiceTest { /** Test native interface group status message handling */ /** Test native interface group status message handling */ @Test @Test public void testMessageFromNativeGroupCodecConfigChanged() { public void testMessageFromNativeGroupCodecConfigChangedNonActiveDevice() { mSetFlagsRule.enableFlags(Flags.FLAG_LEAUDIO_CODEC_CONFIG_CALLBACK_ORDER_FIX); onGroupCodecConfChangedCallbackCalled = false; onGroupCodecConfChangedCallbackCalled = false; injectLocalCodecConfigCapaChanged(INPUT_CAPABILITIES_CONFIG, OUTPUT_CAPABILITIES_CONFIG); injectLocalCodecConfigCapaChanged(INPUT_CAPABILITIES_CONFIG, OUTPUT_CAPABILITIES_CONFIG); Loading Loading @@ -2213,15 +2214,135 @@ public class LeAudioServiceTest { injectGroupSelectableCodecConfigChanged( injectGroupSelectableCodecConfigChanged( testGroupId, INPUT_SELECTABLE_CONFIG, OUTPUT_SELECTABLE_CONFIG); testGroupId, INPUT_SELECTABLE_CONFIG, OUTPUT_SELECTABLE_CONFIG); // Inject configuration and check that AF is NOT notified. injectGroupCurrentCodecConfigChanged(testGroupId, LC3_16KHZ_CONFIG, LC3_48KHZ_CONFIG); injectGroupCurrentCodecConfigChanged(testGroupId, LC3_16KHZ_CONFIG, LC3_48KHZ_CONFIG); TestUtils.waitForLooperToFinishScheduledTask(mService.getMainLooper()); TestUtils.waitForLooperToFinishScheduledTask(mService.getMainLooper()); assertThat(onGroupCodecConfChangedCallbackCalled).isTrue(); assertThat(onGroupCodecConfChangedCallbackCalled).isTrue(); verify(mAudioManager, times(0)) .handleBluetoothActiveDeviceChanged( any(), any(), any(BluetoothProfileConnectionInfo.class)); onGroupCodecConfChangedCallbackCalled = false; // Now inject again new configuration and check that AF is not notified. testCodecStatus = new BluetoothLeAudioCodecStatus( LC3_16KHZ_CONFIG, LC3_16KHZ_CONFIG, INPUT_CAPABILITIES_CONFIG, OUTPUT_CAPABILITIES_CONFIG, INPUT_SELECTABLE_CONFIG, OUTPUT_SELECTABLE_CONFIG); injectGroupCurrentCodecConfigChanged(testGroupId, LC3_16KHZ_CONFIG, LC3_16KHZ_CONFIG); TestUtils.waitForLooperToFinishScheduledTask(mService.getMainLooper()); assertThat(onGroupCodecConfChangedCallbackCalled).isTrue(); verify(mAudioManager, times(0)) .handleBluetoothActiveDeviceChanged( any(), any(), any(BluetoothProfileConnectionInfo.class)); onGroupCodecConfChangedCallbackCalled = false; onGroupCodecConfChangedCallbackCalled = false; mService.mLeAudioCallbacks.unregister(leAudioCallbacks); mService.mLeAudioCallbacks.unregister(leAudioCallbacks); } } /** Test native interface group status message handling */ @Test public void testMessageFromNativeGroupCodecConfigChangedActiveDevice_DifferentConfiguration() { mSetFlagsRule.enableFlags(Flags.FLAG_LEAUDIO_CODEC_CONFIG_CALLBACK_ORDER_FIX); onGroupCodecConfChangedCallbackCalled = false; injectLocalCodecConfigCapaChanged(INPUT_CAPABILITIES_CONFIG, OUTPUT_CAPABILITIES_CONFIG); doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class)); connectTestDevice(mSingleDevice, testGroupId); testCodecStatus = new BluetoothLeAudioCodecStatus( LC3_16KHZ_CONFIG, LC3_48KHZ_CONFIG, INPUT_CAPABILITIES_CONFIG, OUTPUT_CAPABILITIES_CONFIG, INPUT_SELECTABLE_CONFIG, OUTPUT_SELECTABLE_CONFIG); IBluetoothLeAudioCallback leAudioCallbacks = new IBluetoothLeAudioCallback.Stub() { @Override public void onCodecConfigChanged(int gid, BluetoothLeAudioCodecStatus status) { onGroupCodecConfChangedCallbackCalled = true; assertThat(status.equals(testCodecStatus)).isTrue(); } @Override public void onGroupStatusChanged(int gid, int gStatus) {} @Override public void onGroupNodeAdded(BluetoothDevice device, int gid) {} @Override public void onGroupNodeRemoved(BluetoothDevice device, int gid) {} @Override public void onGroupStreamStatusChanged(int groupId, int groupStreamStatus) {} }; synchronized (this.mService.mLeAudioCallbacks) { mService.mLeAudioCallbacks.register(leAudioCallbacks); } injectGroupSelectableCodecConfigChanged( testGroupId, INPUT_SELECTABLE_CONFIG, OUTPUT_SELECTABLE_CONFIG); injectGroupCurrentCodecConfigChanged(testGroupId, LC3_16KHZ_CONFIG, LC3_48KHZ_CONFIG); injectAudioConfChanged( testGroupId, BluetoothLeAudio.CONTEXT_TYPE_MEDIA | BluetoothLeAudio.CONTEXT_TYPE_CONVERSATIONAL, 3); injectGroupStatusChange(testGroupId, LeAudioStackEvent.GROUP_STATUS_ACTIVE); TestUtils.waitForLooperToFinishScheduledTask(mService.getMainLooper()); assertThat(onGroupCodecConfChangedCallbackCalled).isTrue(); verify(mAudioManager, times(2)) .handleBluetoothActiveDeviceChanged( any(), any(), any(BluetoothProfileConnectionInfo.class)); onGroupCodecConfChangedCallbackCalled = false; reset(mAudioManager); // Now inject configuration different sample rate on one direction testCodecStatus = new BluetoothLeAudioCodecStatus( LC3_16KHZ_CONFIG, LC3_16KHZ_CONFIG, INPUT_CAPABILITIES_CONFIG, OUTPUT_CAPABILITIES_CONFIG, INPUT_SELECTABLE_CONFIG, OUTPUT_SELECTABLE_CONFIG); injectGroupCurrentCodecConfigChanged(testGroupId, LC3_16KHZ_CONFIG, LC3_16KHZ_CONFIG); TestUtils.waitForLooperToFinishScheduledTask(mService.getMainLooper()); assertThat(onGroupCodecConfChangedCallbackCalled).isTrue(); verify(mAudioManager, times(1)) .handleBluetoothActiveDeviceChanged( any(), any(), any(BluetoothProfileConnectionInfo.class)); synchronized (this.mService.mLeAudioCallbacks) { mService.mLeAudioCallbacks.unregister(leAudioCallbacks); } onGroupCodecConfChangedCallbackCalled = false; reset(mAudioManager); } /** Test native interface group status message handling */ /** Test native interface group status message handling */ @Test @Test public void testMessageFromNativeGroupCodecConfigChanged_OneDirectionOnly() { public void testMessageFromNativeGroupCodecConfigChanged_OneDirectionOnly() { Loading
system/bta/le_audio/client.cc +12 −12 Original line number Original line Diff line number Diff line Loading @@ -856,13 +856,15 @@ public: ccid_contexts.source.unset(LeAudioContextType::CONVERSATIONAL); ccid_contexts.source.unset(LeAudioContextType::CONVERSATIONAL); } } bool group_is_streaming = group->IsStreaming(); BidirectionalPair<std::vector<uint8_t>> ccids = { BidirectionalPair<std::vector<uint8_t>> ccids = { .sink = ContentControlIdKeeper::GetInstance()->GetAllCcids(ccid_contexts.sink), .sink = ContentControlIdKeeper::GetInstance()->GetAllCcids(ccid_contexts.sink), .source = ContentControlIdKeeper::GetInstance()->GetAllCcids(ccid_contexts.source)}; .source = ContentControlIdKeeper::GetInstance()->GetAllCcids(ccid_contexts.source)}; if (group->IsPendingConfiguration()) { if (group->IsPendingConfiguration()) { return groupStateMachine_->ConfigureStream(group, configuration_context_type_, return groupStateMachine_->ConfigureStream(group, configuration_context_type_, remote_contexts, ccids); remote_contexts, ccids); } else if (group->GetState() != AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) { } else if (!group_is_streaming) { stream_setup_start_timestamp_ = bluetooth::common::time_get_os_boottime_us(); stream_setup_start_timestamp_ = bluetooth::common::time_get_os_boottime_us(); } } Loading @@ -870,7 +872,7 @@ public: * when there would be request to stream unicast. * when there would be request to stream unicast. */ */ if (com::android::bluetooth::flags::leaudio_broadcast_audio_handover_policies() && if (com::android::bluetooth::flags::leaudio_broadcast_audio_handover_policies() && !sink_monitor_mode_ && source_monitor_mode_ && !group->IsStreaming()) { !sink_monitor_mode_ && source_monitor_mode_ && !group_is_streaming) { callbacks_->OnUnicastMonitorModeStatus(bluetooth::le_audio::types::kLeAudioDirectionSource, callbacks_->OnUnicastMonitorModeStatus(bluetooth::le_audio::types::kLeAudioDirectionSource, UnicastMonitorModeStatus::STREAMING_REQUESTED); UnicastMonitorModeStatus::STREAMING_REQUESTED); } } Loading @@ -878,6 +880,13 @@ public: bool result = groupStateMachine_->StartStream(group, configuration_context_type, bool result = groupStateMachine_->StartStream(group, configuration_context_type, remote_contexts, ccids); remote_contexts, ccids); if (result && !group_is_streaming) { /* Notify Java about new configuration when start stream has been accepted and * it is not metadata update */ SendAudioGroupCurrentCodecConfigChanged(group); } return result; return result; } } Loading Loading @@ -4374,6 +4383,7 @@ public: /* Need to reconfigure stream */ /* Need to reconfigure stream */ group->SetPendingConfiguration(); group->SetPendingConfiguration(); groupStateMachine_->StopStream(group); groupStateMachine_->StopStream(group); SendAudioGroupCurrentCodecConfigChanged(group); return true; return true; } } Loading Loading @@ -5280,22 +5290,12 @@ public: std::bind(&LeAudioClientImpl::UpdateAudioConfigToHal, weak_factory_.GetWeakPtr(), std::bind(&LeAudioClientImpl::UpdateAudioConfigToHal, weak_factory_.GetWeakPtr(), std::placeholders::_1, std::placeholders::_2)); std::placeholders::_1, std::placeholders::_2)); /* When at least one direction is started we can assume new * configuration here */ bool new_configuration = false; if (audio_sender_state_ == AudioState::READY_TO_START) { if (audio_sender_state_ == AudioState::READY_TO_START) { StartSendingAudio(group_id); StartSendingAudio(group_id); new_configuration = true; } } if (audio_receiver_state_ == AudioState::READY_TO_START) { if (audio_receiver_state_ == AudioState::READY_TO_START) { StartReceivingAudio(group_id); StartReceivingAudio(group_id); new_configuration = true; } if (new_configuration) { /* Notify Java about new configuration */ SendAudioGroupCurrentCodecConfigChanged(group); } } break; break; } } Loading