Loading system/bta/le_audio/devices.cc +29 −15 Original line number Diff line number Diff line Loading @@ -262,12 +262,16 @@ void LeAudioDeviceGroup::SetCigState(le_audio::types::CigState state) { cig_state_ = state; } bool LeAudioDeviceGroup::Activate(LeAudioContextType context_type) { bool LeAudioDeviceGroup::Activate( LeAudioContextType context_type, const BidirectionalPair<AudioContexts>& metadata_context_types, BidirectionalPair<std::vector<uint8_t>> ccid_lists) { bool is_activate = false; for (auto leAudioDevice : leAudioDevices_) { if (leAudioDevice.expired()) continue; bool activated = leAudioDevice.lock()->ActivateConfiguredAses(context_type); bool activated = leAudioDevice.lock()->ActivateConfiguredAses( context_type, metadata_context_types, ccid_lists); LOG_INFO("Device %s is %s", ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice.lock().get()->address_), activated ? "activated" : " not activated"); Loading Loading @@ -1656,18 +1660,7 @@ bool LeAudioDevice::ConfigureAses( ase->retrans_nb = ent.qos.retransmission_number; ase->max_transport_latency = ent.qos.max_transport_latency; /* Filter multidirectional audio context for each ase direction */ auto directional_audio_context = metadata_context_types.get(ase->direction) & GetAvailableContexts(ase->direction); if (directional_audio_context.any()) { ase->metadata = GetMetadata(directional_audio_context, ccid_lists.get(ase->direction)); } else { ase->metadata = GetMetadata(AudioContexts(LeAudioContextType::UNSPECIFIED), std::vector<uint8_t>()); } SetMetadataToAse(ase, metadata_context_types, ccid_lists); } LOG_DEBUG( Loading Loading @@ -2921,7 +2914,26 @@ void LeAudioDevice::SetAvailableContexts( avail_contexts_.source = contexts.source; } bool LeAudioDevice::ActivateConfiguredAses(LeAudioContextType context_type) { void LeAudioDevice::SetMetadataToAse( struct types::ase* ase, const BidirectionalPair<AudioContexts>& metadata_context_types, BidirectionalPair<std::vector<uint8_t>> ccid_lists) { /* Filter multidirectional audio context for each ase direction */ auto directional_audio_context = metadata_context_types.get(ase->direction) & GetAvailableContexts(ase->direction); if (directional_audio_context.any()) { ase->metadata = GetMetadata(directional_audio_context, ccid_lists.get(ase->direction)); } else { ase->metadata = GetMetadata(AudioContexts(LeAudioContextType::UNSPECIFIED), std::vector<uint8_t>()); } } bool LeAudioDevice::ActivateConfiguredAses( LeAudioContextType context_type, const BidirectionalPair<AudioContexts>& metadata_context_types, BidirectionalPair<std::vector<uint8_t>> ccid_lists) { if (conn_id_ == GATT_INVALID_CONN_ID) { LOG_WARN(" Device %s is not connected ", ADDRESS_TO_LOGGABLE_CSTR(address_)); Loading @@ -2939,6 +2951,8 @@ bool LeAudioDevice::ActivateConfiguredAses(LeAudioContextType context_type) { conn_id_, ase.id, ase.cis_id, ase.cis_conn_hdl); ase.active = true; ret = true; /* update metadata */ SetMetadataToAse(&ase, metadata_context_types, ccid_lists); } } Loading system/bta/le_audio/devices.h +14 −2 Original line number Diff line number Diff line Loading @@ -216,7 +216,16 @@ class LeAudioDevice { types::BidirectionalPair<types::AudioContexts> cont_val); void DeactivateAllAses(void); bool ActivateConfiguredAses(types::LeAudioContextType context_type); bool ActivateConfiguredAses( types::LeAudioContextType context_type, const types::BidirectionalPair<types::AudioContexts>& metadata_context_types, types::BidirectionalPair<std::vector<uint8_t>> ccid_lists); void SetMetadataToAse( struct types::ase* ase, const types::BidirectionalPair<types::AudioContexts>& metadata_context_types, types::BidirectionalPair<std::vector<uint8_t>> ccid_lists); void PrintDebugState(void); void DumpPacsDebugState(std::stringstream& stream); Loading Loading @@ -329,7 +338,10 @@ class LeAudioDeviceGroup { int Size(void) const; int NumOfConnected(types::LeAudioContextType context_type = types::LeAudioContextType::RFU) const; bool Activate(types::LeAudioContextType context_type); bool Activate(types::LeAudioContextType context_type, const types::BidirectionalPair<types::AudioContexts>& metadata_context_types, types::BidirectionalPair<std::vector<uint8_t>> ccid_lists); void Deactivate(void); types::CigState GetCigState(void) const; void SetCigState(le_audio::types::CigState state); Loading system/bta/le_audio/devices_test.cc +2 −1 Original line number Diff line number Diff line Loading @@ -1590,7 +1590,8 @@ TEST_F(LeAudioAseConfigurationTest, test_reactivation_conversational) { * the same CIS ID can be used. This would lead to only activating group * without reconfiguring CIG. */ group_->Activate(LeAudioContextType::CONVERSATIONAL); group_->Activate(LeAudioContextType::CONVERSATIONAL, audio_contexts, ccid_lists); TestActiveAses(); Loading system/bta/le_audio/le_audio_client_test.cc +98 −0 Original line number Diff line number Diff line Loading @@ -5277,6 +5277,104 @@ TEST_F(UnicastTest, TwoEarbudsStreamingContextSwitchReconfigure) { Mock::VerifyAndClearExpectations(&mock_le_audio_source_hal_client_); } TEST_F(UnicastTest, TwoReconfigureAndVerifyEnableContextType) { uint8_t group_size = 2; int group_id = 2; /* Scenario * 1. Earbuds streaming MEDIA * 2. Reconfigure to VOIP * 3. Check if Metadata in Enable command are set to CONVERSATIONAL */ // Report working CSIS ON_CALL(mock_csis_client_module_, IsCsisClientRunning()) .WillByDefault(Return(true)); // First earbud const RawAddress test_address0 = GetTestAddress(0); EXPECT_CALL(mock_btif_storage_, AddLeaudioAutoconnect(test_address0, true)) .Times(1); ConnectCsisDevice(test_address0, 1 /*conn_id*/, codec_spec_conf::kLeAudioLocationFrontLeft, codec_spec_conf::kLeAudioLocationFrontLeft, group_size, group_id, 1 /* rank*/); // Second earbud const RawAddress test_address1 = GetTestAddress(1); EXPECT_CALL(mock_btif_storage_, AddLeaudioAutoconnect(test_address1, true)) .Times(1); ConnectCsisDevice(test_address1, 2 /*conn_id*/, codec_spec_conf::kLeAudioLocationFrontRight, codec_spec_conf::kLeAudioLocationFrontRight, group_size, group_id, 2 /* rank*/, true /*connect_through_csis*/); constexpr int gmcs_ccid = 1; constexpr int gtbs_ccid = 2; ON_CALL(mock_csis_client_module_, GetDesiredSize(group_id)) .WillByDefault(Invoke([&](int group_id) { return 2; })); // Start streaming MEDIA EXPECT_CALL(*mock_le_audio_source_hal_client_, Start(_, _)).Times(1); EXPECT_CALL(*mock_le_audio_sink_hal_client_, Start(_, _)).Times(1); LeAudioClient::Get()->SetCcidInformation(gmcs_ccid, 4 /* Media */); LeAudioClient::Get()->SetCcidInformation(gtbs_ccid, 2 /* Phone */); LeAudioClient::Get()->GroupSetActive(group_id); SyncOnMainLoop(); // Update metadata on local audio sink UpdateLocalSinkMetadata(AUDIO_SOURCE_MIC); types::BidirectionalPair<std::vector<uint8_t>> ccids = {.sink = {gmcs_ccid}, .source = {}}; EXPECT_CALL(mock_state_machine_, StartStream(_, _, _, ccids)).Times(1); StartStreaming(AUDIO_USAGE_MEDIA, AUDIO_CONTENT_TYPE_MUSIC, group_id); SyncOnMainLoop(); Mock::VerifyAndClearExpectations(&mock_state_machine_); Mock::VerifyAndClearExpectations(&mock_audio_hal_client_callbacks_); Mock::VerifyAndClearExpectations(&mock_le_audio_source_hal_client_); // Verify Data transfer on two peer sinks uint8_t cis_count_out = 2; uint8_t cis_count_in = 0; TestAudioDataTransfer(group_id, cis_count_out, cis_count_in, 1920); // Conversational is a bidirectional scenario so expect GTBS CCID // in the metadata for both directions. Can be called twice when one // direction resume after the other and metadata is updated. ccids = {.sink = {gtbs_ccid}, .source = {gtbs_ccid}}; types::BidirectionalPair<types::AudioContexts> conversiational_contexts = { .sink = types::AudioContexts(types::LeAudioContextType::CONVERSATIONAL), .source = types::AudioContexts(types::LeAudioContextType::CONVERSATIONAL)}; EXPECT_CALL( mock_state_machine_, ConfigureStream(_, types::LeAudioContextType::CONVERSATIONAL, _, _)) .Times(AtLeast(1)); // Update metadata and resume UpdateLocalSourceMetadata(AUDIO_USAGE_VOICE_COMMUNICATION, AUDIO_CONTENT_TYPE_SPEECH, true); SyncOnMainLoop(); Mock::VerifyAndClearExpectations(&mock_state_machine_); Mock::VerifyAndClearExpectations(&mock_audio_hal_client_callbacks_); Mock::VerifyAndClearExpectations(&mock_le_audio_source_hal_client_); EXPECT_CALL(mock_state_machine_, StartStream(_, types::LeAudioContextType::CONVERSATIONAL, conversiational_contexts, ccids)) .Times(AtLeast(1)); LocalAudioSourceResume(true); SyncOnMainLoop(); Mock::VerifyAndClearExpectations(&mock_state_machine_); } TEST_F(UnicastTest, TwoEarbuds2ndLateConnect) { uint8_t group_size = 2; int group_id = 2; Loading system/bta/le_audio/state_machine.cc +3 −1 Original line number Diff line number Diff line Loading @@ -176,8 +176,10 @@ class LeAudioGroupStateMachineImpl : public LeAudioGroupStateMachine { switch (group->GetState()) { case AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED: if (group->IsConfiguredForContext(context_type)) { if (group->Activate(context_type)) { if (group->Activate(context_type, metadata_context_types, ccid_lists)) { SetTargetState(group, AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING); if (CigCreate(group)) { return true; } Loading Loading
system/bta/le_audio/devices.cc +29 −15 Original line number Diff line number Diff line Loading @@ -262,12 +262,16 @@ void LeAudioDeviceGroup::SetCigState(le_audio::types::CigState state) { cig_state_ = state; } bool LeAudioDeviceGroup::Activate(LeAudioContextType context_type) { bool LeAudioDeviceGroup::Activate( LeAudioContextType context_type, const BidirectionalPair<AudioContexts>& metadata_context_types, BidirectionalPair<std::vector<uint8_t>> ccid_lists) { bool is_activate = false; for (auto leAudioDevice : leAudioDevices_) { if (leAudioDevice.expired()) continue; bool activated = leAudioDevice.lock()->ActivateConfiguredAses(context_type); bool activated = leAudioDevice.lock()->ActivateConfiguredAses( context_type, metadata_context_types, ccid_lists); LOG_INFO("Device %s is %s", ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice.lock().get()->address_), activated ? "activated" : " not activated"); Loading Loading @@ -1656,18 +1660,7 @@ bool LeAudioDevice::ConfigureAses( ase->retrans_nb = ent.qos.retransmission_number; ase->max_transport_latency = ent.qos.max_transport_latency; /* Filter multidirectional audio context for each ase direction */ auto directional_audio_context = metadata_context_types.get(ase->direction) & GetAvailableContexts(ase->direction); if (directional_audio_context.any()) { ase->metadata = GetMetadata(directional_audio_context, ccid_lists.get(ase->direction)); } else { ase->metadata = GetMetadata(AudioContexts(LeAudioContextType::UNSPECIFIED), std::vector<uint8_t>()); } SetMetadataToAse(ase, metadata_context_types, ccid_lists); } LOG_DEBUG( Loading Loading @@ -2921,7 +2914,26 @@ void LeAudioDevice::SetAvailableContexts( avail_contexts_.source = contexts.source; } bool LeAudioDevice::ActivateConfiguredAses(LeAudioContextType context_type) { void LeAudioDevice::SetMetadataToAse( struct types::ase* ase, const BidirectionalPair<AudioContexts>& metadata_context_types, BidirectionalPair<std::vector<uint8_t>> ccid_lists) { /* Filter multidirectional audio context for each ase direction */ auto directional_audio_context = metadata_context_types.get(ase->direction) & GetAvailableContexts(ase->direction); if (directional_audio_context.any()) { ase->metadata = GetMetadata(directional_audio_context, ccid_lists.get(ase->direction)); } else { ase->metadata = GetMetadata(AudioContexts(LeAudioContextType::UNSPECIFIED), std::vector<uint8_t>()); } } bool LeAudioDevice::ActivateConfiguredAses( LeAudioContextType context_type, const BidirectionalPair<AudioContexts>& metadata_context_types, BidirectionalPair<std::vector<uint8_t>> ccid_lists) { if (conn_id_ == GATT_INVALID_CONN_ID) { LOG_WARN(" Device %s is not connected ", ADDRESS_TO_LOGGABLE_CSTR(address_)); Loading @@ -2939,6 +2951,8 @@ bool LeAudioDevice::ActivateConfiguredAses(LeAudioContextType context_type) { conn_id_, ase.id, ase.cis_id, ase.cis_conn_hdl); ase.active = true; ret = true; /* update metadata */ SetMetadataToAse(&ase, metadata_context_types, ccid_lists); } } Loading
system/bta/le_audio/devices.h +14 −2 Original line number Diff line number Diff line Loading @@ -216,7 +216,16 @@ class LeAudioDevice { types::BidirectionalPair<types::AudioContexts> cont_val); void DeactivateAllAses(void); bool ActivateConfiguredAses(types::LeAudioContextType context_type); bool ActivateConfiguredAses( types::LeAudioContextType context_type, const types::BidirectionalPair<types::AudioContexts>& metadata_context_types, types::BidirectionalPair<std::vector<uint8_t>> ccid_lists); void SetMetadataToAse( struct types::ase* ase, const types::BidirectionalPair<types::AudioContexts>& metadata_context_types, types::BidirectionalPair<std::vector<uint8_t>> ccid_lists); void PrintDebugState(void); void DumpPacsDebugState(std::stringstream& stream); Loading Loading @@ -329,7 +338,10 @@ class LeAudioDeviceGroup { int Size(void) const; int NumOfConnected(types::LeAudioContextType context_type = types::LeAudioContextType::RFU) const; bool Activate(types::LeAudioContextType context_type); bool Activate(types::LeAudioContextType context_type, const types::BidirectionalPair<types::AudioContexts>& metadata_context_types, types::BidirectionalPair<std::vector<uint8_t>> ccid_lists); void Deactivate(void); types::CigState GetCigState(void) const; void SetCigState(le_audio::types::CigState state); Loading
system/bta/le_audio/devices_test.cc +2 −1 Original line number Diff line number Diff line Loading @@ -1590,7 +1590,8 @@ TEST_F(LeAudioAseConfigurationTest, test_reactivation_conversational) { * the same CIS ID can be used. This would lead to only activating group * without reconfiguring CIG. */ group_->Activate(LeAudioContextType::CONVERSATIONAL); group_->Activate(LeAudioContextType::CONVERSATIONAL, audio_contexts, ccid_lists); TestActiveAses(); Loading
system/bta/le_audio/le_audio_client_test.cc +98 −0 Original line number Diff line number Diff line Loading @@ -5277,6 +5277,104 @@ TEST_F(UnicastTest, TwoEarbudsStreamingContextSwitchReconfigure) { Mock::VerifyAndClearExpectations(&mock_le_audio_source_hal_client_); } TEST_F(UnicastTest, TwoReconfigureAndVerifyEnableContextType) { uint8_t group_size = 2; int group_id = 2; /* Scenario * 1. Earbuds streaming MEDIA * 2. Reconfigure to VOIP * 3. Check if Metadata in Enable command are set to CONVERSATIONAL */ // Report working CSIS ON_CALL(mock_csis_client_module_, IsCsisClientRunning()) .WillByDefault(Return(true)); // First earbud const RawAddress test_address0 = GetTestAddress(0); EXPECT_CALL(mock_btif_storage_, AddLeaudioAutoconnect(test_address0, true)) .Times(1); ConnectCsisDevice(test_address0, 1 /*conn_id*/, codec_spec_conf::kLeAudioLocationFrontLeft, codec_spec_conf::kLeAudioLocationFrontLeft, group_size, group_id, 1 /* rank*/); // Second earbud const RawAddress test_address1 = GetTestAddress(1); EXPECT_CALL(mock_btif_storage_, AddLeaudioAutoconnect(test_address1, true)) .Times(1); ConnectCsisDevice(test_address1, 2 /*conn_id*/, codec_spec_conf::kLeAudioLocationFrontRight, codec_spec_conf::kLeAudioLocationFrontRight, group_size, group_id, 2 /* rank*/, true /*connect_through_csis*/); constexpr int gmcs_ccid = 1; constexpr int gtbs_ccid = 2; ON_CALL(mock_csis_client_module_, GetDesiredSize(group_id)) .WillByDefault(Invoke([&](int group_id) { return 2; })); // Start streaming MEDIA EXPECT_CALL(*mock_le_audio_source_hal_client_, Start(_, _)).Times(1); EXPECT_CALL(*mock_le_audio_sink_hal_client_, Start(_, _)).Times(1); LeAudioClient::Get()->SetCcidInformation(gmcs_ccid, 4 /* Media */); LeAudioClient::Get()->SetCcidInformation(gtbs_ccid, 2 /* Phone */); LeAudioClient::Get()->GroupSetActive(group_id); SyncOnMainLoop(); // Update metadata on local audio sink UpdateLocalSinkMetadata(AUDIO_SOURCE_MIC); types::BidirectionalPair<std::vector<uint8_t>> ccids = {.sink = {gmcs_ccid}, .source = {}}; EXPECT_CALL(mock_state_machine_, StartStream(_, _, _, ccids)).Times(1); StartStreaming(AUDIO_USAGE_MEDIA, AUDIO_CONTENT_TYPE_MUSIC, group_id); SyncOnMainLoop(); Mock::VerifyAndClearExpectations(&mock_state_machine_); Mock::VerifyAndClearExpectations(&mock_audio_hal_client_callbacks_); Mock::VerifyAndClearExpectations(&mock_le_audio_source_hal_client_); // Verify Data transfer on two peer sinks uint8_t cis_count_out = 2; uint8_t cis_count_in = 0; TestAudioDataTransfer(group_id, cis_count_out, cis_count_in, 1920); // Conversational is a bidirectional scenario so expect GTBS CCID // in the metadata for both directions. Can be called twice when one // direction resume after the other and metadata is updated. ccids = {.sink = {gtbs_ccid}, .source = {gtbs_ccid}}; types::BidirectionalPair<types::AudioContexts> conversiational_contexts = { .sink = types::AudioContexts(types::LeAudioContextType::CONVERSATIONAL), .source = types::AudioContexts(types::LeAudioContextType::CONVERSATIONAL)}; EXPECT_CALL( mock_state_machine_, ConfigureStream(_, types::LeAudioContextType::CONVERSATIONAL, _, _)) .Times(AtLeast(1)); // Update metadata and resume UpdateLocalSourceMetadata(AUDIO_USAGE_VOICE_COMMUNICATION, AUDIO_CONTENT_TYPE_SPEECH, true); SyncOnMainLoop(); Mock::VerifyAndClearExpectations(&mock_state_machine_); Mock::VerifyAndClearExpectations(&mock_audio_hal_client_callbacks_); Mock::VerifyAndClearExpectations(&mock_le_audio_source_hal_client_); EXPECT_CALL(mock_state_machine_, StartStream(_, types::LeAudioContextType::CONVERSATIONAL, conversiational_contexts, ccids)) .Times(AtLeast(1)); LocalAudioSourceResume(true); SyncOnMainLoop(); Mock::VerifyAndClearExpectations(&mock_state_machine_); } TEST_F(UnicastTest, TwoEarbuds2ndLateConnect) { uint8_t group_size = 2; int group_id = 2; Loading
system/bta/le_audio/state_machine.cc +3 −1 Original line number Diff line number Diff line Loading @@ -176,8 +176,10 @@ class LeAudioGroupStateMachineImpl : public LeAudioGroupStateMachine { switch (group->GetState()) { case AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED: if (group->IsConfiguredForContext(context_type)) { if (group->Activate(context_type)) { if (group->Activate(context_type, metadata_context_types, ccid_lists)) { SetTargetState(group, AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING); if (CigCreate(group)) { return true; } Loading