From b087eae2db98c638abe30571f50c7917a628ae32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Rymanowski?= <lukasz.rymanowski@codecoup.pl> Date: Wed, 3 Nov 2021 07:37:45 +0000 Subject: [PATCH] leaudio: Fix crash when removing device while streaming With this patch: 1) Device is removed from the group when it is disconnected 2) Empty group is removed only when related CIG does not exist Bug: 204945451 Bug: 150670922 Tag: #feature Sponsor: jpawlowski@ Test: atest --host bluetooth_le_audio_test bluetooth_le_audio_client_test Change-Id: I7079594253a504cd0320c6d4000c3d71cb6f0883 --- system/bta/le_audio/client.cc | 30 ++++--- system/bta/le_audio/le_audio_client_test.cc | 86 ++++++++++++++++++++- 2 files changed, 105 insertions(+), 11 deletions(-) diff --git a/system/bta/le_audio/client.cc b/system/bta/le_audio/client.cc index 6522d76a63b..763f8ebf5af 100644 --- a/system/bta/le_audio/client.cc +++ b/system/bta/le_audio/client.cc @@ -414,6 +414,12 @@ class LeAudioClientImpl : public LeAudioClient { group_id); } + void remove_group_if_possible(LeAudioDeviceGroup* group) { + if (group && group->IsEmpty() && !group->cig_created_) { + aseGroups_.Remove(group->group_id_); + } + } + void group_remove_node(LeAudioDeviceGroup* group, const RawAddress& address, bool update_group_module = false) { int group_id = group->group_id_; @@ -431,8 +437,7 @@ class LeAudioClientImpl : public LeAudioClient { /* Remove group if this was the last leAudioDevice in this group */ if (group->IsEmpty()) { - aseGroups_.Remove(group_id); - + remove_group_if_possible(group); return; } @@ -657,6 +662,12 @@ class LeAudioClientImpl : public LeAudioClient { return; } + if (leAudioDevice->conn_id_ != GATT_INVALID_CONN_ID) { + Disconnect(address); + leAudioDevice->removing_device_ = true; + return; + } + /* Remove the group assignment if not yet removed. It might happen that the * group module has already called the appropriate callback and we have * already removed the group assignment. @@ -666,12 +677,6 @@ class LeAudioClientImpl : public LeAudioClient { group_remove_node(group, address, true); } - if (leAudioDevice->conn_id_ != GATT_INVALID_CONN_ID) { - Disconnect(address); - leAudioDevice->removing_device_ = true; - return; - } - leAudioDevices_.Remove(address); } @@ -1181,7 +1186,13 @@ class LeAudioClientImpl : public LeAudioClient { leAudioDevice->conn_id_ = GATT_INVALID_CONN_ID; leAudioDevice->encrypted_ = false; - if (leAudioDevice->removing_device_) leAudioDevices_.Remove(address); + if (leAudioDevice->removing_device_) { + if (leAudioDevice->group_id_ != bluetooth::groups::kGroupUnknown) { + auto group = aseGroups_.FindById(leAudioDevice->group_id_); + group_remove_node(group, address, true); + } + leAudioDevices_.Remove(address); + } } bool subscribe_for_indications(uint16_t conn_id, const RawAddress& address, @@ -2524,6 +2535,7 @@ class LeAudioClientImpl : public LeAudioClient { auto* evt = static_cast<cig_remove_cmpl_evt*>(data); LeAudioDeviceGroup* group = aseGroups_.FindById(evt->cig_id); groupStateMachine_->ProcessHciNotifOnCigRemove(evt->status, group); + remove_group_if_possible(group); } break; default: LOG(ERROR) << __func__ << " Invalid event " << int{event_type}; diff --git a/system/bta/le_audio/le_audio_client_test.cc b/system/bta/le_audio/le_audio_client_test.cc index 8158ef2995e..ce15362bb54 100644 --- a/system/bta/le_audio/le_audio_client_test.cc +++ b/system/bta/le_audio/le_audio_client_test.cc @@ -528,6 +528,10 @@ class UnicastTestNoInit : public Test { state_machine_callbacks_->StatusReportCb( group->group_id_, GroupStreamStatus::STREAMING); streaming_groups[group->group_id_] = group; + + /* Assume CIG is created */ + group->cig_created_ = true; + return true; }); @@ -554,8 +558,8 @@ class UnicastTestNoInit : public Test { }); ON_CALL(mock_state_machine_, ProcessHciNotifAclDisconnected(_, _)) - .WillByDefault([](LeAudioDeviceGroup* group, - LeAudioDevice* leAudioDevice) { + .WillByDefault([this](LeAudioDeviceGroup* group, + LeAudioDevice* leAudioDevice) { if (!group) return; auto* stream_conf = &group->stream_conf; if (stream_conf->valid) { @@ -584,6 +588,11 @@ class UnicastTestNoInit : public Test { stream_conf->valid = false; } } + + if (group->IsEmpty()) { + group->cig_created_ = false; + InjectCigRemoved(group->group_id_); + } }); ON_CALL(mock_state_machine_, ProcessHciNotifCisDisconnected(_, _, _)) @@ -1548,6 +1557,16 @@ class UnicastTestNoInit : public Test { bluetooth::hci::iso_manager::kIsoEventCisDisconnected, &cis_evt); } + void InjectCigRemoved(uint8_t cig_id) { + bluetooth::hci::iso_manager::cig_remove_cmpl_evt evt; + evt.status = 0; + evt.cig_id = cig_id; + + ASSERT_NE(cig_callbacks_, nullptr); + cig_callbacks_->OnCisEvent( + bluetooth::hci::iso_manager::kIsoEventCigOnRemoveCmpl, &evt); + } + MockLeAudioClientCallbacks mock_client_callbacks_; MockLeAudioClientAudioSource mock_audio_source_; MockLeAudioClientAudioSink mock_audio_sink_; @@ -2227,6 +2246,69 @@ TEST_F(UnicastTest, RemoveTwoEarbudsCsisGrouped) { Mock::VerifyAndClearExpectations(&mock_btif_storage_); } +TEST_F(UnicastTest, RemoveWhileStreaming) { + const RawAddress test_address0 = GetTestAddress(0); + int group_id = bluetooth::groups::kGroupUnknown; + + SetSampleDatabaseEarbudsValid( + 1, test_address0, codec_spec_conf::kLeAudioLocationStereo, + codec_spec_conf::kLeAudioLocationStereo, false /*add_csis*/, + true /*add_cas*/, true /*add_pacs*/, true /*add_ascs*/, 1 /*set_size*/, + 0 /*rank*/); + EXPECT_CALL(mock_client_callbacks_, + OnConnectionState(ConnectionState::CONNECTED, test_address0)) + .Times(1); + EXPECT_CALL(mock_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 + uint8_t cis_count_out = 1; + uint8_t cis_count_in = 0; + + EXPECT_CALL(mock_audio_source_, Start(_, _)).Times(1); + LeAudioClient::Get()->GroupSetActive(group_id); + + EXPECT_CALL(mock_state_machine_, StartStream(_, _)).Times(1); + + StartStreaming(AUDIO_USAGE_MEDIA, AUDIO_CONTENT_TYPE_MUSIC, group_id); + + SyncOnMainLoop(); + Mock::VerifyAndClearExpectations(&mock_client_callbacks_); + Mock::VerifyAndClearExpectations(&mock_audio_source_); + 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); + + LeAudioDeviceGroup* group = nullptr; + EXPECT_CALL(mock_state_machine_, ProcessHciNotifAclDisconnected(_, _)) + .WillOnce(DoAll(SaveArg<0>(&group))); + EXPECT_CALL( + mock_client_callbacks_, + OnGroupNodeStatus(test_address0, group_id, GroupNodeStatus::REMOVED)); + + EXPECT_CALL(mock_client_callbacks_, + OnConnectionState(ConnectionState::DISCONNECTED, test_address0)) + .Times(1); + + LeAudioClient::Get()->RemoveDevice(test_address0); + + SyncOnMainLoop(); + Mock::VerifyAndClearExpectations(&mock_groups_module_); + Mock::VerifyAndClearExpectations(&mock_state_machine_); + Mock::VerifyAndClearExpectations(&mock_client_callbacks_); + + ASSERT_NE(group, nullptr); +} + TEST_F(UnicastTest, SpeakerStreaming) { const RawAddress test_address0 = GetTestAddress(0); int group_id = bluetooth::groups::kGroupUnknown; -- GitLab