Loading system/bta/le_audio/state_machine.cc +26 −2 Original line number Diff line number Diff line Loading @@ -1797,7 +1797,19 @@ class LeAudioGroupStateMachineImpl : public LeAudioGroupStateMachine { if (group->GetState() == AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) { /* We are here because of the reconnection of the single device. */ /* Make sure that device is ready to be configured as we could also * get here triggered by the remote device. If device is not connected * yet, we should wait for the stack to trigger adding device to the * stream */ if (leAudioDevice->GetConnectionState() == le_audio::DeviceConnectState::CONNECTED) { PrepareAndSendConfigQos(group, leAudioDevice); } else { LOG_DEBUG( "Device %s initiated configured state but it is not yet ready " "to be configured", ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_)); } return; } Loading Loading @@ -1896,7 +1908,19 @@ class LeAudioGroupStateMachineImpl : public LeAudioGroupStateMachine { if (group->GetState() == AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) { /* We are here because of the reconnection of the single device. */ /* Make sure that device is ready to be configured as we could also * get here triggered by the remote device. If device is not connected * yet, we should wait for the stack to trigger adding device to the * stream */ if (leAudioDevice->GetConnectionState() == le_audio::DeviceConnectState::CONNECTED) { PrepareAndSendConfigQos(group, leAudioDevice); } else { LOG_DEBUG( "Device %s initiated configured state but it is not yet ready " "to be configured", ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_)); } return; } Loading system/bta/le_audio/state_machine_test.cc +147 −0 Original line number Diff line number Diff line Loading @@ -3942,6 +3942,153 @@ TEST_F(StateMachineTest, testAttachDeviceToTheStream) { ASSERT_NE(ase->retrans_nb, 0); } TEST_F(StateMachineTest, testAutonomousConfiguredAndAttachToStream) { const auto context_type = kContextTypeMedia; const auto leaudio_group_id = 6; const auto num_devices = 2; /* Scenario * 1. Start streaming * 2. Stop stream on one device * 3. Reconnect * 4. Autonomous Configured state * 5. Make sure QoS Configure is not send out * 6. Trigger attach the stream * 7. Make sure stream is up */ 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, 0, true); 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); /* Set device is getting ready for the connection */ lastDevice->conn_id_ = 3; lastDevice->SetConnectionState( DeviceConnectState::CONNECTED_AUTOCONNECT_GETTING_READY); // 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); // Symulate remote autonomous CONFIGURE state EXPECT_CALL(gatt_queue, WriteCharacteristic(lastDevice->conn_id_, lastDevice->ctp_hdls_.val_hdl, _, GATT_WRITE_NO_RSP, _, _)) .Times(0); int num_of_notifications = 0; for (auto& ase : lastDevice->ases_) { if (ase.id == le_audio::types::ase::kAseIdInvalid) { continue; } LOG_ERROR("ID : %d, status %s", ase.id, bluetooth::common::ToString(ase.state).c_str()); num_of_notifications++; InjectAseStateNotification(&ase, lastDevice, group, ascs::kAseStateCodecConfigured, &cached_codec_configuration_map_[ase.id]); break; } ASSERT_EQ(num_of_notifications, 1); testing::Mock::VerifyAndClearExpectations(&gatt_queue); // Now device is connected. Attach it to the stream lastDevice->SetConnectionState(DeviceConnectState::CONNECTED); EXPECT_CALL(gatt_queue, WriteCharacteristic(lastDevice->conn_id_, lastDevice->ctp_hdls_.val_hdl, _, GATT_WRITE_NO_RSP, _, _)) .Times(AtLeast(3)); EXPECT_CALL(*mock_iso_manager_, EstablishCis(_)).Times(1); EXPECT_CALL(*mock_iso_manager_, SetupIsoDataPath(_, _)).Times(1); LeAudioGroupStateMachine::Get()->AttachToStream( group, lastDevice, {.sink = {media_ccid}, .source = {}}); // 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); } TEST_F(StateMachineTest, testAttachDeviceToTheStream_autonomusQoSConfiguredState) { const auto context_type = kContextTypeMedia; Loading system/stack/btm/btm_iso_impl.h +4 −1 Original line number Diff line number Diff line Loading @@ -303,7 +303,10 @@ struct iso_impl { LOG_ASSERT(cis) << "No such cis: " << +el.cis_conn_handle; LOG_ASSERT(!(cis->state_flags & (kStateFlagIsConnected | kStateFlagIsConnecting))) << "Already connected or connecting"; << "cis: " << +el.cis_conn_handle << " is already connected or connecting flags: " << +cis->state_flags << ", num of cis params: " << +conn_params.conn_pairs.size(); cis->state_flags |= kStateFlagIsConnecting; tBTM_SEC_DEV_REC* p_rec = btm_find_dev_by_handle(el.acl_conn_handle); Loading system/stack/test/btm_iso_test.cc +1 −1 Original line number Diff line number Diff line Loading @@ -1028,7 +1028,7 @@ TEST_F(IsoManagerDeathTest, ConnectSameCisTwice) { ASSERT_EXIT( IsoManager::GetInstance()->IsoManager::GetInstance()->EstablishCis( params), ::testing::KilledBySignal(SIGABRT), "Already connected or connecting"); ::testing::KilledBySignal(SIGABRT), "already connected or connecting"); } TEST_F(IsoManagerDeathTest, EstablishCisInvalidResponsePacket) { Loading Loading
system/bta/le_audio/state_machine.cc +26 −2 Original line number Diff line number Diff line Loading @@ -1797,7 +1797,19 @@ class LeAudioGroupStateMachineImpl : public LeAudioGroupStateMachine { if (group->GetState() == AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) { /* We are here because of the reconnection of the single device. */ /* Make sure that device is ready to be configured as we could also * get here triggered by the remote device. If device is not connected * yet, we should wait for the stack to trigger adding device to the * stream */ if (leAudioDevice->GetConnectionState() == le_audio::DeviceConnectState::CONNECTED) { PrepareAndSendConfigQos(group, leAudioDevice); } else { LOG_DEBUG( "Device %s initiated configured state but it is not yet ready " "to be configured", ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_)); } return; } Loading Loading @@ -1896,7 +1908,19 @@ class LeAudioGroupStateMachineImpl : public LeAudioGroupStateMachine { if (group->GetState() == AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) { /* We are here because of the reconnection of the single device. */ /* Make sure that device is ready to be configured as we could also * get here triggered by the remote device. If device is not connected * yet, we should wait for the stack to trigger adding device to the * stream */ if (leAudioDevice->GetConnectionState() == le_audio::DeviceConnectState::CONNECTED) { PrepareAndSendConfigQos(group, leAudioDevice); } else { LOG_DEBUG( "Device %s initiated configured state but it is not yet ready " "to be configured", ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_)); } return; } Loading
system/bta/le_audio/state_machine_test.cc +147 −0 Original line number Diff line number Diff line Loading @@ -3942,6 +3942,153 @@ TEST_F(StateMachineTest, testAttachDeviceToTheStream) { ASSERT_NE(ase->retrans_nb, 0); } TEST_F(StateMachineTest, testAutonomousConfiguredAndAttachToStream) { const auto context_type = kContextTypeMedia; const auto leaudio_group_id = 6; const auto num_devices = 2; /* Scenario * 1. Start streaming * 2. Stop stream on one device * 3. Reconnect * 4. Autonomous Configured state * 5. Make sure QoS Configure is not send out * 6. Trigger attach the stream * 7. Make sure stream is up */ 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, 0, true); 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); /* Set device is getting ready for the connection */ lastDevice->conn_id_ = 3; lastDevice->SetConnectionState( DeviceConnectState::CONNECTED_AUTOCONNECT_GETTING_READY); // 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); // Symulate remote autonomous CONFIGURE state EXPECT_CALL(gatt_queue, WriteCharacteristic(lastDevice->conn_id_, lastDevice->ctp_hdls_.val_hdl, _, GATT_WRITE_NO_RSP, _, _)) .Times(0); int num_of_notifications = 0; for (auto& ase : lastDevice->ases_) { if (ase.id == le_audio::types::ase::kAseIdInvalid) { continue; } LOG_ERROR("ID : %d, status %s", ase.id, bluetooth::common::ToString(ase.state).c_str()); num_of_notifications++; InjectAseStateNotification(&ase, lastDevice, group, ascs::kAseStateCodecConfigured, &cached_codec_configuration_map_[ase.id]); break; } ASSERT_EQ(num_of_notifications, 1); testing::Mock::VerifyAndClearExpectations(&gatt_queue); // Now device is connected. Attach it to the stream lastDevice->SetConnectionState(DeviceConnectState::CONNECTED); EXPECT_CALL(gatt_queue, WriteCharacteristic(lastDevice->conn_id_, lastDevice->ctp_hdls_.val_hdl, _, GATT_WRITE_NO_RSP, _, _)) .Times(AtLeast(3)); EXPECT_CALL(*mock_iso_manager_, EstablishCis(_)).Times(1); EXPECT_CALL(*mock_iso_manager_, SetupIsoDataPath(_, _)).Times(1); LeAudioGroupStateMachine::Get()->AttachToStream( group, lastDevice, {.sink = {media_ccid}, .source = {}}); // 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); } TEST_F(StateMachineTest, testAttachDeviceToTheStream_autonomusQoSConfiguredState) { const auto context_type = kContextTypeMedia; Loading
system/stack/btm/btm_iso_impl.h +4 −1 Original line number Diff line number Diff line Loading @@ -303,7 +303,10 @@ struct iso_impl { LOG_ASSERT(cis) << "No such cis: " << +el.cis_conn_handle; LOG_ASSERT(!(cis->state_flags & (kStateFlagIsConnected | kStateFlagIsConnecting))) << "Already connected or connecting"; << "cis: " << +el.cis_conn_handle << " is already connected or connecting flags: " << +cis->state_flags << ", num of cis params: " << +conn_params.conn_pairs.size(); cis->state_flags |= kStateFlagIsConnecting; tBTM_SEC_DEV_REC* p_rec = btm_find_dev_by_handle(el.acl_conn_handle); Loading
system/stack/test/btm_iso_test.cc +1 −1 Original line number Diff line number Diff line Loading @@ -1028,7 +1028,7 @@ TEST_F(IsoManagerDeathTest, ConnectSameCisTwice) { ASSERT_EXIT( IsoManager::GetInstance()->IsoManager::GetInstance()->EstablishCis( params), ::testing::KilledBySignal(SIGABRT), "Already connected or connecting"); ::testing::KilledBySignal(SIGABRT), "already connected or connecting"); } TEST_F(IsoManagerDeathTest, EstablishCisInvalidResponsePacket) { Loading