Loading system/bta/le_audio/devices.cc +3 −2 Original line number Diff line number Diff line Loading @@ -660,8 +660,9 @@ bool LeAudioDevice::HaveAllActiveAsesCisEst(void) const { bool LeAudioDevice::HaveAnyCisConnected(void) { /* Pending and Disconnecting is considered as connected in this function */ for (auto const ase : ases_) { if (ase.cis_state != CisState::ASSIGNED && ase.cis_state != CisState::IDLE) { if (ase.cis_state == CisState::CONNECTED || ase.cis_state == CisState::CONNECTING || ase.cis_state == CisState::DISCONNECTING) { return true; } } Loading system/bta/le_audio/devices.h +2 −0 Original line number Diff line number Diff line Loading @@ -100,6 +100,7 @@ class LeAudioDevice { bool encrypted_; int group_id_; bool csis_member_; int cis_failed_to_be_established_retry_cnt_; std::bitset<16> tmap_role_; uint8_t audio_directions_; Loading Loading @@ -135,6 +136,7 @@ class LeAudioDevice { encrypted_(false), group_id_(group_id), csis_member_(false), cis_failed_to_be_established_retry_cnt_(0), audio_directions_(0), model_name_(""), allowlist_flag_(false), Loading system/bta/le_audio/state_machine.cc +33 −10 Original line number Diff line number Diff line Loading @@ -114,6 +114,7 @@ namespace { constexpr int linkQualityCheckInterval = 4000; constexpr int kAutonomousTransitionTimeoutMs = 5000; constexpr int kNumberOfCisRetries = 2; static void link_quality_cb(void* data) { // very ugly, but we need to pass just two bytes Loading Loading @@ -819,10 +820,29 @@ class LeAudioGroupStateMachineImpl : public LeAudioGroupStateMachine { kLogCisEstablishedOp + "cis_h:" + loghex(event->cis_conn_hdl) + " STATUS=" + loghex(event->status)); if (event->status) { if (event->status != HCI_SUCCESS) { if (ases_pair.sink) ases_pair.sink->cis_state = CisState::ASSIGNED; if (ases_pair.source) ases_pair.source->cis_state = CisState::ASSIGNED; LOG_WARN("%s: failed to create CIS 0x%04x, status: %s (0x%02x)", ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_), event->cis_conn_hdl, ErrorCodeText((ErrorCode)event->status).c_str(), event->status); if (event->status == HCI_ERR_CONN_FAILED_ESTABLISHMENT && ((leAudioDevice->cis_failed_to_be_established_retry_cnt_++) < kNumberOfCisRetries) && (CisCreateForDevice(group, leAudioDevice))) { LOG_INFO("Retrying (%d) to create CIS for %s ", leAudioDevice->cis_failed_to_be_established_retry_cnt_, ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_)); return; } LOG_ERROR("CIS creation failed %d times, stopping the stream", leAudioDevice->cis_failed_to_be_established_retry_cnt_); leAudioDevice->cis_failed_to_be_established_retry_cnt_ = 0; /* CIS establishment failed. Remove CIG if no other CIS is already created * or pending. If CIS is established, this will be handled in disconnected * complete event Loading @@ -831,18 +851,18 @@ class LeAudioGroupStateMachineImpl : public LeAudioGroupStateMachine { RemoveCigForGroup(group); } LOG(ERROR) << __func__ << ", failed to create CIS, status: " << ErrorCodeText((ErrorCode)event->status) << "(" << loghex(event->status) << ")"; StopStream(group); return; } if (leAudioDevice->cis_failed_to_be_established_retry_cnt_ > 0) { /* Reset retry counter */ leAudioDevice->cis_failed_to_be_established_retry_cnt_ = 0; } if (group->GetTargetState() != AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) { LOG(ERROR) << __func__ << ", Unintended CIS establishement event came for group id:" << group->group_id_; LOG_ERROR("Unintended CIS establishement event came for group id: %d", group->group_id_); StopStream(group); return; } Loading Loading @@ -1396,8 +1416,11 @@ class LeAudioGroupStateMachineImpl : public LeAudioGroupStateMachine { /* First in ase pair is Sink, second Source */ auto ases_pair = leAudioDevice->GetAsesByCisConnHdl(ase->cis_conn_hdl); /* Already in pending state - bi-directional CIS */ if (ase->cis_state == CisState::CONNECTING) continue; /* Already in pending state - bi-directional CIS or seconde CIS to same * device */ if (ase->cis_state == CisState::CONNECTING || ase->cis_state == CisState::CONNECTED) continue; if (ases_pair.sink) ases_pair.sink->cis_state = CisState::CONNECTING; if (ases_pair.source) ases_pair.source->cis_state = CisState::CONNECTING; Loading system/bta/le_audio/state_machine_test.cc +131 −3 Original line number Diff line number Diff line Loading @@ -205,6 +205,8 @@ class StateMachineTestBase : public Test { /* Use to simulated error status on Cis creation */ bool overwrite_cis_status_; bool use_cis_retry_cnt_; int retry_cis_established_cnt_; bool do_not_send_cis_establish_event_; uint8_t overwrite_cis_status_idx_; std::vector<uint8_t> cis_status_; Loading @@ -224,6 +226,8 @@ class StateMachineTestBase : public Test { gatt::SetMockBtaGattQueue(&gatt_queue); overwrite_cis_status_idx_ = 0; use_cis_retry_cnt_ = false; retry_cis_established_cnt_ = 0; overwrite_cis_status_ = false; do_not_send_cis_establish_event_ = false; stay_in_releasing_state_ = false; Loading Loading @@ -455,8 +459,19 @@ class StateMachineTestBase : public Test { bluetooth::hci::iso_manager::cis_establish_cmpl_evt evt; // Fill proper values if needed if (overwrite_cis_status_) { if (use_cis_retry_cnt_) { if (retry_cis_established_cnt_ > 0) { evt.status = HCI_ERR_CONN_FAILED_ESTABLISHMENT; retry_cis_established_cnt_--; } else { evt.status = 0; } } else if (overwrite_cis_status_) { evt.status = cis_status_[overwrite_cis_status_idx_++]; /* Reset the index */ if (cis_status_.size() == overwrite_cis_status_idx_) { overwrite_cis_status_idx_ = 0; } } else { evt.status = 0; } Loading Loading @@ -1796,6 +1811,119 @@ TEST_F(StateMachineTest, testStreamSingle) { ASSERT_EQ(1, get_func_call_count("alarm_cancel")); } TEST_F(StateMachineTest, testStreamSingleRetryCisFailure) { /* Device is banded headphones with 1x snk + 0x src ase * (1xunidirectional CIS) with channel count 2 (for stereo */ const auto context_type = kContextTypeRingtone; const int leaudio_group_id = 4; channel_count_ = kLeAudioCodecChannelCountSingleChannel | kLeAudioCodecChannelCountTwoChannel; // Prepare fake connected device group auto* group = PrepareSingleTestDeviceGroup(leaudio_group_id, context_type); /* Ringtone with channel count 1 for single device and 1 ASE sink will * end up with 1 Sink ASE being configured. */ PrepareConfigureCodecHandler(group, 1); PrepareConfigureQosHandler(group, 1); PrepareEnableHandler(group, 1); PrepareReleaseHandler(group); use_cis_retry_cnt_ = true; retry_cis_established_cnt_ = 4; auto* leAudioDevice = group->GetFirstDevice(); EXPECT_CALL(gatt_queue, WriteCharacteristic(1, leAudioDevice->ctp_hdls_.val_hdl, _, GATT_WRITE_NO_RSP, _, _)) .Times(4); EXPECT_CALL(*mock_iso_manager_, CreateCig(_, _)).Times(1); EXPECT_CALL(*mock_iso_manager_, EstablishCis(_)).Times(3); EXPECT_CALL(*mock_iso_manager_, SetupIsoDataPath(_, _)).Times(0); EXPECT_CALL(*mock_iso_manager_, RemoveIsoDataPath(_, _)).Times(0); EXPECT_CALL(*mock_iso_manager_, DisconnectCis(_, _)).Times(0); EXPECT_CALL(*mock_iso_manager_, RemoveCig(_, _)).Times(1); InjectInitialIdleNotification(group); // Validate GroupStreamStatus EXPECT_CALL( mock_callbacks_, StatusReportCb(leaudio_group_id, bluetooth::le_audio::GroupStreamStatus::RELEASING)); EXPECT_CALL(mock_callbacks_, StatusReportCb(leaudio_group_id, bluetooth::le_audio::GroupStreamStatus::IDLE)); // Start the configuration and stream Media content ASSERT_TRUE(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_IDLE); ASSERT_EQ(2, get_func_call_count("alarm_cancel")); } TEST_F(StateMachineTest, testStreamSingleRetryCisSuccess) { /* Device is banded headphones with 1x snk + 0x src ase * (1xunidirectional CIS) with channel count 2 (for stereo */ const auto context_type = kContextTypeRingtone; const int leaudio_group_id = 4; channel_count_ = kLeAudioCodecChannelCountSingleChannel | kLeAudioCodecChannelCountTwoChannel; // Prepare fake connected device group auto* group = PrepareSingleTestDeviceGroup(leaudio_group_id, context_type); /* Ringtone with channel count 1 for single device and 1 ASE sink will * end up with 1 Sink ASE being configured. */ PrepareConfigureCodecHandler(group, 1); PrepareConfigureQosHandler(group, 1); PrepareEnableHandler(group, 1); use_cis_retry_cnt_ = true; retry_cis_established_cnt_ = 2; auto* leAudioDevice = group->GetFirstDevice(); EXPECT_CALL(gatt_queue, WriteCharacteristic(1, leAudioDevice->ctp_hdls_.val_hdl, _, GATT_WRITE_NO_RSP, _, _)) .Times(3); EXPECT_CALL(*mock_iso_manager_, CreateCig(_, _)).Times(1); EXPECT_CALL(*mock_iso_manager_, EstablishCis(_)).Times(3); EXPECT_CALL(*mock_iso_manager_, SetupIsoDataPath(_, _)).Times(1); EXPECT_CALL(*mock_iso_manager_, RemoveIsoDataPath(_, _)).Times(0); EXPECT_CALL(*mock_iso_manager_, DisconnectCis(_, _)).Times(0); EXPECT_CALL(*mock_iso_manager_, RemoveCig(_, _)).Times(0); InjectInitialIdleNotification(group); // Validate GroupStreamStatus EXPECT_CALL( mock_callbacks_, StatusReportCb(leaudio_group_id, bluetooth::le_audio::GroupStreamStatus::STREAMING)); // Start the configuration and stream Media content ASSERT_TRUE(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); ASSERT_EQ(1, get_func_call_count("alarm_cancel")); } TEST_F(StateMachineTest, testStreamSkipEnablingSink) { /* Device is banded headphones with 2x snk + none src ase * (2x unidirectional CIS) Loading Loading @@ -2283,7 +2411,7 @@ TEST_F(StateMachineTest, testAttachToStreamWhileFirstDeviceIsStartingStream) { } TEST_F(StateMachineTest, testFailedStreamCreation) { /* Testing here CIS Failed to be established */ /* Testing here different error than CIS Failed to be established */ const auto context_type = kContextTypeConversational; const auto leaudio_group_id = 4; const auto num_devices = 2; Loading Loading @@ -2343,7 +2471,7 @@ TEST_F(StateMachineTest, testFailedStreamCreation) { .source = types::AudioContexts(context_type)})); bluetooth::hci::iso_manager::cis_establish_cmpl_evt evt; evt.status = 0x3e; evt.status = HCI_ERR_LMP_RESPONSE_TIMEOUT; LeAudioGroupStateMachine::Get()->ProcessHciNotifCisEstablished( group, leAudioDevice, &evt); Loading Loading
system/bta/le_audio/devices.cc +3 −2 Original line number Diff line number Diff line Loading @@ -660,8 +660,9 @@ bool LeAudioDevice::HaveAllActiveAsesCisEst(void) const { bool LeAudioDevice::HaveAnyCisConnected(void) { /* Pending and Disconnecting is considered as connected in this function */ for (auto const ase : ases_) { if (ase.cis_state != CisState::ASSIGNED && ase.cis_state != CisState::IDLE) { if (ase.cis_state == CisState::CONNECTED || ase.cis_state == CisState::CONNECTING || ase.cis_state == CisState::DISCONNECTING) { return true; } } Loading
system/bta/le_audio/devices.h +2 −0 Original line number Diff line number Diff line Loading @@ -100,6 +100,7 @@ class LeAudioDevice { bool encrypted_; int group_id_; bool csis_member_; int cis_failed_to_be_established_retry_cnt_; std::bitset<16> tmap_role_; uint8_t audio_directions_; Loading Loading @@ -135,6 +136,7 @@ class LeAudioDevice { encrypted_(false), group_id_(group_id), csis_member_(false), cis_failed_to_be_established_retry_cnt_(0), audio_directions_(0), model_name_(""), allowlist_flag_(false), Loading
system/bta/le_audio/state_machine.cc +33 −10 Original line number Diff line number Diff line Loading @@ -114,6 +114,7 @@ namespace { constexpr int linkQualityCheckInterval = 4000; constexpr int kAutonomousTransitionTimeoutMs = 5000; constexpr int kNumberOfCisRetries = 2; static void link_quality_cb(void* data) { // very ugly, but we need to pass just two bytes Loading Loading @@ -819,10 +820,29 @@ class LeAudioGroupStateMachineImpl : public LeAudioGroupStateMachine { kLogCisEstablishedOp + "cis_h:" + loghex(event->cis_conn_hdl) + " STATUS=" + loghex(event->status)); if (event->status) { if (event->status != HCI_SUCCESS) { if (ases_pair.sink) ases_pair.sink->cis_state = CisState::ASSIGNED; if (ases_pair.source) ases_pair.source->cis_state = CisState::ASSIGNED; LOG_WARN("%s: failed to create CIS 0x%04x, status: %s (0x%02x)", ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_), event->cis_conn_hdl, ErrorCodeText((ErrorCode)event->status).c_str(), event->status); if (event->status == HCI_ERR_CONN_FAILED_ESTABLISHMENT && ((leAudioDevice->cis_failed_to_be_established_retry_cnt_++) < kNumberOfCisRetries) && (CisCreateForDevice(group, leAudioDevice))) { LOG_INFO("Retrying (%d) to create CIS for %s ", leAudioDevice->cis_failed_to_be_established_retry_cnt_, ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_)); return; } LOG_ERROR("CIS creation failed %d times, stopping the stream", leAudioDevice->cis_failed_to_be_established_retry_cnt_); leAudioDevice->cis_failed_to_be_established_retry_cnt_ = 0; /* CIS establishment failed. Remove CIG if no other CIS is already created * or pending. If CIS is established, this will be handled in disconnected * complete event Loading @@ -831,18 +851,18 @@ class LeAudioGroupStateMachineImpl : public LeAudioGroupStateMachine { RemoveCigForGroup(group); } LOG(ERROR) << __func__ << ", failed to create CIS, status: " << ErrorCodeText((ErrorCode)event->status) << "(" << loghex(event->status) << ")"; StopStream(group); return; } if (leAudioDevice->cis_failed_to_be_established_retry_cnt_ > 0) { /* Reset retry counter */ leAudioDevice->cis_failed_to_be_established_retry_cnt_ = 0; } if (group->GetTargetState() != AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) { LOG(ERROR) << __func__ << ", Unintended CIS establishement event came for group id:" << group->group_id_; LOG_ERROR("Unintended CIS establishement event came for group id: %d", group->group_id_); StopStream(group); return; } Loading Loading @@ -1396,8 +1416,11 @@ class LeAudioGroupStateMachineImpl : public LeAudioGroupStateMachine { /* First in ase pair is Sink, second Source */ auto ases_pair = leAudioDevice->GetAsesByCisConnHdl(ase->cis_conn_hdl); /* Already in pending state - bi-directional CIS */ if (ase->cis_state == CisState::CONNECTING) continue; /* Already in pending state - bi-directional CIS or seconde CIS to same * device */ if (ase->cis_state == CisState::CONNECTING || ase->cis_state == CisState::CONNECTED) continue; if (ases_pair.sink) ases_pair.sink->cis_state = CisState::CONNECTING; if (ases_pair.source) ases_pair.source->cis_state = CisState::CONNECTING; Loading
system/bta/le_audio/state_machine_test.cc +131 −3 Original line number Diff line number Diff line Loading @@ -205,6 +205,8 @@ class StateMachineTestBase : public Test { /* Use to simulated error status on Cis creation */ bool overwrite_cis_status_; bool use_cis_retry_cnt_; int retry_cis_established_cnt_; bool do_not_send_cis_establish_event_; uint8_t overwrite_cis_status_idx_; std::vector<uint8_t> cis_status_; Loading @@ -224,6 +226,8 @@ class StateMachineTestBase : public Test { gatt::SetMockBtaGattQueue(&gatt_queue); overwrite_cis_status_idx_ = 0; use_cis_retry_cnt_ = false; retry_cis_established_cnt_ = 0; overwrite_cis_status_ = false; do_not_send_cis_establish_event_ = false; stay_in_releasing_state_ = false; Loading Loading @@ -455,8 +459,19 @@ class StateMachineTestBase : public Test { bluetooth::hci::iso_manager::cis_establish_cmpl_evt evt; // Fill proper values if needed if (overwrite_cis_status_) { if (use_cis_retry_cnt_) { if (retry_cis_established_cnt_ > 0) { evt.status = HCI_ERR_CONN_FAILED_ESTABLISHMENT; retry_cis_established_cnt_--; } else { evt.status = 0; } } else if (overwrite_cis_status_) { evt.status = cis_status_[overwrite_cis_status_idx_++]; /* Reset the index */ if (cis_status_.size() == overwrite_cis_status_idx_) { overwrite_cis_status_idx_ = 0; } } else { evt.status = 0; } Loading Loading @@ -1796,6 +1811,119 @@ TEST_F(StateMachineTest, testStreamSingle) { ASSERT_EQ(1, get_func_call_count("alarm_cancel")); } TEST_F(StateMachineTest, testStreamSingleRetryCisFailure) { /* Device is banded headphones with 1x snk + 0x src ase * (1xunidirectional CIS) with channel count 2 (for stereo */ const auto context_type = kContextTypeRingtone; const int leaudio_group_id = 4; channel_count_ = kLeAudioCodecChannelCountSingleChannel | kLeAudioCodecChannelCountTwoChannel; // Prepare fake connected device group auto* group = PrepareSingleTestDeviceGroup(leaudio_group_id, context_type); /* Ringtone with channel count 1 for single device and 1 ASE sink will * end up with 1 Sink ASE being configured. */ PrepareConfigureCodecHandler(group, 1); PrepareConfigureQosHandler(group, 1); PrepareEnableHandler(group, 1); PrepareReleaseHandler(group); use_cis_retry_cnt_ = true; retry_cis_established_cnt_ = 4; auto* leAudioDevice = group->GetFirstDevice(); EXPECT_CALL(gatt_queue, WriteCharacteristic(1, leAudioDevice->ctp_hdls_.val_hdl, _, GATT_WRITE_NO_RSP, _, _)) .Times(4); EXPECT_CALL(*mock_iso_manager_, CreateCig(_, _)).Times(1); EXPECT_CALL(*mock_iso_manager_, EstablishCis(_)).Times(3); EXPECT_CALL(*mock_iso_manager_, SetupIsoDataPath(_, _)).Times(0); EXPECT_CALL(*mock_iso_manager_, RemoveIsoDataPath(_, _)).Times(0); EXPECT_CALL(*mock_iso_manager_, DisconnectCis(_, _)).Times(0); EXPECT_CALL(*mock_iso_manager_, RemoveCig(_, _)).Times(1); InjectInitialIdleNotification(group); // Validate GroupStreamStatus EXPECT_CALL( mock_callbacks_, StatusReportCb(leaudio_group_id, bluetooth::le_audio::GroupStreamStatus::RELEASING)); EXPECT_CALL(mock_callbacks_, StatusReportCb(leaudio_group_id, bluetooth::le_audio::GroupStreamStatus::IDLE)); // Start the configuration and stream Media content ASSERT_TRUE(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_IDLE); ASSERT_EQ(2, get_func_call_count("alarm_cancel")); } TEST_F(StateMachineTest, testStreamSingleRetryCisSuccess) { /* Device is banded headphones with 1x snk + 0x src ase * (1xunidirectional CIS) with channel count 2 (for stereo */ const auto context_type = kContextTypeRingtone; const int leaudio_group_id = 4; channel_count_ = kLeAudioCodecChannelCountSingleChannel | kLeAudioCodecChannelCountTwoChannel; // Prepare fake connected device group auto* group = PrepareSingleTestDeviceGroup(leaudio_group_id, context_type); /* Ringtone with channel count 1 for single device and 1 ASE sink will * end up with 1 Sink ASE being configured. */ PrepareConfigureCodecHandler(group, 1); PrepareConfigureQosHandler(group, 1); PrepareEnableHandler(group, 1); use_cis_retry_cnt_ = true; retry_cis_established_cnt_ = 2; auto* leAudioDevice = group->GetFirstDevice(); EXPECT_CALL(gatt_queue, WriteCharacteristic(1, leAudioDevice->ctp_hdls_.val_hdl, _, GATT_WRITE_NO_RSP, _, _)) .Times(3); EXPECT_CALL(*mock_iso_manager_, CreateCig(_, _)).Times(1); EXPECT_CALL(*mock_iso_manager_, EstablishCis(_)).Times(3); EXPECT_CALL(*mock_iso_manager_, SetupIsoDataPath(_, _)).Times(1); EXPECT_CALL(*mock_iso_manager_, RemoveIsoDataPath(_, _)).Times(0); EXPECT_CALL(*mock_iso_manager_, DisconnectCis(_, _)).Times(0); EXPECT_CALL(*mock_iso_manager_, RemoveCig(_, _)).Times(0); InjectInitialIdleNotification(group); // Validate GroupStreamStatus EXPECT_CALL( mock_callbacks_, StatusReportCb(leaudio_group_id, bluetooth::le_audio::GroupStreamStatus::STREAMING)); // Start the configuration and stream Media content ASSERT_TRUE(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); ASSERT_EQ(1, get_func_call_count("alarm_cancel")); } TEST_F(StateMachineTest, testStreamSkipEnablingSink) { /* Device is banded headphones with 2x snk + none src ase * (2x unidirectional CIS) Loading Loading @@ -2283,7 +2411,7 @@ TEST_F(StateMachineTest, testAttachToStreamWhileFirstDeviceIsStartingStream) { } TEST_F(StateMachineTest, testFailedStreamCreation) { /* Testing here CIS Failed to be established */ /* Testing here different error than CIS Failed to be established */ const auto context_type = kContextTypeConversational; const auto leaudio_group_id = 4; const auto num_devices = 2; Loading Loading @@ -2343,7 +2471,7 @@ TEST_F(StateMachineTest, testFailedStreamCreation) { .source = types::AudioContexts(context_type)})); bluetooth::hci::iso_manager::cis_establish_cmpl_evt evt; evt.status = 0x3e; evt.status = HCI_ERR_LMP_RESPONSE_TIMEOUT; LeAudioGroupStateMachine::Get()->ProcessHciNotifCisEstablished( group, leAudioDevice, &evt); Loading