Loading system/bta/le_audio/device_groups.cc +1 −0 Original line number Diff line number Diff line Loading @@ -177,6 +177,7 @@ void LeAudioDeviceGroup::Deactivate(void) { for (auto* ase = leAudioDevice->GetFirstActiveAse(); ase; ase = leAudioDevice->GetNextActiveAse(ase)) { ase->active = false; ase->reconfigure = 0; } } } Loading system/bta/le_audio/devices.cc +5 −2 Original line number Diff line number Diff line Loading @@ -796,9 +796,10 @@ void LeAudioDevice::PrintDebugState(void) { debug_str << "\n id: " << +ase.id << ", active: " << ase.active << ", dir: " << (ase.direction == types::kLeAudioDirectionSink ? "sink" : "source") << ", state: " << bluetooth::common::ToString(ase.state) << ", cis_id: " << +ase.cis_id << ", cis_handle: " << +ase.cis_conn_hdl << ", state: " << bluetooth::common::ToString(ase.cis_state) << ", cis_state: " << bluetooth::common::ToString(ase.cis_state) << ", data_path_state: " << bluetooth::common::ToString(ase.data_path_state) << "\n ase max_latency: " << +ase.qos_config.max_transport_latency Loading @@ -808,7 +809,8 @@ void LeAudioDevice::PrintDebugState(void) { << ", presentation_delay: " << +ase.qos_config.presentation_delay << ", framing: " << +ase.qos_config.framing << ", phy: " << +ase.qos_config.phy << ", target latency: " << +ase.target_latency; << ", target latency: " << +ase.target_latency << ", reconfigure: " << ase.reconfigure << "\n"; } } Loading Loading @@ -1022,6 +1024,7 @@ void LeAudioDevice::DeactivateAllAses(void) { ase.cis_state = CisState::IDLE; ase.data_path_state = DataPathState::IDLE; ase.active = false; ase.reconfigure = 0; ase.cis_id = bluetooth::le_audio::kInvalidCisId; ase.cis_conn_hdl = 0; } Loading system/bta/le_audio/state_machine_test.cc +174 −0 Original line number Diff line number Diff line Loading @@ -890,6 +890,19 @@ class StateMachineTestBase : public Test { } } void InjectInitialConfiguredNotification(LeAudioDeviceGroup* group) { for (auto* device = group->GetFirstDevice(); device != nullptr; device = group->GetNextDevice(device)) { for (auto& ase : device->ases_) { client_parser::ascs::ase_codec_configured_state_params codec_configured_state_params; InjectAseStateNotification(&ase, device, group, ascs::kAseStateCodecConfigured, &codec_configured_state_params); } } } void InjectInitialIdleAndConfiguredNotification(LeAudioDeviceGroup* group) { for (auto* device = group->GetFirstDevice(); device != nullptr; device = group->GetNextDevice(device)) { Loading Loading @@ -4355,6 +4368,167 @@ TEST_F(StateMachineTest, testStateTransitionTimeout) { ASSERT_EQ(1, get_func_call_count("alarm_set_on_mloop")); } TEST_F(StateMachineTest, testStateTransitionTimeoutAndDisconnectWhenConfigured) { const auto context_type = kContextTypeMedia; const int leaudio_group_id = 4; channel_count_ = kLeAudioCodecChannelCountSingleChannel | kLeAudioCodecChannelCountTwoChannel; // Prepare fake connected device group auto* group = PrepareSingleTestDeviceGroup(leaudio_group_id, context_type); auto* leAudioDevice = group->GetFirstDevice(); EXPECT_CALL(gatt_queue, WriteCharacteristic(1, leAudioDevice->ctp_hdls_.val_hdl, _, GATT_WRITE_NO_RSP, _, _)) .Times(1); InjectInitialConfiguredNotification(group); group->PrintDebugState(); // Start the configuration and stream Media content ASSERT_TRUE(LeAudioGroupStateMachine::Get()->StartStream( group, context_type, {.sink = types::AudioContexts(context_type), .source = types::AudioContexts(context_type)})); group->PrintDebugState(); // Check if timeout is fired EXPECT_CALL(mock_callbacks_, OnStateTransitionTimeout(leaudio_group_id)); // simulate timeout seconds passed, alarm executing fake_osi_alarm_set_on_mloop_.cb(fake_osi_alarm_set_on_mloop_.data); ASSERT_EQ(1, get_func_call_count("alarm_set_on_mloop")); LOG_INFO("OnStateTransitionTimeout"); /* Simulate On State timeout */ group->SetTargetState(types::AseState::BTA_LE_AUDIO_ASE_STATE_IDLE); group->ClearAllCises(); group->PrintDebugState(); InjectAclDisconnected(group, leAudioDevice); /* Verify that all ASEs are inactive and reconfiguration flag is cleared.*/ for (const auto& ase : leAudioDevice->ases_) { ASSERT_EQ(ase.state, types::AseState::BTA_LE_AUDIO_ASE_STATE_IDLE); ASSERT_EQ(ase.cis_state, types::CisState::IDLE); ASSERT_EQ(ase.data_path_state, types::DataPathState::IDLE); ASSERT_EQ(ase.reconfigure, 0); } } TEST_F(StateMachineTest, testStateTransitionTimeoutAndDisconnectWhenQoSConfigured) { const auto context_type = kContextTypeMedia; const int leaudio_group_id = 4; channel_count_ = kLeAudioCodecChannelCountSingleChannel | kLeAudioCodecChannelCountTwoChannel; // Prepare fake connected device group auto* group = PrepareSingleTestDeviceGroup(leaudio_group_id, context_type); PrepareConfigureCodecHandler(group, 1); auto* leAudioDevice = group->GetFirstDevice(); EXPECT_CALL(gatt_queue, WriteCharacteristic(1, leAudioDevice->ctp_hdls_.val_hdl, _, GATT_WRITE_NO_RSP, _, _)) .Times(2); InjectInitialConfiguredNotification(group); group->PrintDebugState(); // Start the configuration and stream Media content ASSERT_TRUE(LeAudioGroupStateMachine::Get()->StartStream( group, context_type, {.sink = types::AudioContexts(context_type), .source = types::AudioContexts(context_type)})); group->PrintDebugState(); // Check if timeout is fired EXPECT_CALL(mock_callbacks_, OnStateTransitionTimeout(leaudio_group_id)); // simulate timeout seconds passed, alarm executing fake_osi_alarm_set_on_mloop_.cb(fake_osi_alarm_set_on_mloop_.data); ASSERT_EQ(1, get_func_call_count("alarm_set_on_mloop")); LOG_INFO("OnStateTransitionTimeout"); /* Simulate On State timeout */ group->SetTargetState(types::AseState::BTA_LE_AUDIO_ASE_STATE_IDLE); group->ClearAllCises(); group->PrintDebugState(); InjectAclDisconnected(group, leAudioDevice); /* Verify that all ASEs are inactive and reconfiguration flag is cleared.*/ for (const auto& ase : leAudioDevice->ases_) { ASSERT_EQ(ase.state, types::AseState::BTA_LE_AUDIO_ASE_STATE_IDLE); ASSERT_EQ(ase.cis_state, types::CisState::IDLE); ASSERT_EQ(ase.data_path_state, types::DataPathState::IDLE); ASSERT_EQ(ase.reconfigure, 0); } } TEST_F(StateMachineTest, testStateTransitionTimeoutAndDisconnectWhenEnabling) { const auto context_type = kContextTypeMedia; const int leaudio_group_id = 4; channel_count_ = kLeAudioCodecChannelCountSingleChannel | kLeAudioCodecChannelCountTwoChannel; // Prepare fake connected device group auto* group = PrepareSingleTestDeviceGroup(leaudio_group_id, context_type); PrepareConfigureCodecHandler(group, 1); PrepareConfigureQosHandler(group, 1); auto* leAudioDevice = group->GetFirstDevice(); EXPECT_CALL(gatt_queue, WriteCharacteristic(1, leAudioDevice->ctp_hdls_.val_hdl, _, GATT_WRITE_NO_RSP, _, _)) .Times(3); InjectInitialConfiguredNotification(group); group->PrintDebugState(); // Start the configuration and stream Media content ASSERT_TRUE(LeAudioGroupStateMachine::Get()->StartStream( group, context_type, {.sink = types::AudioContexts(context_type), .source = types::AudioContexts(context_type)})); group->PrintDebugState(); // Check if timeout is fired EXPECT_CALL(mock_callbacks_, OnStateTransitionTimeout(leaudio_group_id)); // simulate timeout seconds passed, alarm executing fake_osi_alarm_set_on_mloop_.cb(fake_osi_alarm_set_on_mloop_.data); ASSERT_EQ(1, get_func_call_count("alarm_set_on_mloop")); LOG_INFO("OnStateTransitionTimeout"); /* Simulate On State timeout */ group->SetTargetState(types::AseState::BTA_LE_AUDIO_ASE_STATE_IDLE); group->ClearAllCises(); group->PrintDebugState(); InjectAclDisconnected(group, leAudioDevice); /* Verify that all ASEs are inactive and reconfiguration flag is cleared.*/ for (const auto& ase : leAudioDevice->ases_) { ASSERT_EQ(ase.state, types::AseState::BTA_LE_AUDIO_ASE_STATE_IDLE); ASSERT_EQ(ase.cis_state, types::CisState::IDLE); ASSERT_EQ(ase.data_path_state, types::DataPathState::IDLE); ASSERT_EQ(ase.reconfigure, 0); } } MATCHER_P(dataPathIsEq, expected, "") { return (arg.data_path_id == expected); } TEST_F(StateMachineTest, testConfigureDataPathForHost) { Loading Loading
system/bta/le_audio/device_groups.cc +1 −0 Original line number Diff line number Diff line Loading @@ -177,6 +177,7 @@ void LeAudioDeviceGroup::Deactivate(void) { for (auto* ase = leAudioDevice->GetFirstActiveAse(); ase; ase = leAudioDevice->GetNextActiveAse(ase)) { ase->active = false; ase->reconfigure = 0; } } } Loading
system/bta/le_audio/devices.cc +5 −2 Original line number Diff line number Diff line Loading @@ -796,9 +796,10 @@ void LeAudioDevice::PrintDebugState(void) { debug_str << "\n id: " << +ase.id << ", active: " << ase.active << ", dir: " << (ase.direction == types::kLeAudioDirectionSink ? "sink" : "source") << ", state: " << bluetooth::common::ToString(ase.state) << ", cis_id: " << +ase.cis_id << ", cis_handle: " << +ase.cis_conn_hdl << ", state: " << bluetooth::common::ToString(ase.cis_state) << ", cis_state: " << bluetooth::common::ToString(ase.cis_state) << ", data_path_state: " << bluetooth::common::ToString(ase.data_path_state) << "\n ase max_latency: " << +ase.qos_config.max_transport_latency Loading @@ -808,7 +809,8 @@ void LeAudioDevice::PrintDebugState(void) { << ", presentation_delay: " << +ase.qos_config.presentation_delay << ", framing: " << +ase.qos_config.framing << ", phy: " << +ase.qos_config.phy << ", target latency: " << +ase.target_latency; << ", target latency: " << +ase.target_latency << ", reconfigure: " << ase.reconfigure << "\n"; } } Loading Loading @@ -1022,6 +1024,7 @@ void LeAudioDevice::DeactivateAllAses(void) { ase.cis_state = CisState::IDLE; ase.data_path_state = DataPathState::IDLE; ase.active = false; ase.reconfigure = 0; ase.cis_id = bluetooth::le_audio::kInvalidCisId; ase.cis_conn_hdl = 0; } Loading
system/bta/le_audio/state_machine_test.cc +174 −0 Original line number Diff line number Diff line Loading @@ -890,6 +890,19 @@ class StateMachineTestBase : public Test { } } void InjectInitialConfiguredNotification(LeAudioDeviceGroup* group) { for (auto* device = group->GetFirstDevice(); device != nullptr; device = group->GetNextDevice(device)) { for (auto& ase : device->ases_) { client_parser::ascs::ase_codec_configured_state_params codec_configured_state_params; InjectAseStateNotification(&ase, device, group, ascs::kAseStateCodecConfigured, &codec_configured_state_params); } } } void InjectInitialIdleAndConfiguredNotification(LeAudioDeviceGroup* group) { for (auto* device = group->GetFirstDevice(); device != nullptr; device = group->GetNextDevice(device)) { Loading Loading @@ -4355,6 +4368,167 @@ TEST_F(StateMachineTest, testStateTransitionTimeout) { ASSERT_EQ(1, get_func_call_count("alarm_set_on_mloop")); } TEST_F(StateMachineTest, testStateTransitionTimeoutAndDisconnectWhenConfigured) { const auto context_type = kContextTypeMedia; const int leaudio_group_id = 4; channel_count_ = kLeAudioCodecChannelCountSingleChannel | kLeAudioCodecChannelCountTwoChannel; // Prepare fake connected device group auto* group = PrepareSingleTestDeviceGroup(leaudio_group_id, context_type); auto* leAudioDevice = group->GetFirstDevice(); EXPECT_CALL(gatt_queue, WriteCharacteristic(1, leAudioDevice->ctp_hdls_.val_hdl, _, GATT_WRITE_NO_RSP, _, _)) .Times(1); InjectInitialConfiguredNotification(group); group->PrintDebugState(); // Start the configuration and stream Media content ASSERT_TRUE(LeAudioGroupStateMachine::Get()->StartStream( group, context_type, {.sink = types::AudioContexts(context_type), .source = types::AudioContexts(context_type)})); group->PrintDebugState(); // Check if timeout is fired EXPECT_CALL(mock_callbacks_, OnStateTransitionTimeout(leaudio_group_id)); // simulate timeout seconds passed, alarm executing fake_osi_alarm_set_on_mloop_.cb(fake_osi_alarm_set_on_mloop_.data); ASSERT_EQ(1, get_func_call_count("alarm_set_on_mloop")); LOG_INFO("OnStateTransitionTimeout"); /* Simulate On State timeout */ group->SetTargetState(types::AseState::BTA_LE_AUDIO_ASE_STATE_IDLE); group->ClearAllCises(); group->PrintDebugState(); InjectAclDisconnected(group, leAudioDevice); /* Verify that all ASEs are inactive and reconfiguration flag is cleared.*/ for (const auto& ase : leAudioDevice->ases_) { ASSERT_EQ(ase.state, types::AseState::BTA_LE_AUDIO_ASE_STATE_IDLE); ASSERT_EQ(ase.cis_state, types::CisState::IDLE); ASSERT_EQ(ase.data_path_state, types::DataPathState::IDLE); ASSERT_EQ(ase.reconfigure, 0); } } TEST_F(StateMachineTest, testStateTransitionTimeoutAndDisconnectWhenQoSConfigured) { const auto context_type = kContextTypeMedia; const int leaudio_group_id = 4; channel_count_ = kLeAudioCodecChannelCountSingleChannel | kLeAudioCodecChannelCountTwoChannel; // Prepare fake connected device group auto* group = PrepareSingleTestDeviceGroup(leaudio_group_id, context_type); PrepareConfigureCodecHandler(group, 1); auto* leAudioDevice = group->GetFirstDevice(); EXPECT_CALL(gatt_queue, WriteCharacteristic(1, leAudioDevice->ctp_hdls_.val_hdl, _, GATT_WRITE_NO_RSP, _, _)) .Times(2); InjectInitialConfiguredNotification(group); group->PrintDebugState(); // Start the configuration and stream Media content ASSERT_TRUE(LeAudioGroupStateMachine::Get()->StartStream( group, context_type, {.sink = types::AudioContexts(context_type), .source = types::AudioContexts(context_type)})); group->PrintDebugState(); // Check if timeout is fired EXPECT_CALL(mock_callbacks_, OnStateTransitionTimeout(leaudio_group_id)); // simulate timeout seconds passed, alarm executing fake_osi_alarm_set_on_mloop_.cb(fake_osi_alarm_set_on_mloop_.data); ASSERT_EQ(1, get_func_call_count("alarm_set_on_mloop")); LOG_INFO("OnStateTransitionTimeout"); /* Simulate On State timeout */ group->SetTargetState(types::AseState::BTA_LE_AUDIO_ASE_STATE_IDLE); group->ClearAllCises(); group->PrintDebugState(); InjectAclDisconnected(group, leAudioDevice); /* Verify that all ASEs are inactive and reconfiguration flag is cleared.*/ for (const auto& ase : leAudioDevice->ases_) { ASSERT_EQ(ase.state, types::AseState::BTA_LE_AUDIO_ASE_STATE_IDLE); ASSERT_EQ(ase.cis_state, types::CisState::IDLE); ASSERT_EQ(ase.data_path_state, types::DataPathState::IDLE); ASSERT_EQ(ase.reconfigure, 0); } } TEST_F(StateMachineTest, testStateTransitionTimeoutAndDisconnectWhenEnabling) { const auto context_type = kContextTypeMedia; const int leaudio_group_id = 4; channel_count_ = kLeAudioCodecChannelCountSingleChannel | kLeAudioCodecChannelCountTwoChannel; // Prepare fake connected device group auto* group = PrepareSingleTestDeviceGroup(leaudio_group_id, context_type); PrepareConfigureCodecHandler(group, 1); PrepareConfigureQosHandler(group, 1); auto* leAudioDevice = group->GetFirstDevice(); EXPECT_CALL(gatt_queue, WriteCharacteristic(1, leAudioDevice->ctp_hdls_.val_hdl, _, GATT_WRITE_NO_RSP, _, _)) .Times(3); InjectInitialConfiguredNotification(group); group->PrintDebugState(); // Start the configuration and stream Media content ASSERT_TRUE(LeAudioGroupStateMachine::Get()->StartStream( group, context_type, {.sink = types::AudioContexts(context_type), .source = types::AudioContexts(context_type)})); group->PrintDebugState(); // Check if timeout is fired EXPECT_CALL(mock_callbacks_, OnStateTransitionTimeout(leaudio_group_id)); // simulate timeout seconds passed, alarm executing fake_osi_alarm_set_on_mloop_.cb(fake_osi_alarm_set_on_mloop_.data); ASSERT_EQ(1, get_func_call_count("alarm_set_on_mloop")); LOG_INFO("OnStateTransitionTimeout"); /* Simulate On State timeout */ group->SetTargetState(types::AseState::BTA_LE_AUDIO_ASE_STATE_IDLE); group->ClearAllCises(); group->PrintDebugState(); InjectAclDisconnected(group, leAudioDevice); /* Verify that all ASEs are inactive and reconfiguration flag is cleared.*/ for (const auto& ase : leAudioDevice->ases_) { ASSERT_EQ(ase.state, types::AseState::BTA_LE_AUDIO_ASE_STATE_IDLE); ASSERT_EQ(ase.cis_state, types::CisState::IDLE); ASSERT_EQ(ase.data_path_state, types::DataPathState::IDLE); ASSERT_EQ(ase.reconfigure, 0); } } MATCHER_P(dataPathIsEq, expected, "") { return (arg.data_path_id == expected); } TEST_F(StateMachineTest, testConfigureDataPathForHost) { Loading