Loading system/bta/le_audio/device_groups.cc +3 −2 Original line number Diff line number Diff line Loading @@ -763,13 +763,14 @@ bool LeAudioDeviceGroup::UpdateAudioSetConfigurationCache( if (update_config) { context_to_configuration_cache_map[ctx_type] = std::pair(true, new_conf); LOG_DEBUG("config: %s -> %s", ToHexString(ctx_type).c_str(), LOG_INFO("config: %s -> %s", ToHexString(ctx_type).c_str(), (new_conf ? new_conf->name.c_str() : "(none)")); } return update_config; } void LeAudioDeviceGroup::InvalidateCachedConfigurations(void) { LOG_INFO(" Group id: %d", group_id_); context_to_configuration_cache_map.clear(); } Loading system/bta/le_audio/state_machine.cc +5 −0 Original line number Diff line number Diff line Loading @@ -173,6 +173,11 @@ class LeAudioGroupStateMachineImpl : public LeAudioGroupStateMachine { return false; } /* Invalidate configuration to make sure it is chosen properly when new * member connects */ group->InvalidateCachedConfigurations(); if (!group->Configure(group->GetConfigurationContextType(), group->GetMetadataContexts(), ccids)) { LOG_ERROR(" failed to set ASE configuration"); Loading system/bta/le_audio/state_machine_test.cc +121 −0 Original line number Diff line number Diff line Loading @@ -4377,6 +4377,127 @@ TEST_F(StateMachineTest, testAttachDeviceToTheStream) { ASSERT_NE(ase->retrans_nb, 0); } TEST_F(StateMachineTest, testAttachDeviceToTheStreamV2) { const auto context_type = kContextTypeMedia; const auto leaudio_group_id = 6; const auto num_devices = 2; /* Scenario * 1. Both devices streaming * 2. One device disconnects * 3. Audio configuration resume and configuration cache is rebuilt * 4. Device attached */ 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); /* Force update configuration which is what happens when stream stops * and starts while streaming to single dev. This will rebuild cache, * which is what we need in this test. */ group->UpdateAudioSetConfigurationCache(context_type); // 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); // 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)); 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, testAttachDeviceToTheStreamDeviceNoAvailableContext) { const auto context_type = kContextTypeMedia; const auto leaudio_group_id = 6; Loading Loading
system/bta/le_audio/device_groups.cc +3 −2 Original line number Diff line number Diff line Loading @@ -763,13 +763,14 @@ bool LeAudioDeviceGroup::UpdateAudioSetConfigurationCache( if (update_config) { context_to_configuration_cache_map[ctx_type] = std::pair(true, new_conf); LOG_DEBUG("config: %s -> %s", ToHexString(ctx_type).c_str(), LOG_INFO("config: %s -> %s", ToHexString(ctx_type).c_str(), (new_conf ? new_conf->name.c_str() : "(none)")); } return update_config; } void LeAudioDeviceGroup::InvalidateCachedConfigurations(void) { LOG_INFO(" Group id: %d", group_id_); context_to_configuration_cache_map.clear(); } Loading
system/bta/le_audio/state_machine.cc +5 −0 Original line number Diff line number Diff line Loading @@ -173,6 +173,11 @@ class LeAudioGroupStateMachineImpl : public LeAudioGroupStateMachine { return false; } /* Invalidate configuration to make sure it is chosen properly when new * member connects */ group->InvalidateCachedConfigurations(); if (!group->Configure(group->GetConfigurationContextType(), group->GetMetadataContexts(), ccids)) { LOG_ERROR(" failed to set ASE configuration"); Loading
system/bta/le_audio/state_machine_test.cc +121 −0 Original line number Diff line number Diff line Loading @@ -4377,6 +4377,127 @@ TEST_F(StateMachineTest, testAttachDeviceToTheStream) { ASSERT_NE(ase->retrans_nb, 0); } TEST_F(StateMachineTest, testAttachDeviceToTheStreamV2) { const auto context_type = kContextTypeMedia; const auto leaudio_group_id = 6; const auto num_devices = 2; /* Scenario * 1. Both devices streaming * 2. One device disconnects * 3. Audio configuration resume and configuration cache is rebuilt * 4. Device attached */ 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); /* Force update configuration which is what happens when stream stops * and starts while streaming to single dev. This will rebuild cache, * which is what we need in this test. */ group->UpdateAudioSetConfigurationCache(context_type); // 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); // 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)); 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, testAttachDeviceToTheStreamDeviceNoAvailableContext) { const auto context_type = kContextTypeMedia; const auto leaudio_group_id = 6; Loading