Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 76d6783c authored by Łukasz Rymanowski's avatar Łukasz Rymanowski Committed by Gerrit Code Review
Browse files

Merge "leaudio: Fix double CIS disconnect for bidirectional case"

parents d633bb03 b880dd00
Loading
Loading
Loading
Loading
+29 −4
Original line number Diff line number Diff line
@@ -2427,6 +2427,34 @@ class LeAudioGroupStateMachineImpl : public LeAudioGroupStateMachine {
    }
  }

  void DisconnectCisIfNeeded(LeAudioDeviceGroup* group,
                             LeAudioDevice* leAudioDevice, struct ase* ase) {
    LOG_DEBUG(
        "Group id: %d, %s, ase id: %d, cis_handle: 0x%04x, direction: %s, "
        "data_path_state: %s",
        group->group_id_, ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_),
        ase->id, ase->cis_conn_hdl,
        ase->direction == le_audio::types::kLeAudioDirectionSink ? "sink"
                                                                 : "source",
        bluetooth::common::ToString(ase->data_path_state).c_str());

    auto bidirection_ase = leAudioDevice->GetAseToMatchBidirectionCis(ase);
    if (bidirection_ase != nullptr &&
        bidirection_ase->data_path_state ==
            AudioStreamDataPathState::CIS_ESTABLISHED &&
        (bidirection_ase->state == AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING ||
         bidirection_ase->state == AseState::BTA_LE_AUDIO_ASE_STATE_ENABLING)) {
      LOG_INFO("Still waiting for the bidirectional ase %d to be released (%s)",
               bidirection_ase->id,
               bluetooth::common::ToString(bidirection_ase->state).c_str());
      return;
    }

    RemoveCisFromStreamConfiguration(group, leAudioDevice, ase->cis_conn_hdl);
    IsoManager::GetInstance()->DisconnectCis(ase->cis_conn_hdl,
                                             HCI_ERR_PEER_USER);
  }

  void AseStateMachineProcessReleasing(
      struct le_audio::client_parser::ascs::ase_rsp_hdr& arh, struct ase* ase,
      LeAudioDeviceGroup* group, LeAudioDevice* leAudioDevice) {
@@ -2468,10 +2496,7 @@ class LeAudioGroupStateMachineImpl : public LeAudioGroupStateMachine {
                       AudioStreamDataPathState::CIS_ESTABLISHED ||
                   ase->data_path_state ==
                       AudioStreamDataPathState::CIS_PENDING) {
          RemoveCisFromStreamConfiguration(group, leAudioDevice,
                                           ase->cis_conn_hdl);
          IsoManager::GetInstance()->DisconnectCis(ase->cis_conn_hdl,
                                                   HCI_ERR_PEER_USER);
          DisconnectCisIfNeeded(group, leAudioDevice, ase);
        } else {
          DLOG(INFO) << __func__ << ", Nothing to do ase data path state: "
                     << static_cast<int>(ase->data_path_state);
+92 −1
Original line number Diff line number Diff line
@@ -173,6 +173,11 @@ class StateMachineTest : public Test {
  uint8_t channel_count_ = kLeAudioCodecLC3ChannelCountSingleChannel;
  uint16_t sample_freq_ = codec_specific::kCapSamplingFrequency16000Hz;

  /* Use to simulated error status on Cis creation */
  bool overwrite_cis_status_;
  uint8_t overwrite_cis_status_idx_;
  std::vector<uint8_t> cis_status_;

  void SetUp() override {
    bluetooth::common::InitFlags::Load(test_flags);
    reset_mock_function_count_map();
@@ -181,6 +186,10 @@ class StateMachineTest : public Test {
    gatt::SetMockBtaGattInterface(&gatt_interface);
    gatt::SetMockBtaGattQueue(&gatt_queue);

    overwrite_cis_status_idx_ = 0;
    overwrite_cis_status_ = false;
    cis_status_.clear();

    ::le_audio::AudioSetConfigurationProvider::Initialize();
    LeAudioGroupStateMachine::Initialize(&mock_callbacks_);

@@ -369,7 +378,12 @@ class StateMachineTest : public Test {
                bluetooth::hci::iso_manager::cis_establish_cmpl_evt evt;

                // Fill proper values if needed
                evt.status = 0x00;
                if (overwrite_cis_status_) {
                  evt.status = cis_status_[overwrite_cis_status_idx_++];
                } else {
                  evt.status = 0;
                }

                evt.cig_id = group->group_id_;
                evt.cis_conn_hdl = pair.cis_conn_handle;
                evt.cig_sync_delay = 0;
@@ -1644,6 +1658,83 @@ TEST_F(StateMachineTest, testStreamMultipleConversational) {
  ASSERT_EQ(1, mock_function_count_map["alarm_cancel"]);
}

TEST_F(StateMachineTest, testFailedStreamMultipleConversational) {
  /* Testing here CIS Failed to be established */
  const auto context_type = kContextTypeConversational;
  const auto leaudio_group_id = 4;
  const auto num_devices = 2;
  overwrite_cis_status_ = true;

  cis_status_.resize(2);
  cis_status_[0] = 0x00;
  cis_status_[1] = 0x0e;  // Failed to be established

  // Prepare multiple fake connected devices in a group
  auto* group =
      PrepareSingleTestDeviceGroup(leaudio_group_id, context_type, num_devices);
  ASSERT_EQ(group->Size(), num_devices);

  PrepareConfigureCodecHandler(group);
  PrepareConfigureQosHandler(group);
  PrepareEnableHandler(group);
  PrepareReceiverStartReady(group);
  PrepareReleaseHandler(group);

  EXPECT_CALL(*mock_iso_manager_, CreateCig(_, _)).Times(1);
  EXPECT_CALL(*mock_iso_manager_, EstablishCis(_)).Times(AtLeast(1));
  EXPECT_CALL(*mock_iso_manager_, SetupIsoDataPath(_, _)).Times(0);
  EXPECT_CALL(*mock_iso_manager_, RemoveIsoDataPath(_, _)).Times(0);

  /* This check is the major one in this test, as we want to make sure,
   * it will not be called twice but only once (when both bidirectional ASEs are
   * not in the STREAMING or ENABLING state)
   */
  EXPECT_CALL(*mock_iso_manager_, DisconnectCis(_, _)).Times(1);

  EXPECT_CALL(*mock_iso_manager_, RemoveCig(_, _)).Times(0);

  InjectInitialIdleNotification(group);

  auto* leAudioDevice = group->GetFirstDevice();

  /* First device Control Point actions
   * Codec Config
   * QoS Config
   * Enable
   * Receiver ready
   * Release
   */
  EXPECT_CALL(gatt_queue, WriteCharacteristic(leAudioDevice->conn_id_,
                                              leAudioDevice->ctp_hdls_.val_hdl,
                                              _, GATT_WRITE_NO_RSP, _, _))
      .Times(5);
  leAudioDevice = group->GetNextDevice(leAudioDevice);

  /* Second device Control Point actions
   * Codec Config
   * QoS Config
   * Enable (failed on CIS established - therefore no Receiver Ready)
   * Release
   */
  EXPECT_CALL(gatt_queue, WriteCharacteristic(leAudioDevice->conn_id_,
                                              leAudioDevice->ctp_hdls_.val_hdl,
                                              _, GATT_WRITE_NO_RSP, _, _))
      .Times(4);

  /* Prepare DisconnectCis mock to not symulate CisDisconnection */
  ON_CALL(*mock_iso_manager_, DisconnectCis).WillByDefault(Return());

  // 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(1, mock_function_count_map["alarm_cancel"]);
}

TEST_F(StateMachineTest, testStreamMultiple) {
  const auto context_type = kContextTypeMedia;
  const auto leaudio_group_id = 4;