Loading system/bta/le_audio/state_machine.cc +12 −2 Original line number Diff line number Diff line Loading @@ -806,7 +806,8 @@ class LeAudioGroupStateMachineImpl : public LeAudioGroupStateMachine { /* Cis establishment may came after setting group state to streaming, e.g. * for autonomous scenario when ase is sink */ if (group->GetState() == AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING && group->IsGroupStreamReady()) { group->GetFirstActiveDeviceByDataPathState( AudioStreamDataPathState::CIS_ESTABLISHED)) { /* No more transition for group */ cancel_watchdog_if_needed(group->group_id_); PrepareDataPath(group); Loading Loading @@ -2609,8 +2610,17 @@ class LeAudioGroupStateMachineImpl : public LeAudioGroupStateMachine { return; } /* This case may happen because of the reconnection device. */ if (group->GetState() == AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) { /* We are here because of the reconnection of the single device. */ /* Not all CISes establish evens came */ if (group->GetFirstActiveDeviceByDataPathState( AudioStreamDataPathState::CIS_PENDING)) return; /* Streaming status notification came after setting data path */ if (!group->GetFirstActiveDeviceByDataPathState( AudioStreamDataPathState::CIS_ESTABLISHED)) return; PrepareDataPath(group); return; } Loading system/bta/le_audio/state_machine_test.cc +120 −0 Original line number Diff line number Diff line Loading @@ -177,6 +177,7 @@ class StateMachineTest : public Test { /* Use to simulated error status on Cis creation */ bool overwrite_cis_status_; bool do_not_send_cis_establish_event_; uint8_t overwrite_cis_status_idx_; std::vector<uint8_t> cis_status_; Loading @@ -190,6 +191,7 @@ class StateMachineTest : public Test { overwrite_cis_status_idx_ = 0; overwrite_cis_status_ = false; do_not_send_cis_establish_event_ = false; cis_status_.clear(); ::le_audio::AudioSetConfigurationProvider::Initialize(); Loading Loading @@ -362,6 +364,11 @@ class StateMachineTest : public Test { conn_params) { DLOG(INFO) << "EstablishCis"; if (do_not_send_cis_establish_event_) { DLOG(INFO) << "Don't send cis establish event"; return; } for (auto& pair : conn_params.conn_pairs) { auto dev_it = std::find_if( le_audio_devices_.begin(), le_audio_devices_.end(), Loading Loading @@ -4353,5 +4360,118 @@ TEST_F(StateMachineTest, StreamStartWithDifferentContextFromConfiguredState) { testing::Mock::VerifyAndClearExpectations(&mock_callbacks_); } TEST_F(StateMachineTest, testAttachDeviceToTheStreamCisFailure) { const auto context_type = kContextTypeMedia; const auto leaudio_group_id = 6; const auto num_devices = 2; ContentControlIdKeeper::GetInstance()->SetCcid(media_context, media_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); PrepareConfigureQosHandler(group); PrepareEnableHandler(group); PrepareDisableHandler(group); PrepareReleaseHandler(group); auto* leAudioDevice = group->GetFirstDevice(); LeAudioDevice* lastDevice; LeAudioDevice* fistDevice = leAudioDevice; auto expected_devices_written = 0; while (leAudioDevice) { /* Three Writes: * 1: Codec Config * 2: Codec QoS * 3: Enabling */ lastDevice = leAudioDevice; EXPECT_CALL(gatt_queue, WriteCharacteristic(leAudioDevice->conn_id_, leAudioDevice->ctp_hdls_.val_hdl, _, GATT_WRITE_NO_RSP, _, _)) .Times(AtLeast(3)); 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); InjectInitialIdleNotification(group); // 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_iso_manager_); // Inject CIS and ACL disconnection of first device InjectCisDisconnected(group, lastDevice, HCI_ERR_CONNECTION_TOUT); InjectAclDisconnected(group, lastDevice); // Check if group keeps streaming ASSERT_EQ(group->GetState(), types::AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING); lastDevice->conn_id_ = 3; lastDevice->SetConnectionState(DeviceConnectState::CONNECTED); group->UpdateAudioContextTypeAvailability(); // Make sure ASE with disconnected CIS are not left in STREAMING ASSERT_EQ(lastDevice->GetFirstAseWithState( ::le_audio::types::kLeAudioDirectionSink, types::AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING), nullptr); ASSERT_EQ(lastDevice->GetFirstAseWithState( ::le_audio::types::kLeAudioDirectionSource, types::AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING), nullptr); EXPECT_CALL(gatt_queue, WriteCharacteristic(lastDevice->conn_id_, lastDevice->ctp_hdls_.val_hdl, _, GATT_WRITE_NO_RSP, _, _)) .Times(AtLeast(3)); do_not_send_cis_establish_event_ = true; EXPECT_CALL(*mock_iso_manager_, EstablishCis(_)).Times(1); EXPECT_CALL(*mock_iso_manager_, SetupIsoDataPath(_, _)).Times(0); LeAudioGroupStateMachine::Get()->AttachToStream(group, lastDevice); // Check if group keeps streaming ASSERT_EQ(group->GetState(), types::AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING); // Verify that the joining device receives the right CCID list auto lastMeta = lastDevice->GetFirstActiveAse()->metadata; bool parsedOk = false; auto ltv = le_audio::types::LeAudioLtvMap::Parse(lastMeta.data(), lastMeta.size(), parsedOk); ASSERT_TRUE(parsedOk); auto ccids = ltv.Find(le_audio::types::kLeAudioMetadataTypeCcidList); ASSERT_TRUE(ccids.has_value()); ASSERT_NE(std::find(ccids->begin(), ccids->end(), media_ccid), ccids->end()); /* Verify that ASE of first device are still good*/ auto ase = fistDevice->GetFirstActiveAse(); ASSERT_NE(ase->max_transport_latency, 0); ASSERT_NE(ase->retrans_nb, 0); } } // namespace internal } // namespace le_audio Loading
system/bta/le_audio/state_machine.cc +12 −2 Original line number Diff line number Diff line Loading @@ -806,7 +806,8 @@ class LeAudioGroupStateMachineImpl : public LeAudioGroupStateMachine { /* Cis establishment may came after setting group state to streaming, e.g. * for autonomous scenario when ase is sink */ if (group->GetState() == AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING && group->IsGroupStreamReady()) { group->GetFirstActiveDeviceByDataPathState( AudioStreamDataPathState::CIS_ESTABLISHED)) { /* No more transition for group */ cancel_watchdog_if_needed(group->group_id_); PrepareDataPath(group); Loading Loading @@ -2609,8 +2610,17 @@ class LeAudioGroupStateMachineImpl : public LeAudioGroupStateMachine { return; } /* This case may happen because of the reconnection device. */ if (group->GetState() == AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) { /* We are here because of the reconnection of the single device. */ /* Not all CISes establish evens came */ if (group->GetFirstActiveDeviceByDataPathState( AudioStreamDataPathState::CIS_PENDING)) return; /* Streaming status notification came after setting data path */ if (!group->GetFirstActiveDeviceByDataPathState( AudioStreamDataPathState::CIS_ESTABLISHED)) return; PrepareDataPath(group); return; } Loading
system/bta/le_audio/state_machine_test.cc +120 −0 Original line number Diff line number Diff line Loading @@ -177,6 +177,7 @@ class StateMachineTest : public Test { /* Use to simulated error status on Cis creation */ bool overwrite_cis_status_; bool do_not_send_cis_establish_event_; uint8_t overwrite_cis_status_idx_; std::vector<uint8_t> cis_status_; Loading @@ -190,6 +191,7 @@ class StateMachineTest : public Test { overwrite_cis_status_idx_ = 0; overwrite_cis_status_ = false; do_not_send_cis_establish_event_ = false; cis_status_.clear(); ::le_audio::AudioSetConfigurationProvider::Initialize(); Loading Loading @@ -362,6 +364,11 @@ class StateMachineTest : public Test { conn_params) { DLOG(INFO) << "EstablishCis"; if (do_not_send_cis_establish_event_) { DLOG(INFO) << "Don't send cis establish event"; return; } for (auto& pair : conn_params.conn_pairs) { auto dev_it = std::find_if( le_audio_devices_.begin(), le_audio_devices_.end(), Loading Loading @@ -4353,5 +4360,118 @@ TEST_F(StateMachineTest, StreamStartWithDifferentContextFromConfiguredState) { testing::Mock::VerifyAndClearExpectations(&mock_callbacks_); } TEST_F(StateMachineTest, testAttachDeviceToTheStreamCisFailure) { const auto context_type = kContextTypeMedia; const auto leaudio_group_id = 6; const auto num_devices = 2; ContentControlIdKeeper::GetInstance()->SetCcid(media_context, media_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); PrepareConfigureQosHandler(group); PrepareEnableHandler(group); PrepareDisableHandler(group); PrepareReleaseHandler(group); auto* leAudioDevice = group->GetFirstDevice(); LeAudioDevice* lastDevice; LeAudioDevice* fistDevice = leAudioDevice; auto expected_devices_written = 0; while (leAudioDevice) { /* Three Writes: * 1: Codec Config * 2: Codec QoS * 3: Enabling */ lastDevice = leAudioDevice; EXPECT_CALL(gatt_queue, WriteCharacteristic(leAudioDevice->conn_id_, leAudioDevice->ctp_hdls_.val_hdl, _, GATT_WRITE_NO_RSP, _, _)) .Times(AtLeast(3)); 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); InjectInitialIdleNotification(group); // 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_iso_manager_); // Inject CIS and ACL disconnection of first device InjectCisDisconnected(group, lastDevice, HCI_ERR_CONNECTION_TOUT); InjectAclDisconnected(group, lastDevice); // Check if group keeps streaming ASSERT_EQ(group->GetState(), types::AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING); lastDevice->conn_id_ = 3; lastDevice->SetConnectionState(DeviceConnectState::CONNECTED); group->UpdateAudioContextTypeAvailability(); // Make sure ASE with disconnected CIS are not left in STREAMING ASSERT_EQ(lastDevice->GetFirstAseWithState( ::le_audio::types::kLeAudioDirectionSink, types::AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING), nullptr); ASSERT_EQ(lastDevice->GetFirstAseWithState( ::le_audio::types::kLeAudioDirectionSource, types::AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING), nullptr); EXPECT_CALL(gatt_queue, WriteCharacteristic(lastDevice->conn_id_, lastDevice->ctp_hdls_.val_hdl, _, GATT_WRITE_NO_RSP, _, _)) .Times(AtLeast(3)); do_not_send_cis_establish_event_ = true; EXPECT_CALL(*mock_iso_manager_, EstablishCis(_)).Times(1); EXPECT_CALL(*mock_iso_manager_, SetupIsoDataPath(_, _)).Times(0); LeAudioGroupStateMachine::Get()->AttachToStream(group, lastDevice); // Check if group keeps streaming ASSERT_EQ(group->GetState(), types::AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING); // Verify that the joining device receives the right CCID list auto lastMeta = lastDevice->GetFirstActiveAse()->metadata; bool parsedOk = false; auto ltv = le_audio::types::LeAudioLtvMap::Parse(lastMeta.data(), lastMeta.size(), parsedOk); ASSERT_TRUE(parsedOk); auto ccids = ltv.Find(le_audio::types::kLeAudioMetadataTypeCcidList); ASSERT_TRUE(ccids.has_value()); ASSERT_NE(std::find(ccids->begin(), ccids->end(), media_ccid), ccids->end()); /* Verify that ASE of first device are still good*/ auto ase = fistDevice->GetFirstActiveAse(); ASSERT_NE(ase->max_transport_latency, 0); ASSERT_NE(ase->retrans_nb, 0); } } // namespace internal } // namespace le_audio