Loading system/bta/le_audio/state_machine.cc +2 −2 Original line number Diff line number Diff line Loading @@ -1228,14 +1228,14 @@ public: * In such an event, there is need to notify upper layer about state * from here. */ cancel_watchdog_if_needed(group->group_id_); if (current_group_state == AseState::BTA_LE_AUDIO_ASE_STATE_IDLE) { cancel_watchdog_if_needed(group->group_id_); log::info("Cises disconnected for group {}, we are good in Idle state.", group->group_id_); ReleaseCisIds(group); state_machine_callbacks_->StatusReportCb(group->group_id_, GroupStreamStatus::IDLE); } else if (current_group_state == AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED) { cancel_watchdog_if_needed(group->group_id_); auto reconfig = group->IsPendingConfiguration(); log::info( "Cises disconnected for group: {}, we are good in Configured " Loading system/bta/le_audio/state_machine_test.cc +181 −0 Original line number Diff line number Diff line Loading @@ -766,6 +766,15 @@ protected: } } void InjectCachedConfigurationForGroup(LeAudioDeviceGroup* group) { auto leAudioDevice = group->GetFirstActiveDevice(); while (leAudioDevice) { log::info("Group : {}, dev: {}", group->group_id_, leAudioDevice->address_); InjectCachedConfigurationForActiveAses(group, leAudioDevice); leAudioDevice = group->GetNextActiveDevice(leAudioDevice); } } void InjectStreamingStateFroActiveAses(LeAudioDeviceGroup* group, LeAudioDevice* device) { for (auto& ase : device->ases_) { if (!ase.active) { Loading Loading @@ -3806,6 +3815,178 @@ static void InjectCisDisconnected(LeAudioDeviceGroup* group, LeAudioDevice* leAu } } TEST_F(StateMachineTest, testReleaseMultiple_CisDisconnectedBeforeGettingToIdleState) { const auto context_type = kContextTypeMedia; const auto leaudio_group_id = 6; const auto num_devices = 2; /* Test Scenario: * 1. Start stream * 2. Stop the stream * 3. While stopping, make sure that CISes are disconnected before current state is IDLE - verify * watchdog keeps running * 4. Move to IDLE, make sure watchdog is cleared */ auto* group = PrepareSingleTestDeviceGroup(leaudio_group_id, context_type, num_devices); ASSERT_EQ(group->Size(), num_devices); PrepareConfigureCodecHandler(group); PrepareConfigureQosHandler(group); PrepareEnableHandler(group); PrepareDisableHandler(group); PrepareReleaseHandler(group); stay_in_releasing_state_ = true; auto* leAudioDevice = group->GetFirstDevice(); auto expected_devices_written = 0; while (leAudioDevice) { EXPECT_CALL(gatt_queue, WriteCharacteristic(leAudioDevice->conn_id_, leAudioDevice->ctp_hdls_.val_hdl, _, GATT_WRITE_NO_RSP, _, _)) .Times(AtLeast(4)); expected_devices_written++; leAudioDevice = group->GetNextDevice(leAudioDevice); } ASSERT_EQ(expected_devices_written, num_devices); EXPECT_CALL(*mock_iso_manager_, CreateCig(_, _)).Times(1); EXPECT_CALL(*mock_iso_manager_, EstablishCis(_)).Times(1); EXPECT_CALL(*mock_iso_manager_, SetupIsoDataPath(_, _)).Times(2); EXPECT_CALL(*mock_iso_manager_, RemoveIsoDataPath(_, _)).Times(2); EXPECT_CALL(*mock_iso_manager_, DisconnectCis(_, _)).Times(2); EXPECT_CALL(*mock_iso_manager_, RemoveCig(_, _)).Times(1); InjectInitialIdleNotification(group); EXPECT_CALL(mock_callbacks_, StatusReportCb(leaudio_group_id, bluetooth::le_audio::GroupStreamStatus::STREAMING)) .Times(1); // Start the configuration and stream Media content LeAudioGroupStateMachine::Get()->StartStream(group, context_type, {.sink = types::AudioContexts(context_type), .source = types::AudioContexts(context_type)}); // Check if group has transitioned to a proper state ASSERT_EQ(group->GetState(), types::AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING); ASSERT_EQ(1, get_func_call_count("alarm_cancel")); reset_mock_function_count_map(); testing::Mock::VerifyAndClearExpectations(&mock_callbacks_); // 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::IDLE)); EXPECT_CALL(mock_callbacks_, StatusReportCb(leaudio_group_id, bluetooth::le_audio::GroupStreamStatus::STREAMING)) .Times(0); // Stop the stream LeAudioGroupStateMachine::Get()->StopStream(group); // Watchdog shall not be cancled here. ASSERT_EQ(0, get_func_call_count("alarm_cancel")); InjectReleaseAndIdleStateForAGroup(group, false, true); // Check if group has transitioned to a proper state ASSERT_EQ(group->GetState(), types::AseState::BTA_LE_AUDIO_ASE_STATE_IDLE); ASSERT_EQ(1, get_func_call_count("alarm_cancel")); testing::Mock::VerifyAndClearExpectations(&mock_callbacks_); testing::Mock::VerifyAndClearExpectations(mock_iso_manager_); } TEST_F(StateMachineTest, testReleaseMultiple_CisDisconnectedBeforeGettingToConfiguredState) { const auto context_type = kContextTypeMedia; const auto leaudio_group_id = 6; const auto num_devices = 2; /* Test Scenario: * 1. Start stream * 2. Stop the stream * 3. While stopping, make sure that CISes are disconnected before current state is CONFIGURED - * verify watchdog keeps running * 4. Move to CONFIGURED, make sure watchdog is cleared */ auto* group = PrepareSingleTestDeviceGroup(leaudio_group_id, context_type, num_devices); ASSERT_EQ(group->Size(), num_devices); PrepareConfigureCodecHandler(group, 0, true); PrepareConfigureQosHandler(group, 0, true); PrepareEnableHandler(group); PrepareDisableHandler(group); PrepareReleaseHandler(group); stay_in_releasing_state_ = true; auto* leAudioDevice = group->GetFirstDevice(); auto expected_devices_written = 0; while (leAudioDevice) { EXPECT_CALL(gatt_queue, WriteCharacteristic(leAudioDevice->conn_id_, leAudioDevice->ctp_hdls_.val_hdl, _, GATT_WRITE_NO_RSP, _, _)) .Times(AtLeast(4)); expected_devices_written++; leAudioDevice = group->GetNextDevice(leAudioDevice); } ASSERT_EQ(expected_devices_written, num_devices); EXPECT_CALL(*mock_iso_manager_, CreateCig(_, _)).Times(1); EXPECT_CALL(*mock_iso_manager_, EstablishCis(_)).Times(1); EXPECT_CALL(*mock_iso_manager_, SetupIsoDataPath(_, _)).Times(2); EXPECT_CALL(*mock_iso_manager_, RemoveIsoDataPath(_, _)).Times(2); EXPECT_CALL(*mock_iso_manager_, DisconnectCis(_, _)).Times(2); EXPECT_CALL(*mock_iso_manager_, RemoveCig(_, _)).Times(1); InjectInitialIdleNotification(group); EXPECT_CALL(mock_callbacks_, StatusReportCb(leaudio_group_id, bluetooth::le_audio::GroupStreamStatus::STREAMING)) .Times(1); // Start the configuration and stream Media content LeAudioGroupStateMachine::Get()->StartStream(group, context_type, {.sink = types::AudioContexts(context_type), .source = types::AudioContexts(context_type)}); // Check if group has transitioned to a proper state ASSERT_EQ(group->GetState(), types::AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING); 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)); EXPECT_CALL(mock_callbacks_, StatusReportCb(leaudio_group_id, bluetooth::le_audio::GroupStreamStatus::STREAMING)) .Times(0); // Stop the stream LeAudioGroupStateMachine::Get()->StopStream(group); // Watchdog shall not be cancled here. ASSERT_EQ(0, get_func_call_count("alarm_cancel")); InjectCachedConfigurationForGroup(group); // Check if group has transitioned to a proper state ASSERT_EQ(group->GetState(), types::AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED); ASSERT_EQ(1, get_func_call_count("alarm_cancel")); testing::Mock::VerifyAndClearExpectations(&mock_callbacks_); testing::Mock::VerifyAndClearExpectations(mock_iso_manager_); } TEST_F(StateMachineTest, testAutonomousReleaseMultiple) { const auto context_type = kContextTypeMedia; const auto leaudio_group_id = 6; Loading Loading
system/bta/le_audio/state_machine.cc +2 −2 Original line number Diff line number Diff line Loading @@ -1228,14 +1228,14 @@ public: * In such an event, there is need to notify upper layer about state * from here. */ cancel_watchdog_if_needed(group->group_id_); if (current_group_state == AseState::BTA_LE_AUDIO_ASE_STATE_IDLE) { cancel_watchdog_if_needed(group->group_id_); log::info("Cises disconnected for group {}, we are good in Idle state.", group->group_id_); ReleaseCisIds(group); state_machine_callbacks_->StatusReportCb(group->group_id_, GroupStreamStatus::IDLE); } else if (current_group_state == AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED) { cancel_watchdog_if_needed(group->group_id_); auto reconfig = group->IsPendingConfiguration(); log::info( "Cises disconnected for group: {}, we are good in Configured " Loading
system/bta/le_audio/state_machine_test.cc +181 −0 Original line number Diff line number Diff line Loading @@ -766,6 +766,15 @@ protected: } } void InjectCachedConfigurationForGroup(LeAudioDeviceGroup* group) { auto leAudioDevice = group->GetFirstActiveDevice(); while (leAudioDevice) { log::info("Group : {}, dev: {}", group->group_id_, leAudioDevice->address_); InjectCachedConfigurationForActiveAses(group, leAudioDevice); leAudioDevice = group->GetNextActiveDevice(leAudioDevice); } } void InjectStreamingStateFroActiveAses(LeAudioDeviceGroup* group, LeAudioDevice* device) { for (auto& ase : device->ases_) { if (!ase.active) { Loading Loading @@ -3806,6 +3815,178 @@ static void InjectCisDisconnected(LeAudioDeviceGroup* group, LeAudioDevice* leAu } } TEST_F(StateMachineTest, testReleaseMultiple_CisDisconnectedBeforeGettingToIdleState) { const auto context_type = kContextTypeMedia; const auto leaudio_group_id = 6; const auto num_devices = 2; /* Test Scenario: * 1. Start stream * 2. Stop the stream * 3. While stopping, make sure that CISes are disconnected before current state is IDLE - verify * watchdog keeps running * 4. Move to IDLE, make sure watchdog is cleared */ auto* group = PrepareSingleTestDeviceGroup(leaudio_group_id, context_type, num_devices); ASSERT_EQ(group->Size(), num_devices); PrepareConfigureCodecHandler(group); PrepareConfigureQosHandler(group); PrepareEnableHandler(group); PrepareDisableHandler(group); PrepareReleaseHandler(group); stay_in_releasing_state_ = true; auto* leAudioDevice = group->GetFirstDevice(); auto expected_devices_written = 0; while (leAudioDevice) { EXPECT_CALL(gatt_queue, WriteCharacteristic(leAudioDevice->conn_id_, leAudioDevice->ctp_hdls_.val_hdl, _, GATT_WRITE_NO_RSP, _, _)) .Times(AtLeast(4)); expected_devices_written++; leAudioDevice = group->GetNextDevice(leAudioDevice); } ASSERT_EQ(expected_devices_written, num_devices); EXPECT_CALL(*mock_iso_manager_, CreateCig(_, _)).Times(1); EXPECT_CALL(*mock_iso_manager_, EstablishCis(_)).Times(1); EXPECT_CALL(*mock_iso_manager_, SetupIsoDataPath(_, _)).Times(2); EXPECT_CALL(*mock_iso_manager_, RemoveIsoDataPath(_, _)).Times(2); EXPECT_CALL(*mock_iso_manager_, DisconnectCis(_, _)).Times(2); EXPECT_CALL(*mock_iso_manager_, RemoveCig(_, _)).Times(1); InjectInitialIdleNotification(group); EXPECT_CALL(mock_callbacks_, StatusReportCb(leaudio_group_id, bluetooth::le_audio::GroupStreamStatus::STREAMING)) .Times(1); // Start the configuration and stream Media content LeAudioGroupStateMachine::Get()->StartStream(group, context_type, {.sink = types::AudioContexts(context_type), .source = types::AudioContexts(context_type)}); // Check if group has transitioned to a proper state ASSERT_EQ(group->GetState(), types::AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING); ASSERT_EQ(1, get_func_call_count("alarm_cancel")); reset_mock_function_count_map(); testing::Mock::VerifyAndClearExpectations(&mock_callbacks_); // 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::IDLE)); EXPECT_CALL(mock_callbacks_, StatusReportCb(leaudio_group_id, bluetooth::le_audio::GroupStreamStatus::STREAMING)) .Times(0); // Stop the stream LeAudioGroupStateMachine::Get()->StopStream(group); // Watchdog shall not be cancled here. ASSERT_EQ(0, get_func_call_count("alarm_cancel")); InjectReleaseAndIdleStateForAGroup(group, false, true); // Check if group has transitioned to a proper state ASSERT_EQ(group->GetState(), types::AseState::BTA_LE_AUDIO_ASE_STATE_IDLE); ASSERT_EQ(1, get_func_call_count("alarm_cancel")); testing::Mock::VerifyAndClearExpectations(&mock_callbacks_); testing::Mock::VerifyAndClearExpectations(mock_iso_manager_); } TEST_F(StateMachineTest, testReleaseMultiple_CisDisconnectedBeforeGettingToConfiguredState) { const auto context_type = kContextTypeMedia; const auto leaudio_group_id = 6; const auto num_devices = 2; /* Test Scenario: * 1. Start stream * 2. Stop the stream * 3. While stopping, make sure that CISes are disconnected before current state is CONFIGURED - * verify watchdog keeps running * 4. Move to CONFIGURED, make sure watchdog is cleared */ auto* group = PrepareSingleTestDeviceGroup(leaudio_group_id, context_type, num_devices); ASSERT_EQ(group->Size(), num_devices); PrepareConfigureCodecHandler(group, 0, true); PrepareConfigureQosHandler(group, 0, true); PrepareEnableHandler(group); PrepareDisableHandler(group); PrepareReleaseHandler(group); stay_in_releasing_state_ = true; auto* leAudioDevice = group->GetFirstDevice(); auto expected_devices_written = 0; while (leAudioDevice) { EXPECT_CALL(gatt_queue, WriteCharacteristic(leAudioDevice->conn_id_, leAudioDevice->ctp_hdls_.val_hdl, _, GATT_WRITE_NO_RSP, _, _)) .Times(AtLeast(4)); expected_devices_written++; leAudioDevice = group->GetNextDevice(leAudioDevice); } ASSERT_EQ(expected_devices_written, num_devices); EXPECT_CALL(*mock_iso_manager_, CreateCig(_, _)).Times(1); EXPECT_CALL(*mock_iso_manager_, EstablishCis(_)).Times(1); EXPECT_CALL(*mock_iso_manager_, SetupIsoDataPath(_, _)).Times(2); EXPECT_CALL(*mock_iso_manager_, RemoveIsoDataPath(_, _)).Times(2); EXPECT_CALL(*mock_iso_manager_, DisconnectCis(_, _)).Times(2); EXPECT_CALL(*mock_iso_manager_, RemoveCig(_, _)).Times(1); InjectInitialIdleNotification(group); EXPECT_CALL(mock_callbacks_, StatusReportCb(leaudio_group_id, bluetooth::le_audio::GroupStreamStatus::STREAMING)) .Times(1); // Start the configuration and stream Media content LeAudioGroupStateMachine::Get()->StartStream(group, context_type, {.sink = types::AudioContexts(context_type), .source = types::AudioContexts(context_type)}); // Check if group has transitioned to a proper state ASSERT_EQ(group->GetState(), types::AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING); 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)); EXPECT_CALL(mock_callbacks_, StatusReportCb(leaudio_group_id, bluetooth::le_audio::GroupStreamStatus::STREAMING)) .Times(0); // Stop the stream LeAudioGroupStateMachine::Get()->StopStream(group); // Watchdog shall not be cancled here. ASSERT_EQ(0, get_func_call_count("alarm_cancel")); InjectCachedConfigurationForGroup(group); // Check if group has transitioned to a proper state ASSERT_EQ(group->GetState(), types::AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED); ASSERT_EQ(1, get_func_call_count("alarm_cancel")); testing::Mock::VerifyAndClearExpectations(&mock_callbacks_); testing::Mock::VerifyAndClearExpectations(mock_iso_manager_); } TEST_F(StateMachineTest, testAutonomousReleaseMultiple) { const auto context_type = kContextTypeMedia; const auto leaudio_group_id = 6; Loading