aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorƁukasz Rymanowski <lukasz.rymanowski@codecoup.pl>2021-11-03 07:37:45 +0000
committerAlice Kuo <aliceypkuo@google.com>2021-11-03 11:19:07 +0000
commitfc6625b245433d4b59d2468864efb9b77ccaf432 (patch)
treea7129ac3ce0ff3c653fdf6bddbc079979874a7e6
parent732295891a70c731883dca07f68991709151a8d1 (diff)
downloadbt-fc6625b245433d4b59d2468864efb9b77ccaf432.tar.gz
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
-rw-r--r--bta/le_audio/client.cc30
-rw-r--r--bta/le_audio/le_audio_client_test.cc86
2 files changed, 105 insertions, 11 deletions
diff --git a/bta/le_audio/client.cc b/bta/le_audio/client.cc
index 6522d76a6..763f8ebf5 100644
--- a/bta/le_audio/client.cc
+++ b/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/bta/le_audio/le_audio_client_test.cc b/bta/le_audio/le_audio_client_test.cc
index 8158ef299..ce15362bb 100644
--- a/bta/le_audio/le_audio_client_test.cc
+++ b/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;