Loading system/bta/le_audio/device_groups.cc +11 −5 Original line number Diff line number Diff line Loading @@ -537,18 +537,24 @@ static uint16_t find_max_transport_latency(const LeAudioDeviceGroup* group, ase = leAudioDevice->GetNextActiveAseWithSameDirection(ase)) { if (!ase) break; if (!max_transport_latency) if (max_transport_latency == 0) { // first assignment max_transport_latency = ase->max_transport_latency; else if (ase->max_transport_latency < max_transport_latency) } else if (ase->max_transport_latency < max_transport_latency) { if (ase->max_transport_latency != 0) { max_transport_latency = ase->max_transport_latency; } else { LOG_WARN("Trying to set latency back to 0, ASE ID %d", ase->id); } } } } if (max_transport_latency < types::kMaxTransportLatencyMin) if (max_transport_latency < types::kMaxTransportLatencyMin) { max_transport_latency = types::kMaxTransportLatencyMin; else if (max_transport_latency > types::kMaxTransportLatencyMax) } else if (max_transport_latency > types::kMaxTransportLatencyMax) { max_transport_latency = types::kMaxTransportLatencyMax; } return max_transport_latency; } Loading system/bta/le_audio/state_machine.cc +17 −0 Original line number Diff line number Diff line Loading @@ -1248,6 +1248,14 @@ class LeAudioGroupStateMachineImpl : public LeAudioGroupStateMachine { ase->direction); } static bool isIntervalAndLatencyProperlySet(uint32_t sdu_interval_us, uint16_t max_latency_ms) { if (sdu_interval_us == 0) { return max_latency_ms == le_audio::types::kMaxTransportLatencyMin; } return ((1000 * max_latency_ms) > sdu_interval_us); } bool CigCreate(LeAudioDeviceGroup* group) { uint32_t sdu_interval_mtos, sdu_interval_stom; uint16_t max_trans_lat_mtos, max_trans_lat_stom; Loading Loading @@ -1280,6 +1288,15 @@ class LeAudioGroupStateMachineImpl : public LeAudioGroupStateMachine { uint8_t phy_stom = group->GetPhyBitmask(le_audio::types::kLeAudioDirectionSource); if (!isIntervalAndLatencyProperlySet(sdu_interval_mtos, max_trans_lat_mtos) || !isIntervalAndLatencyProperlySet(sdu_interval_stom, max_trans_lat_stom)) { LOG_ERROR("Latency and interval not properly set"); group->PrintDebugState(); return false; } // Use 1M Phy for the ACK packet from remote device to phone for better // sensitivity if (max_sdu_size_stom == 0 && Loading system/bta/le_audio/state_machine_test.cc +115 −0 Original line number Diff line number Diff line Loading @@ -186,6 +186,9 @@ class StateMachineTestBase : public Test { /* Keep ASE in releasing state */ bool stay_in_releasing_state_; /* Use for single test to simulate late ASE notifications */ bool stop_inject_configured_ase_after_first_ase_configured_; virtual void SetUp() override { bluetooth::common::InitFlags::Load(test_flags); reset_mock_function_count_map(); Loading @@ -198,6 +201,7 @@ class StateMachineTestBase : public Test { overwrite_cis_status_ = false; do_not_send_cis_establish_event_ = false; stay_in_releasing_state_ = false; stop_inject_configured_ase_after_first_ase_configured_ = false; cis_status_.clear(); LeAudioGroupStateMachine::Initialize(&mock_callbacks_); Loading Loading @@ -935,6 +939,10 @@ class StateMachineTestBase : public Test { InjectAseStateNotification(ase, device, group, ascs::kAseStateCodecConfigured, &codec_configured_state_params); if (stop_inject_configured_ase_after_first_ase_configured_) { return; } } }; } Loading Loading @@ -4554,6 +4562,113 @@ TEST_F(StateMachineTest, StartStreamCachedConfig) { ASSERT_EQ(1, get_func_call_count("alarm_cancel")); } TEST_F(StateMachineTest, StartStreamCachedConfigReconfigInvalidBehavior) { const auto context_type = kContextTypeConversational; const auto leaudio_group_id = 6; const auto num_devices = 1; channel_count_ = kLeAudioCodecChannelCountSingleChannel | kLeAudioCodecChannelCountTwoChannel; /* Scenario * 1. Start stream and stop stream so ASEs stays in Configured State * 2. Reconfigure ASEs localy, so the QoS parameters are zeroed * 3. Inject one ASE 2 to be in Releasing state * 4. Start stream and Incject ASE 1 to go into Codec Configured state * 5. IN such case CIG shall not be created and fallback to Release and * Configure stream should happen. Before fix CigCreate with invalid * parameters were called */ ContentControlIdKeeper::GetInstance()->SetCcid(call_context, call_ccid); // Prepare multiple fake connected devices in a group auto* group = PrepareSingleTestDeviceGroup(leaudio_group_id, context_type, num_devices); ASSERT_EQ(group->Size(), num_devices); PrepareConfigureCodecHandler(group, 0, true); PrepareConfigureQosHandler(group); PrepareEnableHandler(group); PrepareDisableHandler(group); PrepareReceiverStartReady(group); PrepareReleaseHandler(group); InjectInitialIdleNotification(group); // Validate GroupStreamStatus EXPECT_CALL( mock_callbacks_, StatusReportCb(leaudio_group_id, bluetooth::le_audio::GroupStreamStatus::STREAMING)); EXPECT_CALL(*mock_iso_manager_, CreateCig).Times(1); // Start the configuration and stream call content LeAudioGroupStateMachine::Get()->StartStream( group, context_type, {.sink = types::AudioContexts(context_type), .source = types::AudioContexts(context_type)}); testing::Mock::VerifyAndClearExpectations(&mock_callbacks_); ASSERT_EQ(1, get_func_call_count("alarm_cancel")); reset_mock_function_count_map(); // Validate GroupStreamStatus EXPECT_CALL( mock_callbacks_, StatusReportCb(leaudio_group_id, bluetooth::le_audio::GroupStreamStatus::RELEASING)); EXPECT_CALL( mock_callbacks_, StatusReportCb( leaudio_group_id, bluetooth::le_audio::GroupStreamStatus::CONFIGURED_AUTONOMOUS)); // Start the configuration and stream Media content LeAudioGroupStateMachine::Get()->StopStream(group); testing::Mock::VerifyAndClearExpectations(&mock_callbacks_); testing::Mock::VerifyAndClearExpectations(&mock_iso_manager_); ASSERT_EQ(1, get_func_call_count("alarm_cancel")); reset_mock_function_count_map(); stop_inject_configured_ase_after_first_ase_configured_ = true; auto device = group->GetFirstDevice(); int i = 0; for (auto& ase : device->ases_) { if (i++ == 0) continue; // Simulate autonomus release for one ASE - this is invalid behaviour InjectAseStateNotification(&ase, device, group, ascs::kAseStateReleasing, nullptr); } // Restart stream and expect it will not be created. EXPECT_CALL(mock_callbacks_, StatusReportCb(leaudio_group_id, bluetooth::le_audio::GroupStreamStatus::STREAMING)) .Times(0); EXPECT_CALL(mock_callbacks_, StatusReportCb(leaudio_group_id, bluetooth::le_audio::GroupStreamStatus::RELEASING)) .Times(1); EXPECT_CALL(*mock_iso_manager_, CreateCig(_, _)).Times(0); // Block the fallback Release which will happen when CreateCig will faile stay_in_releasing_state_ = true; // Start the configuration and stream Live content LeAudioGroupStateMachine::Get()->StartStream( group, kContextTypeLive, {.sink = types::AudioContexts(kContextTypeLive), .source = types::AudioContexts(kContextTypeLive)}); testing::Mock::VerifyAndClearExpectations(&mock_callbacks_); testing::Mock::VerifyAndClearExpectations(&mock_iso_manager_); } TEST_F(StateMachineTest, BoundedHeadphonesConversationalToMediaChannelCount_2) { const auto initial_context_type = kContextTypeConversational; const auto new_context_type = kContextTypeMedia; Loading Loading
system/bta/le_audio/device_groups.cc +11 −5 Original line number Diff line number Diff line Loading @@ -537,18 +537,24 @@ static uint16_t find_max_transport_latency(const LeAudioDeviceGroup* group, ase = leAudioDevice->GetNextActiveAseWithSameDirection(ase)) { if (!ase) break; if (!max_transport_latency) if (max_transport_latency == 0) { // first assignment max_transport_latency = ase->max_transport_latency; else if (ase->max_transport_latency < max_transport_latency) } else if (ase->max_transport_latency < max_transport_latency) { if (ase->max_transport_latency != 0) { max_transport_latency = ase->max_transport_latency; } else { LOG_WARN("Trying to set latency back to 0, ASE ID %d", ase->id); } } } } if (max_transport_latency < types::kMaxTransportLatencyMin) if (max_transport_latency < types::kMaxTransportLatencyMin) { max_transport_latency = types::kMaxTransportLatencyMin; else if (max_transport_latency > types::kMaxTransportLatencyMax) } else if (max_transport_latency > types::kMaxTransportLatencyMax) { max_transport_latency = types::kMaxTransportLatencyMax; } return max_transport_latency; } Loading
system/bta/le_audio/state_machine.cc +17 −0 Original line number Diff line number Diff line Loading @@ -1248,6 +1248,14 @@ class LeAudioGroupStateMachineImpl : public LeAudioGroupStateMachine { ase->direction); } static bool isIntervalAndLatencyProperlySet(uint32_t sdu_interval_us, uint16_t max_latency_ms) { if (sdu_interval_us == 0) { return max_latency_ms == le_audio::types::kMaxTransportLatencyMin; } return ((1000 * max_latency_ms) > sdu_interval_us); } bool CigCreate(LeAudioDeviceGroup* group) { uint32_t sdu_interval_mtos, sdu_interval_stom; uint16_t max_trans_lat_mtos, max_trans_lat_stom; Loading Loading @@ -1280,6 +1288,15 @@ class LeAudioGroupStateMachineImpl : public LeAudioGroupStateMachine { uint8_t phy_stom = group->GetPhyBitmask(le_audio::types::kLeAudioDirectionSource); if (!isIntervalAndLatencyProperlySet(sdu_interval_mtos, max_trans_lat_mtos) || !isIntervalAndLatencyProperlySet(sdu_interval_stom, max_trans_lat_stom)) { LOG_ERROR("Latency and interval not properly set"); group->PrintDebugState(); return false; } // Use 1M Phy for the ACK packet from remote device to phone for better // sensitivity if (max_sdu_size_stom == 0 && Loading
system/bta/le_audio/state_machine_test.cc +115 −0 Original line number Diff line number Diff line Loading @@ -186,6 +186,9 @@ class StateMachineTestBase : public Test { /* Keep ASE in releasing state */ bool stay_in_releasing_state_; /* Use for single test to simulate late ASE notifications */ bool stop_inject_configured_ase_after_first_ase_configured_; virtual void SetUp() override { bluetooth::common::InitFlags::Load(test_flags); reset_mock_function_count_map(); Loading @@ -198,6 +201,7 @@ class StateMachineTestBase : public Test { overwrite_cis_status_ = false; do_not_send_cis_establish_event_ = false; stay_in_releasing_state_ = false; stop_inject_configured_ase_after_first_ase_configured_ = false; cis_status_.clear(); LeAudioGroupStateMachine::Initialize(&mock_callbacks_); Loading Loading @@ -935,6 +939,10 @@ class StateMachineTestBase : public Test { InjectAseStateNotification(ase, device, group, ascs::kAseStateCodecConfigured, &codec_configured_state_params); if (stop_inject_configured_ase_after_first_ase_configured_) { return; } } }; } Loading Loading @@ -4554,6 +4562,113 @@ TEST_F(StateMachineTest, StartStreamCachedConfig) { ASSERT_EQ(1, get_func_call_count("alarm_cancel")); } TEST_F(StateMachineTest, StartStreamCachedConfigReconfigInvalidBehavior) { const auto context_type = kContextTypeConversational; const auto leaudio_group_id = 6; const auto num_devices = 1; channel_count_ = kLeAudioCodecChannelCountSingleChannel | kLeAudioCodecChannelCountTwoChannel; /* Scenario * 1. Start stream and stop stream so ASEs stays in Configured State * 2. Reconfigure ASEs localy, so the QoS parameters are zeroed * 3. Inject one ASE 2 to be in Releasing state * 4. Start stream and Incject ASE 1 to go into Codec Configured state * 5. IN such case CIG shall not be created and fallback to Release and * Configure stream should happen. Before fix CigCreate with invalid * parameters were called */ ContentControlIdKeeper::GetInstance()->SetCcid(call_context, call_ccid); // Prepare multiple fake connected devices in a group auto* group = PrepareSingleTestDeviceGroup(leaudio_group_id, context_type, num_devices); ASSERT_EQ(group->Size(), num_devices); PrepareConfigureCodecHandler(group, 0, true); PrepareConfigureQosHandler(group); PrepareEnableHandler(group); PrepareDisableHandler(group); PrepareReceiverStartReady(group); PrepareReleaseHandler(group); InjectInitialIdleNotification(group); // Validate GroupStreamStatus EXPECT_CALL( mock_callbacks_, StatusReportCb(leaudio_group_id, bluetooth::le_audio::GroupStreamStatus::STREAMING)); EXPECT_CALL(*mock_iso_manager_, CreateCig).Times(1); // Start the configuration and stream call content LeAudioGroupStateMachine::Get()->StartStream( group, context_type, {.sink = types::AudioContexts(context_type), .source = types::AudioContexts(context_type)}); testing::Mock::VerifyAndClearExpectations(&mock_callbacks_); ASSERT_EQ(1, get_func_call_count("alarm_cancel")); reset_mock_function_count_map(); // Validate GroupStreamStatus EXPECT_CALL( mock_callbacks_, StatusReportCb(leaudio_group_id, bluetooth::le_audio::GroupStreamStatus::RELEASING)); EXPECT_CALL( mock_callbacks_, StatusReportCb( leaudio_group_id, bluetooth::le_audio::GroupStreamStatus::CONFIGURED_AUTONOMOUS)); // Start the configuration and stream Media content LeAudioGroupStateMachine::Get()->StopStream(group); testing::Mock::VerifyAndClearExpectations(&mock_callbacks_); testing::Mock::VerifyAndClearExpectations(&mock_iso_manager_); ASSERT_EQ(1, get_func_call_count("alarm_cancel")); reset_mock_function_count_map(); stop_inject_configured_ase_after_first_ase_configured_ = true; auto device = group->GetFirstDevice(); int i = 0; for (auto& ase : device->ases_) { if (i++ == 0) continue; // Simulate autonomus release for one ASE - this is invalid behaviour InjectAseStateNotification(&ase, device, group, ascs::kAseStateReleasing, nullptr); } // Restart stream and expect it will not be created. EXPECT_CALL(mock_callbacks_, StatusReportCb(leaudio_group_id, bluetooth::le_audio::GroupStreamStatus::STREAMING)) .Times(0); EXPECT_CALL(mock_callbacks_, StatusReportCb(leaudio_group_id, bluetooth::le_audio::GroupStreamStatus::RELEASING)) .Times(1); EXPECT_CALL(*mock_iso_manager_, CreateCig(_, _)).Times(0); // Block the fallback Release which will happen when CreateCig will faile stay_in_releasing_state_ = true; // Start the configuration and stream Live content LeAudioGroupStateMachine::Get()->StartStream( group, kContextTypeLive, {.sink = types::AudioContexts(kContextTypeLive), .source = types::AudioContexts(kContextTypeLive)}); testing::Mock::VerifyAndClearExpectations(&mock_callbacks_); testing::Mock::VerifyAndClearExpectations(&mock_iso_manager_); } TEST_F(StateMachineTest, BoundedHeadphonesConversationalToMediaChannelCount_2) { const auto initial_context_type = kContextTypeConversational; const auto new_context_type = kContextTypeMedia; Loading