Loading system/bta/le_audio/client.cc +39 −2 Original line number Diff line number Diff line Loading @@ -361,6 +361,16 @@ class LeAudioClientImpl : public LeAudioClient { group_add_node(group_id, address); } /* If device participates in streaming the group, it has to be stopped and * group needs to be reconfigured if needed to new configuration without * considering this removing device. */ void SetDeviceAsRemovePendingAndStopGroup(LeAudioDevice* leAudioDevice) { LOG_INFO("device %s", leAudioDevice->address_.ToString().c_str()); leAudioDevice->SetConnectionState(DeviceConnectState::PENDING_REMOVAL); GroupStop(leAudioDevice->group_id_); } void OnGroupMemberAddedCb(const RawAddress& address, int group_id) { LOG(INFO) << __func__ << " address: " << address << " group_id: " << group_id; Loading Loading @@ -388,8 +398,9 @@ class LeAudioClientImpl : public LeAudioClient { LeAudioDevice* leAudioDevice = leAudioDevices_.FindByAddress(address); if (!leAudioDevice) return; if (leAudioDevice->group_id_ == bluetooth::groups::kGroupUnknown) { LOG(INFO) << __func__ << " device already not assigned to the group."; if (leAudioDevice->group_id_ != group_id) { LOG_WARN("Device: %s not assigned to the group.", leAudioDevice->address_.ToString().c_str()); return; } Loading @@ -401,6 +412,11 @@ class LeAudioClientImpl : public LeAudioClient { return; } if (leAudioDevice->HaveActiveAse()) { SetDeviceAsRemovePendingAndStopGroup(leAudioDevice); return; } group_remove_node(group, address); } Loading Loading @@ -690,6 +706,11 @@ class LeAudioClientImpl : public LeAudioClient { return; } if (leAudioDevice->HaveActiveAse()) { SetDeviceAsRemovePendingAndStopGroup(leAudioDevice); return; } group_remove_node(group, address, true); } Loading Loading @@ -3993,6 +4014,21 @@ class LeAudioClientImpl : public LeAudioClient { } } void HandlePendingDeviceRemove(LeAudioDeviceGroup* group) { for (auto device = group->GetFirstDevice(); device != nullptr; device = group->GetNextDevice(device)) { if (device->GetConnectionState() == DeviceConnectState::PENDING_REMOVAL) { if (device->closing_stream_for_disconnection_) { device->closing_stream_for_disconnection_ = false; LOG_INFO("Disconnecting group id: %d, address: %s", group->group_id_, device->address_.ToString().c_str()); DisconnectDevice(device); } group_remove_node(group, device->address_, true); } } } void HandlePendingDeviceDisconnection(LeAudioDeviceGroup* group) { LOG_DEBUG(); auto leAudioDevice = group->GetFirstDevice(); Loading Loading @@ -4172,6 +4208,7 @@ class LeAudioClientImpl : public LeAudioClient { if (group) { NotifyUpperLayerGroupTurnedIdleDuringCall(group->group_id_); HandlePendingAvailableContextsChange(group); HandlePendingDeviceRemove(group); HandlePendingDeviceDisconnection(group); } break; Loading system/bta/le_audio/devices.cc +3 −0 Original line number Diff line number Diff line Loading @@ -71,6 +71,9 @@ std::ostream& operator<<(std::ostream& os, const DeviceConnectState& state) { case DeviceConnectState::DISCONNECTING: char_value_ = "DISCONNECTING"; break; case DeviceConnectState::PENDING_REMOVAL: char_value_ = "PENDING_REMOVAL"; break; case DeviceConnectState::CONNECTING_BY_USER: char_value_ = "CONNECTING_BY_USER"; break; Loading system/bta/le_audio/devices.h +4 −0 Original line number Diff line number Diff line Loading @@ -46,6 +46,10 @@ enum class DeviceConnectState : uint8_t { REMOVING, /* Disconnecting */ DISCONNECTING, /* Device will be removed after scheduled action is finished: One of such * action is taking Stream to IDLE */ PENDING_REMOVAL, /* 2 states below are used when user creates connection. Connect API is called. */ CONNECTING_BY_USER, Loading system/bta/le_audio/le_audio_client_test.cc +69 −0 Original line number Diff line number Diff line Loading @@ -2845,6 +2845,75 @@ TEST_F(UnicastTest, GroupingAddRemove) { ASSERT_NE(std::find(devs.begin(), devs.end(), test_address1), devs.end()); } TEST_F(UnicastTest, RemoveNodeWhileStreaming) { const RawAddress test_address0 = GetTestAddress(0); int group_id = bluetooth::groups::kGroupUnknown; SetSampleDatabaseEarbudsValid( 1, test_address0, codec_spec_conf::kLeAudioLocationStereo, codec_spec_conf::kLeAudioLocationStereo, default_channel_cnt, default_channel_cnt, 0x0004, /* source sample freq 16khz */ false /*add_csis*/, true /*add_cas*/, true /*add_pacs*/, default_ase_cnt /*add_ascs_cnt*/, 1 /*set_size*/, 0 /*rank*/); EXPECT_CALL(mock_audio_hal_client_callbacks_, OnConnectionState(ConnectionState::CONNECTED, test_address0)) .Times(1); EXPECT_CALL(mock_audio_hal_client_callbacks_, OnGroupNodeStatus(test_address0, _, GroupNodeStatus::ADDED)) .WillOnce(DoAll(SaveArg<1>(&group_id))); ConnectLeAudio(test_address0); ASSERT_NE(group_id, bluetooth::groups::kGroupUnknown); // Start streaming constexpr uint8_t cis_count_out = 1; constexpr uint8_t cis_count_in = 0; constexpr int gmcs_ccid = 1; constexpr int gtbs_ccid = 2; // Audio sessions are started only when device gets active 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); EXPECT_CALL(mock_state_machine_, StartStream(_, _, _, {{gmcs_ccid}})) .Times(1); StartStreaming(AUDIO_USAGE_MEDIA, AUDIO_CONTENT_TYPE_MUSIC, group_id); SyncOnMainLoop(); Mock::VerifyAndClearExpectations(&mock_audio_hal_client_callbacks_); Mock::VerifyAndClearExpectations(&mock_le_audio_source_hal_client_); Mock::VerifyAndClearExpectations(&mock_state_machine_); SyncOnMainLoop(); // Verify Data transfer on one audio source cis TestAudioDataTransfer(group_id, cis_count_out, cis_count_in, 1920); EXPECT_CALL(mock_groups_module_, RemoveDevice(test_address0, group_id)) .Times(1); EXPECT_CALL(mock_state_machine_, StopStream(_)).Times(1); EXPECT_CALL(mock_state_machine_, ProcessHciNotifAclDisconnected(_, _)) .Times(0); EXPECT_CALL( mock_audio_hal_client_callbacks_, OnGroupNodeStatus(test_address0, group_id, GroupNodeStatus::REMOVED)); EXPECT_CALL(mock_audio_hal_client_callbacks_, OnConnectionState(ConnectionState::DISCONNECTED, test_address0)) .Times(0); LeAudioClient::Get()->GroupRemoveNode(group_id, test_address0); SyncOnMainLoop(); Mock::VerifyAndClearExpectations(&mock_groups_module_); Mock::VerifyAndClearExpectations(&mock_state_machine_); Mock::VerifyAndClearExpectations(&mock_audio_hal_client_callbacks_); } TEST_F(UnicastTest, GroupingAddTwiceNoRemove) { // Earbud connects without known grouping uint8_t group_id0 = bluetooth::groups::kGroupUnknown; Loading Loading
system/bta/le_audio/client.cc +39 −2 Original line number Diff line number Diff line Loading @@ -361,6 +361,16 @@ class LeAudioClientImpl : public LeAudioClient { group_add_node(group_id, address); } /* If device participates in streaming the group, it has to be stopped and * group needs to be reconfigured if needed to new configuration without * considering this removing device. */ void SetDeviceAsRemovePendingAndStopGroup(LeAudioDevice* leAudioDevice) { LOG_INFO("device %s", leAudioDevice->address_.ToString().c_str()); leAudioDevice->SetConnectionState(DeviceConnectState::PENDING_REMOVAL); GroupStop(leAudioDevice->group_id_); } void OnGroupMemberAddedCb(const RawAddress& address, int group_id) { LOG(INFO) << __func__ << " address: " << address << " group_id: " << group_id; Loading Loading @@ -388,8 +398,9 @@ class LeAudioClientImpl : public LeAudioClient { LeAudioDevice* leAudioDevice = leAudioDevices_.FindByAddress(address); if (!leAudioDevice) return; if (leAudioDevice->group_id_ == bluetooth::groups::kGroupUnknown) { LOG(INFO) << __func__ << " device already not assigned to the group."; if (leAudioDevice->group_id_ != group_id) { LOG_WARN("Device: %s not assigned to the group.", leAudioDevice->address_.ToString().c_str()); return; } Loading @@ -401,6 +412,11 @@ class LeAudioClientImpl : public LeAudioClient { return; } if (leAudioDevice->HaveActiveAse()) { SetDeviceAsRemovePendingAndStopGroup(leAudioDevice); return; } group_remove_node(group, address); } Loading Loading @@ -690,6 +706,11 @@ class LeAudioClientImpl : public LeAudioClient { return; } if (leAudioDevice->HaveActiveAse()) { SetDeviceAsRemovePendingAndStopGroup(leAudioDevice); return; } group_remove_node(group, address, true); } Loading Loading @@ -3993,6 +4014,21 @@ class LeAudioClientImpl : public LeAudioClient { } } void HandlePendingDeviceRemove(LeAudioDeviceGroup* group) { for (auto device = group->GetFirstDevice(); device != nullptr; device = group->GetNextDevice(device)) { if (device->GetConnectionState() == DeviceConnectState::PENDING_REMOVAL) { if (device->closing_stream_for_disconnection_) { device->closing_stream_for_disconnection_ = false; LOG_INFO("Disconnecting group id: %d, address: %s", group->group_id_, device->address_.ToString().c_str()); DisconnectDevice(device); } group_remove_node(group, device->address_, true); } } } void HandlePendingDeviceDisconnection(LeAudioDeviceGroup* group) { LOG_DEBUG(); auto leAudioDevice = group->GetFirstDevice(); Loading Loading @@ -4172,6 +4208,7 @@ class LeAudioClientImpl : public LeAudioClient { if (group) { NotifyUpperLayerGroupTurnedIdleDuringCall(group->group_id_); HandlePendingAvailableContextsChange(group); HandlePendingDeviceRemove(group); HandlePendingDeviceDisconnection(group); } break; Loading
system/bta/le_audio/devices.cc +3 −0 Original line number Diff line number Diff line Loading @@ -71,6 +71,9 @@ std::ostream& operator<<(std::ostream& os, const DeviceConnectState& state) { case DeviceConnectState::DISCONNECTING: char_value_ = "DISCONNECTING"; break; case DeviceConnectState::PENDING_REMOVAL: char_value_ = "PENDING_REMOVAL"; break; case DeviceConnectState::CONNECTING_BY_USER: char_value_ = "CONNECTING_BY_USER"; break; Loading
system/bta/le_audio/devices.h +4 −0 Original line number Diff line number Diff line Loading @@ -46,6 +46,10 @@ enum class DeviceConnectState : uint8_t { REMOVING, /* Disconnecting */ DISCONNECTING, /* Device will be removed after scheduled action is finished: One of such * action is taking Stream to IDLE */ PENDING_REMOVAL, /* 2 states below are used when user creates connection. Connect API is called. */ CONNECTING_BY_USER, Loading
system/bta/le_audio/le_audio_client_test.cc +69 −0 Original line number Diff line number Diff line Loading @@ -2845,6 +2845,75 @@ TEST_F(UnicastTest, GroupingAddRemove) { ASSERT_NE(std::find(devs.begin(), devs.end(), test_address1), devs.end()); } TEST_F(UnicastTest, RemoveNodeWhileStreaming) { const RawAddress test_address0 = GetTestAddress(0); int group_id = bluetooth::groups::kGroupUnknown; SetSampleDatabaseEarbudsValid( 1, test_address0, codec_spec_conf::kLeAudioLocationStereo, codec_spec_conf::kLeAudioLocationStereo, default_channel_cnt, default_channel_cnt, 0x0004, /* source sample freq 16khz */ false /*add_csis*/, true /*add_cas*/, true /*add_pacs*/, default_ase_cnt /*add_ascs_cnt*/, 1 /*set_size*/, 0 /*rank*/); EXPECT_CALL(mock_audio_hal_client_callbacks_, OnConnectionState(ConnectionState::CONNECTED, test_address0)) .Times(1); EXPECT_CALL(mock_audio_hal_client_callbacks_, OnGroupNodeStatus(test_address0, _, GroupNodeStatus::ADDED)) .WillOnce(DoAll(SaveArg<1>(&group_id))); ConnectLeAudio(test_address0); ASSERT_NE(group_id, bluetooth::groups::kGroupUnknown); // Start streaming constexpr uint8_t cis_count_out = 1; constexpr uint8_t cis_count_in = 0; constexpr int gmcs_ccid = 1; constexpr int gtbs_ccid = 2; // Audio sessions are started only when device gets active 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); EXPECT_CALL(mock_state_machine_, StartStream(_, _, _, {{gmcs_ccid}})) .Times(1); StartStreaming(AUDIO_USAGE_MEDIA, AUDIO_CONTENT_TYPE_MUSIC, group_id); SyncOnMainLoop(); Mock::VerifyAndClearExpectations(&mock_audio_hal_client_callbacks_); Mock::VerifyAndClearExpectations(&mock_le_audio_source_hal_client_); Mock::VerifyAndClearExpectations(&mock_state_machine_); SyncOnMainLoop(); // Verify Data transfer on one audio source cis TestAudioDataTransfer(group_id, cis_count_out, cis_count_in, 1920); EXPECT_CALL(mock_groups_module_, RemoveDevice(test_address0, group_id)) .Times(1); EXPECT_CALL(mock_state_machine_, StopStream(_)).Times(1); EXPECT_CALL(mock_state_machine_, ProcessHciNotifAclDisconnected(_, _)) .Times(0); EXPECT_CALL( mock_audio_hal_client_callbacks_, OnGroupNodeStatus(test_address0, group_id, GroupNodeStatus::REMOVED)); EXPECT_CALL(mock_audio_hal_client_callbacks_, OnConnectionState(ConnectionState::DISCONNECTED, test_address0)) .Times(0); LeAudioClient::Get()->GroupRemoveNode(group_id, test_address0); SyncOnMainLoop(); Mock::VerifyAndClearExpectations(&mock_groups_module_); Mock::VerifyAndClearExpectations(&mock_state_machine_); Mock::VerifyAndClearExpectations(&mock_audio_hal_client_callbacks_); } TEST_F(UnicastTest, GroupingAddTwiceNoRemove) { // Earbud connects without known grouping uint8_t group_id0 = bluetooth::groups::kGroupUnknown; Loading